From 4641a8001b5ebd98087870c0fb25c094e3189d76 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Tue, 7 Jan 2014 23:50:27 +0100 Subject: [PATCH] JournallingObject, ProjectJournal: global checkpoint management There's no need for having each JournallingObject maintain it's own checkpoints and build a complex (and buggy) logic in ProjectJournal in order to manage all the JournallingObject with their checkpoints. Instead do it the simple way: in ProjectJournal maintain a stack for undo checkpoints and a stack for redo checkpoints. On each undo or redo operation simply push and pop to/from the according stacks and save and load states of the concerned JournallingObject. This basically strips most functionality from JournallingObject. All what's left is the management of its ID which unluckily is still required in order to properly implement undo/redo of additions and removals of JournallingObject. --- include/JournallingObject.h | 69 ++--------------- include/ProjectJournal.h | 32 +++++--- src/core/JournallingObject.cpp | 133 ++++----------------------------- src/core/ProjectJournal.cpp | 99 +++++++++++------------- 4 files changed, 85 insertions(+), 248 deletions(-) diff --git a/include/JournallingObject.h b/include/JournallingObject.h index 84731c07c..1be811d20 100644 --- a/include/JournallingObject.h +++ b/include/JournallingObject.h @@ -27,7 +27,6 @@ #include "lmms_basics.h" #include "export.h" -#include "mmp.h" #include "SerializingObject.h" #include @@ -35,39 +34,6 @@ #include -class JournalCheckPoint -{ -public: - JournalCheckPoint( const multimediaProject &data = - multimediaProject( multimediaProject::JournalData ) ) : - m_data( data ) - { - } - - ~JournalCheckPoint() - { - } - - const multimediaProject &data() const - { - return m_data; - } - - multimediaProject &data() - { - return m_data; - } - - -private: - multimediaProject m_data; - -} ; - - -typedef QVector JournalCheckPointVector; - - class EXPORT JournallingObject : public SerializingObject { public: @@ -79,27 +45,10 @@ public: return m_id; } - void undo(); - void redo(); - - void clear() - { - m_journalCheckPoints.clear(); - m_currentJournalCheckPoint = m_journalCheckPoints.end(); - } - - void clearRedoSteps() - { - m_journalCheckPoints.erase( m_currentJournalCheckPoint, - m_journalCheckPoints.end() ); - m_currentJournalCheckPoint = m_journalCheckPoints.end(); - - } - - void saveJournallingState( const bool _new_state ) + void saveJournallingState( const bool newState ) { m_journallingStateStack.push( m_journalling ); - m_journalling = _new_state; + m_journalling = newState; } void restoreJournallingState() @@ -110,11 +59,10 @@ public: void addJournalCheckPoint(); virtual QDomElement saveState( QDomDocument & _doc, - QDomElement & _parent ); + QDomElement & _parent ); virtual void restoreState( const QDomElement & _this ); - inline bool isJournalling() const { return m_journalling; @@ -125,10 +73,10 @@ public: m_journalling = _sr; } - inline bool testAndSetJournalling( const bool _sr ) + inline bool testAndSetJournalling( const bool newState ) { const bool oldJournalling = m_journalling; - m_journalling = _sr; + m_journalling = newState; return oldJournalling; } @@ -138,15 +86,8 @@ protected: private: - void saveJournal( QDomDocument & _doc, QDomElement & _parent ); - void loadJournal( const QDomElement & _this ); - - jo_id_t m_id; - JournalCheckPointVector m_journalCheckPoints; - JournalCheckPointVector::Iterator m_currentJournalCheckPoint; - bool m_journalling; QStack m_journallingStateStack; diff --git a/include/ProjectJournal.h b/include/ProjectJournal.h index 041f7507e..7c819daa0 100644 --- a/include/ProjectJournal.h +++ b/include/ProjectJournal.h @@ -1,7 +1,7 @@ /* * ProjectJournal.h - declaration of class ProjectJournal * - * Copyright (c) 2006-2008 Tobias Doerffel + * Copyright (c) 2006-2010 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -26,10 +26,10 @@ #define _PROJECT_JOURNAL_H #include -#include -#include +#include #include "lmms_basics.h" +#include "mmp.h" class JournallingObject; @@ -43,8 +43,7 @@ public: void undo(); void redo(); - // tell history that a new journal entry was added to object with ID _id - void journalEntryAdded( const jo_id_t _id ); + void addJournalCheckPoint( JournallingObject *jo ); bool isJournalling() const { @@ -72,10 +71,6 @@ public: reallocID( _id, NULL ); } - // completely remove everything linked with ID _id - all global - // journalling information about the ID get's lost - void forgetAboutID( const jo_id_t _id ); - void clearJournal(); JournallingObject * journallingObject( const jo_id_t _id ) @@ -90,12 +85,25 @@ public: private: typedef QHash JoIdMap; - typedef QVector JournalEntryVector; + + struct CheckPoint + { + CheckPoint( jo_id_t initID = 0, + const multimediaProject &initData = + multimediaProject( multimediaProject::JournalData ) ) : + joID( initID ), + data( initData ) + { + } + jo_id_t joID; + multimediaProject data; + } ; + typedef QStack CheckPointStack; JoIdMap m_joIDs; - JournalEntryVector m_journalEntries; - JournalEntryVector::Iterator m_currentJournalEntry; + CheckPointStack m_undoCheckPoints; + CheckPointStack m_redoCheckPoints; bool m_journalling; diff --git a/src/core/JournallingObject.cpp b/src/core/JournallingObject.cpp index 7d3229597..ce565551f 100644 --- a/src/core/JournallingObject.cpp +++ b/src/core/JournallingObject.cpp @@ -37,8 +37,6 @@ JournallingObject::JournallingObject() : SerializingObject(), m_id( engine::projectJournal()->allocID( this ) ), - m_journalCheckPoints(), - m_currentJournalCheckPoint( m_journalCheckPoints.end() ), m_journalling( true ), m_journallingStateStack() { @@ -58,34 +56,11 @@ JournallingObject::~JournallingObject() -void JournallingObject::undo() +void JournallingObject::addJournalCheckPoint() { - if( m_journalCheckPoints.empty() == true ) + if( isJournalling() ) { - return; - } - - if( m_currentJournalCheckPoint - 1 >= m_journalCheckPoints.begin() ) - { - --m_currentJournalCheckPoint; - restoreState( m_currentJournalCheckPoint->data().content().firstChildElement() ); - } -} - - - - -void JournallingObject::redo() -{ - if( m_journalCheckPoints.empty() == true ) - { - return; - } - - if( m_currentJournalCheckPoint < m_journalCheckPoints.end() ) - { - restoreState( m_currentJournalCheckPoint->data().content().firstChildElement() ); - ++m_currentJournalCheckPoint; + engine::projectJournal()->addJournalCheckPoint( this ); } } @@ -96,7 +71,12 @@ QDomElement JournallingObject::saveState( QDomDocument & _doc, QDomElement & _parent ) { QDomElement _this = SerializingObject::saveState( _doc, _parent ); - saveJournal( _doc, _this ); + + QDomElement journalNode = _doc.createElement( "journallingObject" ); + journalNode.setAttribute( "id", id() ); + journalNode.setAttribute( "metadata", true ); + _this.appendChild( journalNode ); + return _this; } @@ -115,7 +95,11 @@ void JournallingObject::restoreState( const QDomElement & _this ) { if( node.isElement() && node.nodeName() == "journal" ) { - loadJournal( node.toElement() ); + const jo_id_t new_id = node.toElement().attribute( "id" ).toInt(); + if( new_id ) + { + changeID( new_id ); + } } node = node.nextSibling(); } @@ -126,26 +110,6 @@ void JournallingObject::restoreState( const QDomElement & _this ) -void JournallingObject::addJournalCheckPoint() -{ - if( engine::projectJournal()->isJournalling() && isJournalling() ) - { - m_journalCheckPoints.erase( m_currentJournalCheckPoint, - m_journalCheckPoints.end() ); - - multimediaProject mmp( multimediaProject::JournalData ); - saveState( mmp, mmp.content() ); - - m_journalCheckPoints.push_back( JournalCheckPoint( mmp ) ); - - m_currentJournalCheckPoint = m_journalCheckPoints.end(); - engine::projectJournal()->journalEntryAdded( id() ); - } -} - - - - void JournallingObject::changeID( jo_id_t _id ) { if( id() != _id ) @@ -166,77 +130,10 @@ void JournallingObject::changeID( jo_id_t _id ) (int) _id, used_by.toUtf8().constData() ); return; } - engine::projectJournal()->forgetAboutID( id() ); + engine::projectJournal()->reallocID( _id, this ); m_id = _id; } } - - -void JournallingObject::saveJournal( QDomDocument & _doc, - QDomElement & _parent ) -{ -/* // avoid creating empty journal-nodes - if( m_journalCheckPoints.size() == 0 ) - { - return; - }*/ - QDomElement journal_de = _doc.createElement( "journal" ); - journal_de.setAttribute( "id", id() ); - journal_de.setAttribute( "entries", m_journalCheckPoints.size() ); - journal_de.setAttribute( "curentry", (int)( m_currentJournalCheckPoint - - m_journalCheckPoints.begin() ) ); - journal_de.setAttribute( "metadata", true ); - - for( JournalCheckPointVector::const_iterator it = m_journalCheckPoints.begin(); - it != m_journalCheckPoints.end(); ++it ) - { - QDomElement je_de = _doc.createElement( "entry" ); - je_de.setAttribute( "pos", (int)( it - - m_journalCheckPoints.begin() ) ); - //je_de.setAttribute( "data", base64::encode( it->data().toString() ) ); - je_de.setAttribute( "data", it->data().toString() ); - journal_de.appendChild( je_de ); - } - - _parent.appendChild( journal_de ); -} - - - - -void JournallingObject::loadJournal( const QDomElement & _this ) -{ - clear(); - - const jo_id_t new_id = _this.attribute( "id" ).toInt(); - - if( new_id == 0 ) - { - return; - } - - changeID( new_id ); - - m_journalCheckPoints.resize( _this.attribute( "entries" ).toInt() ); - - QDomNode node = _this.firstChild(); - while( !node.isNull() ) - { - if( node.isElement() ) - { - const QDomElement & je = node.toElement(); - m_journalCheckPoints[je.attribute( "pos" ).toInt()] = - JournalCheckPoint( - multimediaProject( je.attribute( "data" ).toUtf8() ) ); - } - node = node.nextSibling(); - } - - m_currentJournalCheckPoint = m_journalCheckPoints.begin() + - _this.attribute( "curentry" ).toInt(); -} - - diff --git a/src/core/ProjectJournal.cpp b/src/core/ProjectJournal.cpp index 04e452fde..bcd30d807 100644 --- a/src/core/ProjectJournal.cpp +++ b/src/core/ProjectJournal.cpp @@ -32,8 +32,8 @@ ProjectJournal::ProjectJournal() : m_joIDs(), - m_journalEntries(), - m_currentJournalEntry( m_journalEntries.end() ), + m_undoCheckPoints(), + m_redoCheckPoints(), m_journalling( false ) { } @@ -50,57 +50,66 @@ ProjectJournal::~ProjectJournal() void ProjectJournal::undo() { - if( m_journalEntries.empty() == true ) + while( !m_undoCheckPoints.isEmpty() ) { - return; - } + CheckPoint c = m_undoCheckPoints.pop(); + JournallingObject *jo = m_joIDs[c.joID]; - JournallingObject * jo; + if( jo ) + { + multimediaProject curState( multimediaProject::JournalData ); + jo->saveState( curState, curState.content() ); + m_redoCheckPoints.push( CheckPoint( c.joID, curState ) ); - if( m_currentJournalEntry - 1 >= m_journalEntries.begin() && - ( jo = m_joIDs[*--m_currentJournalEntry] ) != NULL ) - { - bool prev = isJournalling(); - setJournalling( false ); - jo->undo(); - setJournalling( prev ); - engine::getSong()->setModified(); + bool prev = isJournalling(); + setJournalling( false ); + jo->restoreState( c.data.content().firstChildElement() ); + setJournalling( prev ); + engine::getSong()->setModified(); + break; + } } } - void ProjectJournal::redo() { - if( m_journalEntries.empty() == true ) + while( !m_redoCheckPoints.isEmpty() ) { - return; - } + CheckPoint c = m_redoCheckPoints.pop(); + JournallingObject *jo = m_joIDs[c.joID]; - JournallingObject * jo; + if( jo ) + { + multimediaProject curState( multimediaProject::JournalData ); + jo->saveState( curState, curState.content() ); + m_undoCheckPoints.push( CheckPoint( c.joID, curState ) ); - //printf("%d\n", m_joIDs[*(m_currentJournalEntry+1)] ); - if( m_currentJournalEntry < m_journalEntries.end() && - ( jo = m_joIDs[*m_currentJournalEntry++] ) != NULL ) - { - bool prev = isJournalling(); - setJournalling( false ); - jo->redo(); - setJournalling( prev ); - engine::getSong()->setModified(); + bool prev = isJournalling(); + setJournalling( false ); + jo->restoreState( c.data.content().firstChildElement() ); + setJournalling( prev ); + engine::getSong()->setModified(); + break; + } } } -void ProjectJournal::journalEntryAdded( const jo_id_t _id ) +void ProjectJournal::addJournalCheckPoint( JournallingObject *jo ) { - m_journalEntries.erase( m_currentJournalEntry, m_journalEntries.end() ); - m_journalEntries.push_back( _id ); - m_currentJournalEntry = m_journalEntries.end(); - engine::getSong()->setModified(); + if( isJournalling() ) + { + m_redoCheckPoints.clear(); + + multimediaProject mmp( multimediaProject::JournalData ); + jo->saveState( mmp, mmp.content() ); + + m_undoCheckPoints.push( CheckPoint( jo->id(), mmp ) ); + } } @@ -136,29 +145,11 @@ void ProjectJournal::reallocID( const jo_id_t _id, JournallingObject * _obj ) -void ProjectJournal::forgetAboutID( const jo_id_t _id ) -{ - //printf("forget about %d\n", _id ); - JournalEntryVector::Iterator it; - while( ( it = qFind( m_journalEntries.begin(), m_journalEntries.end(), - _id ) ) != m_journalEntries.end() ) - { - if( m_currentJournalEntry >= it ) - { - --m_currentJournalEntry; - } - m_journalEntries.erase( it ); - } - m_joIDs.remove( _id ); -} - - - - void ProjectJournal::clearJournal() { - m_journalEntries.clear(); - m_currentJournalEntry = m_journalEntries.end(); + m_undoCheckPoints.clear(); + m_redoCheckPoints.clear(); + for( JoIdMap::Iterator it = m_joIDs.begin(); it != m_joIDs.end(); ) { if( it.value() == NULL )