From a5c3cf6a99fbe1b471e64b7aecb0bcb3cf3454aa Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Tue, 7 Jan 2014 23:42:17 +0100 Subject: [PATCH 1/5] Revised journalling (undo/redo) to record full states instead of changes Recording single changes of objects or their specific properties is completely superfluous as we have full implemented state tracking in all objects already. Therefore use SerializingObject::saveState() and SerializingObject::restoreState() in order to implement the undo/redo functionality. This is just an initial commit and needs some further work (especially regarding stability). However even things like undo/redo of addition/removal of Tracks and TrackContentObjects do work already. --- include/AutomatableModel.h | 22 ++-- include/JournallingObject.h | 67 ++++-------- include/note.h | 15 +-- include/surround_area.h | 14 +-- include/track.h | 18 +-- include/track_container_view.h | 5 +- src/core/AutomatableModel.cpp | 67 +++--------- src/core/JournallingObject.cpp | 66 ++++++----- src/core/ProjectJournal.cpp | 8 +- src/core/note.cpp | 50 +-------- src/core/track.cpp | 181 ++----------------------------- src/core/track_container.cpp | 12 +- src/gui/track_container_view.cpp | 71 +----------- src/gui/widgets/knob.cpp | 11 +- src/gui/widgets/lcd_spinbox.cpp | 15 ++- 15 files changed, 134 insertions(+), 488 deletions(-) diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index 453949b73..9488e8511 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -1,7 +1,7 @@ /* * AutomatableModel.h - declaration of class AutomatableModel * - * Copyright (c) 2007-2009 Tobias Doerffel + * Copyright (c) 2007-2014 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -180,23 +180,19 @@ public: static void unlinkModels( AutomatableModel * _m1, AutomatableModel * _m2 ); - virtual void saveSettings( QDomDocument & _doc, - QDomElement & _this, - const QString & _name = QString( "value" ) ); + virtual void saveSettings( QDomDocument &doc, QDomElement &_this ); + virtual void loadSettings( const QDomElement &_this ); - virtual void loadSettings( const QDomElement & _this, - const QString & _name = QString( "value" ) ); + virtual void saveSettings( QDomDocument &doc, + QDomElement &_this, + const QString &name ); + virtual void loadSettings( const QDomElement &_this, const QString &name ); virtual QString nodeName() const { return "automatablemodel"; } - void prepareJournalEntryFromOldVal(); - - void addJournalEntryFromOldToCurVal(); - - QString displayValue( const float _val ) const { switch( m_dataType ) @@ -217,9 +213,6 @@ public slots: protected: - virtual void redoStep( JournalEntry & _je ); - virtual void undoStep( JournalEntry & _je ); - float fittedValue( float _value ) const; @@ -239,7 +232,6 @@ private: // most objects will need this temporarily (until sampleExact is // standard) float m_oldValue; - bool m_journalEntryReady; int m_setValueDepth; AutoModelVector m_linkedModels; diff --git a/include/JournallingObject.h b/include/JournallingObject.h index df9902546..84731c07c 100644 --- a/include/JournallingObject.h +++ b/include/JournallingObject.h @@ -1,7 +1,7 @@ /* * JournallingObject.h - declaration of class JournallingObject * - * Copyright (c) 2006-2009 Tobias Doerffel + * Copyright (c) 2006-2014 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -27,6 +27,7 @@ #include "lmms_basics.h" #include "export.h" +#include "mmp.h" #include "SerializingObject.h" #include @@ -34,57 +35,37 @@ #include -typedef uint32_t t_action_id; - - -class JournalEntry +class JournalCheckPoint { public: - JournalEntry( const t_action_id _action_id, const QVariant & _data ) : - m_actionID( _action_id ), - m_data( _data ) + JournalCheckPoint( const multimediaProject &data = + multimediaProject( multimediaProject::JournalData ) ) : + m_data( data ) { } - JournalEntry() : - m_actionID( 0 ), - m_data( 0 ) + ~JournalCheckPoint() { } - ~JournalEntry() - { - } - - t_action_id actionID() const - { - return m_actionID; - } - - t_action_id & actionID() - { - return m_actionID; - } - - const QVariant & data() const + const multimediaProject &data() const { return m_data; } - QVariant & data() + multimediaProject &data() { return m_data; } private: - t_action_id m_actionID; - QVariant m_data; + multimediaProject m_data; } ; -typedef QVector JournalEntryVector; +typedef QVector JournalCheckPointVector; class EXPORT JournallingObject : public SerializingObject @@ -103,15 +84,15 @@ public: void clear() { - m_journalEntries.clear(); - m_currentJournalEntry = m_journalEntries.end(); + m_journalCheckPoints.clear(); + m_currentJournalCheckPoint = m_journalCheckPoints.end(); } void clearRedoSteps() { - m_journalEntries.erase( m_currentJournalEntry, - m_journalEntries.end() ); - m_currentJournalEntry = m_journalEntries.end(); + m_journalCheckPoints.erase( m_currentJournalCheckPoint, + m_journalCheckPoints.end() ); + m_currentJournalCheckPoint = m_journalCheckPoints.end(); } @@ -126,6 +107,8 @@ public: m_journalling = m_journallingStateStack.pop(); } + void addJournalCheckPoint(); + virtual QDomElement saveState( QDomDocument & _doc, QDomElement & _parent ); @@ -153,16 +136,6 @@ public: protected: void changeID( jo_id_t _id ); - void addJournalEntry( const JournalEntry & _je ); - - // to be implemented by sub-objects - virtual void undoStep( JournalEntry & ) - { - } - virtual void redoStep( JournalEntry & ) - { - } - private: void saveJournal( QDomDocument & _doc, QDomElement & _parent ); @@ -171,8 +144,8 @@ private: jo_id_t m_id; - JournalEntryVector m_journalEntries; - JournalEntryVector::Iterator m_currentJournalEntry; + JournalCheckPointVector m_journalCheckPoints; + JournalCheckPointVector::Iterator m_currentJournalCheckPoint; bool m_journalling; diff --git a/include/note.h b/include/note.h index 36bf1640d..11d2e4a17 100644 --- a/include/note.h +++ b/include/note.h @@ -2,7 +2,7 @@ * note.h - declaration of class note which contains all informations about a * note + definitions of several constants and enums * - * Copyright (c) 2004-2010 Tobias Doerffel + * Copyright (c) 2004-2014 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -208,21 +208,8 @@ protected: QDomElement & _parent ); virtual void loadSettings( const QDomElement & _this ); -/* virtual void undoStep( JournalEntry & _je ); - virtual void redoStep( JournalEntry & _je );*/ - private: -/* enum Actions - { - ChangeKey, - ChangeVolume, - ChangePanning, - ChangeLength, - ChangePosition - } ;*/ - - // for piano roll editing bool m_selected; int m_oldKey; diff --git a/include/surround_area.h b/include/surround_area.h index 16b5822e1..30969a93a 100644 --- a/include/surround_area.h +++ b/include/surround_area.h @@ -3,7 +3,7 @@ * position of a channel + calculation of volume for each * speaker * - * Copyright (c) 2004-2008 Tobias Doerffel + * Copyright (c) 2004-2014 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -57,18 +57,6 @@ public: void loadSettings( const QDomElement & _this, const QString & _name = "surpos" ); - inline void prepareJournalEntryFromOldVal() - { - m_posX.prepareJournalEntryFromOldVal(); - m_posY.prepareJournalEntryFromOldVal(); - } - - inline void addJournalEntryFromOldToCurVal() - { - m_posX.addJournalEntryFromOldToCurVal(); - m_posY.addJournalEntryFromOldToCurVal(); - } - // AutomationPattern * automationPatternX(); // AutomationPattern * automationPatternY(); diff --git a/include/track.h b/include/track.h index 1011c245d..2d7349be4 100644 --- a/include/track.h +++ b/include/track.h @@ -2,7 +2,7 @@ * track.h - declaration of classes concerning tracks -> necessary for all * track-like objects (beat/bassline, sample-track...) * - * Copyright (c) 2004-2009 Tobias Doerffel + * Copyright (c) 2004-2014 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -128,11 +128,6 @@ public slots: void toggleMute(); -protected: - virtual void undoStep( JournalEntry & _je ); - virtual void redoStep( JournalEntry & _je ); - - signals: void lengthChanged(); void positionChanged(); @@ -275,17 +270,8 @@ protected: return "trackcontentwidget"; } - virtual void undoStep( JournalEntry & _je ); - virtual void redoStep( JournalEntry & _je ); - private: - enum Actions - { - AddTrackContentObject, - RemoveTrackContentObject - } ; - track * getTrack(); midiTime getPosition( int _mouse_x ); @@ -539,8 +525,6 @@ public slots: protected: virtual void modelChanged(); - virtual void undoStep( JournalEntry & _je ); - virtual void redoStep( JournalEntry & _je ); virtual QString nodeName() const { diff --git a/include/track_container_view.h b/include/track_container_view.h index cace6bf8e..6597a39ac 100644 --- a/include/track_container_view.h +++ b/include/track_container_view.h @@ -1,7 +1,7 @@ /* * track_container_view.h - view-component for trackContainer * - * Copyright (c) 2004-2009 Tobias Doerffel + * Copyright (c) 2004-2014 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -139,9 +139,6 @@ protected: virtual void mouseReleaseEvent( QMouseEvent * _me ); virtual void resizeEvent( QResizeEvent * ); - virtual void undoStep( JournalEntry & _je ); - virtual void redoStep( JournalEntry & _je ); - midiTime m_currentPosition; diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index 034be3bb5..29ee3cb12 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -1,7 +1,7 @@ /* * AutomatableModel.cpp - some implementations of AutomatableModel-class * - * Copyright (c) 2008-2012 Tobias Doerffel + * Copyright (c) 2008-2014 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -50,7 +50,6 @@ AutomatableModel::AutomatableModel( DataType _type, m_maxValue( _max ), m_step( _step ), m_range( _max - _min ), - m_journalEntryReady( false ), m_setValueDepth( 0 ), m_hasLinkedModels( false ), m_controllerConnection( NULL ) @@ -88,6 +87,22 @@ bool AutomatableModel::isAutomated() const +void AutomatableModel::saveSettings( QDomDocument &doc, QDomElement &_this ) +{ + saveSettings( doc, _this, "value" ); +} + + + + +void AutomatableModel::loadSettings( const QDomElement &_this ) +{ + loadSettings( _this, "value" ); +} + + + + void AutomatableModel::saveSettings( QDomDocument & _doc, QDomElement & _this, const QString & _name ) { @@ -184,7 +199,7 @@ void AutomatableModel::setValue( const float _value ) if( old_val != m_value ) { // add changes to history so user can undo it - addJournalEntry( JournalEntry( 0, m_value - old_val ) ); + addJournalCheckPoint(); // notify linked models for( AutoModelVector::Iterator it = @@ -319,52 +334,6 @@ float AutomatableModel::fittedValue( float _value ) const -void AutomatableModel::redoStep( JournalEntry & _je ) -{ - bool journalling = testAndSetJournalling( false ); - setValue( value() + (float) _je.data().toDouble() ); - setJournalling( journalling ); -} - - - - -void AutomatableModel::undoStep( JournalEntry & _je ) -{ - JournalEntry je( _je.actionID(), -_je.data().toDouble() ); - redoStep( je ); -} - - - - -void AutomatableModel::prepareJournalEntryFromOldVal() -{ - m_oldValue = value(); - saveJournallingState( false ); - m_journalEntryReady = true; -} - - - - -void AutomatableModel::addJournalEntryFromOldToCurVal() -{ - if( m_journalEntryReady ) - { - restoreJournallingState(); - if( value() != m_oldValue ) - { - addJournalEntry( JournalEntry( 0, value() - - m_oldValue ) ); - } - m_journalEntryReady = false; - } -} - - - - void AutomatableModel::linkModel( AutomatableModel * _model ) { if( !m_linkedModels.contains( _model ) ) diff --git a/src/core/JournallingObject.cpp b/src/core/JournallingObject.cpp index b2a4bc575..7d3229597 100644 --- a/src/core/JournallingObject.cpp +++ b/src/core/JournallingObject.cpp @@ -1,7 +1,7 @@ /* * JournallingObject.cpp - implementation of journalling-object related stuff * - * Copyright (c) 2006-2009 Tobias Doerffel + * Copyright (c) 2006-2014 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -37,8 +37,8 @@ JournallingObject::JournallingObject() : SerializingObject(), m_id( engine::projectJournal()->allocID( this ) ), - m_journalEntries(), - m_currentJournalEntry( m_journalEntries.end() ), + m_journalCheckPoints(), + m_currentJournalCheckPoint( m_journalCheckPoints.end() ), m_journalling( true ), m_journallingStateStack() { @@ -60,14 +60,15 @@ JournallingObject::~JournallingObject() void JournallingObject::undo() { - if( m_journalEntries.empty() == true ) + if( m_journalCheckPoints.empty() == true ) { return; } - if( m_currentJournalEntry - 1 >= m_journalEntries.begin() ) + if( m_currentJournalCheckPoint - 1 >= m_journalCheckPoints.begin() ) { - undoStep( *--m_currentJournalEntry ); + --m_currentJournalCheckPoint; + restoreState( m_currentJournalCheckPoint->data().content().firstChildElement() ); } } @@ -76,14 +77,15 @@ void JournallingObject::undo() void JournallingObject::redo() { - if( m_journalEntries.empty() == true ) + if( m_journalCheckPoints.empty() == true ) { return; } - if( m_currentJournalEntry < m_journalEntries.end() ) + if( m_currentJournalCheckPoint < m_journalCheckPoints.end() ) { - redoStep( *m_currentJournalEntry++ ); + restoreState( m_currentJournalCheckPoint->data().content().firstChildElement() ); + ++m_currentJournalCheckPoint; } } @@ -124,14 +126,19 @@ void JournallingObject::restoreState( const QDomElement & _this ) -void JournallingObject::addJournalEntry( const JournalEntry & _je ) +void JournallingObject::addJournalCheckPoint() { if( engine::projectJournal()->isJournalling() && isJournalling() ) { - m_journalEntries.erase( m_currentJournalEntry, - m_journalEntries.end() ); - m_journalEntries.push_back( _je ); - m_currentJournalEntry = m_journalEntries.end(); + 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() ); } } @@ -172,25 +179,25 @@ void JournallingObject::saveJournal( QDomDocument & _doc, QDomElement & _parent ) { /* // avoid creating empty journal-nodes - if( m_journalEntries.size() == 0 ) + if( m_journalCheckPoints.size() == 0 ) { return; }*/ QDomElement journal_de = _doc.createElement( "journal" ); journal_de.setAttribute( "id", id() ); - journal_de.setAttribute( "entries", m_journalEntries.size() ); - journal_de.setAttribute( "curentry", (int)( m_currentJournalEntry - - m_journalEntries.begin() ) ); + journal_de.setAttribute( "entries", m_journalCheckPoints.size() ); + journal_de.setAttribute( "curentry", (int)( m_currentJournalCheckPoint - + m_journalCheckPoints.begin() ) ); journal_de.setAttribute( "metadata", true ); - for( JournalEntryVector::const_iterator it = m_journalEntries.begin(); - it != m_journalEntries.end(); ++it ) + 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_journalEntries.begin() ) ); - je_de.setAttribute( "actionid", it->actionID() ); - je_de.setAttribute( "data", base64::encode( it->data() ) ); + m_journalCheckPoints.begin() ) ); + //je_de.setAttribute( "data", base64::encode( it->data().toString() ) ); + je_de.setAttribute( "data", it->data().toString() ); journal_de.appendChild( je_de ); } @@ -213,7 +220,7 @@ void JournallingObject::loadJournal( const QDomElement & _this ) changeID( new_id ); - m_journalEntries.resize( _this.attribute( "entries" ).toInt() ); + m_journalCheckPoints.resize( _this.attribute( "entries" ).toInt() ); QDomNode node = _this.firstChild(); while( !node.isNull() ) @@ -221,15 +228,14 @@ void JournallingObject::loadJournal( const QDomElement & _this ) if( node.isElement() ) { const QDomElement & je = node.toElement(); - m_journalEntries[je.attribute( "pos" ).toInt()] = - JournalEntry( - je.attribute( "actionid" ).toInt(), - base64::decode( je.attribute( "data" ) ) ); + m_journalCheckPoints[je.attribute( "pos" ).toInt()] = + JournalCheckPoint( + multimediaProject( je.attribute( "data" ).toUtf8() ) ); } node = node.nextSibling(); - } + } - m_currentJournalEntry = m_journalEntries.begin() + + m_currentJournalCheckPoint = m_journalCheckPoints.begin() + _this.attribute( "curentry" ).toInt(); } diff --git a/src/core/ProjectJournal.cpp b/src/core/ProjectJournal.cpp index dffd60eee..04e452fde 100644 --- a/src/core/ProjectJournal.cpp +++ b/src/core/ProjectJournal.cpp @@ -1,7 +1,7 @@ /* * ProjectJournal.cpp - implementation of ProjectJournal * - * Copyright (c) 2006-2009 Tobias Doerffel + * Copyright (c) 2006-2014 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -60,7 +60,10 @@ void ProjectJournal::undo() 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(); } } @@ -81,7 +84,10 @@ void ProjectJournal::redo() if( m_currentJournalEntry < m_journalEntries.end() && ( jo = m_joIDs[*m_currentJournalEntry++] ) != NULL ) { + bool prev = isJournalling(); + setJournalling( false ); jo->redo(); + setJournalling( prev ); engine::getSong()->setModified(); } } diff --git a/src/core/note.cpp b/src/core/note.cpp index 2088e1188..e40d5f59f 100644 --- a/src/core/note.cpp +++ b/src/core/note.cpp @@ -1,7 +1,7 @@ /* * note.cpp - implementation of class note * - * Copyright (c) 2004-2009 Tobias Doerffel + * Copyright (c) 2004-2014 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -50,8 +50,6 @@ note::note( const midiTime & _length, const midiTime & _pos, m_pos( _pos ), m_detuning( NULL ) { - //saveJournallingState( false ); -// setJournalling( false ); if( _detuning ) { m_detuning = sharedObject::ref( _detuning ); @@ -60,7 +58,6 @@ note::note( const midiTime & _length, const midiTime & _pos, { createDetuning(); } - //restoreJournallingState(); } @@ -106,7 +103,6 @@ note::~note() void note::setLength( const midiTime & _length ) { -// addJournalEntry( journalEntry( ChangeLength, m_length - _length ) ); m_length = _length; } @@ -115,7 +111,6 @@ void note::setLength( const midiTime & _length ) void note::setPos( const midiTime & _pos ) { -// addJournalEntry( journalEntry( ChangePosition, m_pos - _pos ) ); m_pos = _pos; } @@ -125,7 +120,6 @@ void note::setPos( const midiTime & _pos ) void note::setKey( const int _key ) { const int k = tLimit( _key, 0, NumKeys ); -// addJournalEntry( journalEntry( ChangeKey, m_key - k ) ); m_key = k; } @@ -135,7 +129,6 @@ void note::setKey( const int _key ) void note::setVolume( const volume_t _volume ) { const volume_t v = tLimit( _volume, MinVolume, MaxVolume ); -// addJournalEntry( journalEntry( ChangeVolume, (int) m_volume - v ) ); m_volume = v; } @@ -145,7 +138,6 @@ void note::setVolume( const volume_t _volume ) void note::setPanning( const panning_t _panning ) { const panning_t p = tLimit( _panning, PanningLeft, PanningRight ); -// addJournalEntry( journalEntry( ChangePanning, (int) m_panning - p ) ); m_panning = p; } @@ -219,46 +211,6 @@ void note::loadSettings( const QDomElement & _this ) -/*void note::undoStep( journalEntry & _je ) -{ - saveJournallingState( false ); - switch( static_cast( _je.actionID() ) ) - { - case ChangeKey: - setKey( key() - _je.data().toInt() ); - break; - - case ChangeVolume: - setVolume( getVolume() - _je.data().toInt() ); - break; - - case ChangePanning: - setPanning( getPanning() - _je.data().toInt() ); - break; - - case ChangeLength: - setLength( length() - _je.data().toInt() ); - break; - - case ChangePosition: - setPos( pos() - _je.data().toInt() ); - break; - } - restoreJournallingState(); -} - - - - -void note::redoStep( journalEntry & _je ) -{ - journalEntry je( _je.actionID(), -_je.data().toInt() ); - undoStep( je ); -}*/ - - - - void note::editDetuningPattern() { m_detuning->automationPattern()->openInAutomationEditor(); diff --git a/src/core/track.cpp b/src/core/track.cpp index 82694aaf5..f24ec6d0f 100644 --- a/src/core/track.cpp +++ b/src/core/track.cpp @@ -2,7 +2,7 @@ * track.cpp - implementation of classes concerning tracks -> necessary for * all track-like objects (beat/bassline, sample-track...) * - * Copyright (c) 2004-2012 Tobias Doerffel + * Copyright (c) 2004-2014 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -149,7 +149,6 @@ void trackContentObject::movePosition( const midiTime & _pos ) { if( m_startPosition != _pos ) { - addJournalEntry( JournalEntry( Move, m_startPosition - _pos ) ); m_startPosition = _pos; engine::getSong()->updateLength(); } @@ -170,7 +169,6 @@ void trackContentObject::changeLength( const midiTime & _length ) { if( m_length != _length ) { - addJournalEntry( JournalEntry( Resize, m_length - _length ) ); m_length = _length; engine::getSong()->updateLength(); } @@ -180,47 +178,6 @@ void trackContentObject::changeLength( const midiTime & _length ) -/*! \brief Undo one journal entry of this trackContentObject - * - * Restore the previous state of this track content object. This will - * restore the position or the length of the track content object - * depending on what was changed. - * - * \param _je The journal entry to undo - */ -void trackContentObject::undoStep( JournalEntry & _je ) -{ - saveJournallingState( false ); - switch( _je.actionID() ) - { - case Move: - movePosition( startPosition() + _je.data().toInt() ); - break; - case Resize: - changeLength( length() + _je.data().toInt() ); - break; - } - restoreJournallingState(); -} - - - - -/*! \brief Redo one journal entry of this trackContentObject - * - * Undoes one 'undo' of this track content object. - * - * \param _je The journal entry to redo - */ -void trackContentObject::redoStep( JournalEntry & _je ) -{ - JournalEntry je( _je.actionID(), -_je.data().toInt() ); - undoStep( je ); -} - - - - /*! \brief Copy this trackContentObject to the clipboard. * * Copies this track content object to the clipboard. @@ -367,6 +324,8 @@ bool trackContentObjectView::fixedTCOs() */ bool trackContentObjectView::close() { + m_trackView->getTrack()->addJournalCheckPoint(); + m_trackView->getTrackContentWidget()->removeTCOView( this ); return QWidget::close(); } @@ -577,6 +536,8 @@ void trackContentObjectView::mousePressEvent( QMouseEvent * _me ) /* engine::mainWindow()->isShiftPressed() == false &&*/ fixedTCOs() == false ) { + m_tco->addJournalCheckPoint(); + // move or resize m_tco->setJournalling( false ); @@ -773,9 +734,6 @@ void trackContentObjectView::mouseReleaseEvent( QMouseEvent * _me ) if( m_action == Move || m_action == Resize ) { m_tco->setJournalling( true ); - m_tco->addJournalEntry( JournalEntry( m_action, m_oldTime - - ( ( m_action == Move ) ? - m_tco->startPosition() : m_tco->length() ) ) ); } m_action = NoAction; delete m_hint; @@ -951,9 +909,6 @@ void trackContentWidget::updateBackground() void trackContentWidget::addTCOView( trackContentObjectView * _tcov ) { trackContentObject * tco = _tcov->getTrackContentObject(); -/* QMap map; - map["id"] = tco->id(); - addJournalEntry( JournalEntry( AddTrackContentObject, map ) );*/ m_tcoViews.push_back( _tcov ); @@ -978,14 +933,6 @@ void trackContentWidget::removeTCOView( trackContentObjectView * _tcov ) _tcov ); if( it != m_tcoViews.end() ) { -/* QMap map; - multimediaProject mmp( multimediaProject::JournalData ); - _tcov->getTrackContentObject()->saveState( mmp, mmp.content() ); - map["id"] = _tcov->getTrackContentObject()->id(); - map["state"] = mmp.toString(); - addJournalEntry( JournalEntry( RemoveTrackContentObject, - map ) );*/ - m_tcoViews.erase( it ); engine::getSong()->setModified(); } @@ -1127,6 +1074,7 @@ void trackContentWidget::dropEvent( QDropEvent * _de ) { const midiTime pos = getPosition( _de->pos().x() ).toNearestTact(); + getTrack()->addJournalCheckPoint(); trackContentObject * tco = getTrack()->createTCO( pos ); // value contains our XML-data so simply create a @@ -1167,6 +1115,7 @@ void trackContentWidget::mousePressEvent( QMouseEvent * _me ) { const midiTime pos = getPosition( _me->x() ).getTact() * midiTime::ticksPerTact(); + getTrack()->addJournalCheckPoint(); trackContentObject * tco = getTrack()->createTCO( pos ); tco->saveJournallingState( false ); @@ -1215,71 +1164,6 @@ void trackContentWidget::resizeEvent( QResizeEvent * resizeEvent ) -/*! \brief Undo an action on the trackContentWidget - * - * \param _je the details of the edit journal - */ -void trackContentWidget::undoStep( JournalEntry & _je ) -{ - saveJournallingState( false ); - switch( _je.actionID() ) - { - case AddTrackContentObject: - { - QMap map = _je.data().toMap(); - trackContentObject * tco = - dynamic_cast( - engine::projectJournal()->journallingObject( map["id"].toInt() ) ); - multimediaProject mmp( multimediaProject::JournalData ); - tco->saveState( mmp, mmp.content() ); - map["state"] = mmp.toString(); - _je.data() = map; - tco->deleteLater(); - break; - } - - case RemoveTrackContentObject: - { - trackContentObject * tco = getTrack()->createTCO( midiTime( 0 ) ); - multimediaProject mmp( - _je.data().toMap()["state"]. - toString().toUtf8() ); - tco->restoreState( mmp.content().firstChild().toElement() ); - break; - } - } - restoreJournallingState(); -} - - - - -/*! \brief Redo an action of the trackContentWidget - * - * \param _je the entry in the edit journal to redo. - */ -void trackContentWidget::redoStep( JournalEntry & _je ) -{ - switch( _je.actionID() ) - { - case AddTrackContentObject: - case RemoveTrackContentObject: - _je.actionID() = ( _je.actionID() == - AddTrackContentObject ) ? - RemoveTrackContentObject : - AddTrackContentObject; - undoStep( _je ); - _je.actionID() = ( _je.actionID() == - AddTrackContentObject ) ? - RemoveTrackContentObject : - AddTrackContentObject; - break; - } -} - - - - /*! \brief Return the track shown by the trackContentWidget * */ @@ -2231,55 +2115,6 @@ void trackView::modelChanged() -/*! \brief Undo a change to this track View. - * - * \param _je the Journal Entry to undo. - */ -void trackView::undoStep( JournalEntry & _je ) -{ - saveJournallingState( false ); - switch( _je.actionID() ) - { - case MoveTrack: - if( _je.data().toInt() > 0 ) - { - m_trackContainerView->moveTrackViewUp( this ); - } - else - { - m_trackContainerView->moveTrackViewDown( this ); - } - break; - case ResizeTrack: - setFixedHeight( qMax( height() + - _je.data().toInt(), - MINIMAL_TRACK_HEIGHT ) ); - m_trackContainerView->realignTracks(); - break; - /*case RestoreTrack: - setFixedHeight( DEFAULT_TRACK_HEIGHT ); - m_trackContainerView->realignTracks(); - break; */ - } - restoreJournallingState(); -} - - - - -/*! \brief Redo a change to this track View. - * - * \param _je the Journal Event to redo. - */ -void trackView::redoStep( JournalEntry & _je ) -{ - JournalEntry je( _je.actionID(), -_je.data().toInt() ); - undoStep( je ); -} - - - - /*! \brief Start a drag event on this track View. * * \param _dee the DragEnterEvent to start. @@ -2414,6 +2249,7 @@ void trackView::mouseMoveEvent( QMouseEvent * _me ) // a track-widget not equal to ourself? if( track_at_y != NULL && track_at_y != this ) { + addJournalCheckPoint(); // then move us up/down there! if( _me->y() < 0 ) { @@ -2423,7 +2259,6 @@ void trackView::mouseMoveEvent( QMouseEvent * _me ) { m_trackContainerView->moveTrackViewDown( this ); } - addJournalEntry( JournalEntry( MoveTrack, _me->y() ) ); } } else if( m_action == ResizeTrack ) diff --git a/src/core/track_container.cpp b/src/core/track_container.cpp index d9266f448..e366c42e2 100644 --- a/src/core/track_container.cpp +++ b/src/core/track_container.cpp @@ -2,7 +2,7 @@ * track_container.cpp - implementation of base-class for all track-containers * like Song-Editor, BB-Editor... * - * Copyright (c) 2004-2009 Tobias Doerffel + * Copyright (c) 2004-2014 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -73,10 +73,16 @@ void trackContainer::saveSettings( QDomDocument & _doc, QDomElement & _this ) void trackContainer::loadSettings( const QDomElement & _this ) { + bool journalRestore = _this.parentNode().nodeName() == "journaldata"; + if( journalRestore ) + { + clearAllTracks(); + } + static QProgressDialog * pd = NULL; bool was_null = ( pd == NULL ); int start_val = 0; - if( engine::hasGUI() ) + if( !journalRestore && engine::hasGUI() ) { if( pd == NULL ) { @@ -154,6 +160,8 @@ void trackContainer::addTrack( track * _track ) { if( _track->type() != track::HiddenAutomationTrack ) { + addJournalCheckPoint(); + m_tracksMutex.lockForWrite(); m_tracks.push_back( _track ); m_tracksMutex.unlock(); diff --git a/src/gui/track_container_view.cpp b/src/gui/track_container_view.cpp index d9da9e471..77f1305a4 100644 --- a/src/gui/track_container_view.cpp +++ b/src/gui/track_container_view.cpp @@ -121,10 +121,6 @@ void trackContainerView::loadSettings( const QDomElement & _this ) trackView * trackContainerView::addTrackView( trackView * _tv ) { -/* QMap map; - map["id"] = _tv->getTrack()->id(); - addJournalEntry( JournalEntry( AddTrack, map ) );*/ - m_trackViews.push_back( _tv ); m_scrollLayout->addWidget( _tv ); connect( this, SIGNAL( positionChanged( const midiTime & ) ), @@ -142,13 +138,6 @@ void trackContainerView::removeTrackView( trackView * _tv ) int index = m_trackViews.indexOf( _tv ); if( index != -1 ) { -/* QMap map; - multimediaProject mmp( multimediaProject::JournalData ); - _tv->getTrack()->saveState( mmp, mmp.content() ); - map["id"] = _tv->getTrack()->id(); - map["state"] = mmp.toString(); - addJournalEntry( JournalEntry( RemoveTrack, map ) );*/ - m_trackViews.removeAt( index ); disconnect( _tv ); @@ -238,6 +227,8 @@ void trackContainerView::createTrackView( track * _t ) void trackContainerView::deleteTrackView( trackView * _tv ) { + m_tc->addJournalCheckPoint(); + track * t = _tv->getTrack(); removeTrackView( _tv ); delete _tv; @@ -307,64 +298,6 @@ void trackContainerView::clearAllTracks() -void trackContainerView::undoStep( JournalEntry & _je ) -{ -#if 0 - saveJournallingState( false ); - switch( _je.actionID() ) - { - case AddTrack: - { - QMap map = _je.data().toMap(); - track * t = - dynamic_cast( - engine::projectJournal()->getJournallingObject( - map["id"].toInt() ) ); - assert( t != NULL ); - multimediaProject mmp( multimediaProject::JournalData ); - t->saveState( mmp, mmp.content() ); - map["state"] = mmp.toString(); - _je.data() = map; - t->deleteLater(); - break; - } - - case RemoveTrack: - { - multimediaProject mmp( - _je.data().toMap()["state"].toString().utf8() ); - track::create( mmp.content().firstChild().toElement(), - m_tc ); - break; - } - } - restoreJournallingState(); -#endif -} - - - - -void trackContainerView::redoStep( JournalEntry & _je ) -{ -#if 0 - switch( _je.actionID() ) - { - case AddTrack: - case RemoveTrack: - _je.actionID() = ( _je.actionID() == AddTrack ) ? - RemoveTrack : AddTrack; - undoStep( _je ); - _je.actionID() = ( _je.actionID() == AddTrack ) ? - RemoveTrack : AddTrack; - break; - } -#endif -} - - - - void trackContainerView::dragEnterEvent( QDragEnterEvent * _dee ) { stringPairDrag::processDragEnterEvent( _dee, diff --git a/src/gui/widgets/knob.cpp b/src/gui/widgets/knob.cpp index efb4b6e0b..9f6840349 100644 --- a/src/gui/widgets/knob.cpp +++ b/src/gui/widgets/knob.cpp @@ -1,7 +1,7 @@ /* * knob.cpp - powerful knob-widget * - * Copyright (c) 2004-2011 Tobias Doerffel + * Copyright (c) 2004-2014 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -438,7 +438,12 @@ void knob::mousePressEvent( QMouseEvent * _me ) ! ( _me->modifiers() & Qt::ControlModifier ) && ! ( _me->modifiers() & Qt::ShiftModifier ) ) { - model()->prepareJournalEntryFromOldVal(); + AutomatableModel *thisModel = model(); + if( thisModel ) + { + thisModel->addJournalCheckPoint(); + thisModel->saveJournallingState( false ); + } const QPoint & p = _me->pos(); m_origMousePos = p; @@ -485,7 +490,7 @@ void knob::mouseMoveEvent( QMouseEvent * _me ) void knob::mouseReleaseEvent( QMouseEvent * /* _me*/ ) { - model()->addJournalEntryFromOldToCurVal(); + model()->restoreJournallingState(); m_buttonPressed = false; diff --git a/src/gui/widgets/lcd_spinbox.cpp b/src/gui/widgets/lcd_spinbox.cpp index dcc1ebd09..b3b27ce06 100644 --- a/src/gui/widgets/lcd_spinbox.cpp +++ b/src/gui/widgets/lcd_spinbox.cpp @@ -100,7 +100,13 @@ void lcdSpinBox::mousePressEvent( QMouseEvent* event ) { m_origMousePos = event->globalPos(); QApplication::setOverrideCursor( Qt::BlankCursor ); - model()->prepareJournalEntryFromOldVal(); + + AutomatableModel *thisModel = model(); + if( thisModel ) + { + thisModel->addJournalCheckPoint(); + thisModel->saveJournallingState( false ); + } } else { @@ -131,7 +137,12 @@ void lcdSpinBox::mouseMoveEvent( QMouseEvent* event ) void lcdSpinBox::mouseReleaseEvent( QMouseEvent* event ) { - model()->addJournalEntryFromOldToCurVal(); + AutomatableModel *thisModel = model(); + if( thisModel ) + { + thisModel->restoreJournallingState(); + } + QCursor::setPos( m_origMousePos ); QApplication::restoreOverrideCursor(); From d0f6eaef3b503cc18d477aeaee4023436158337b Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Tue, 7 Jan 2014 23:43:36 +0100 Subject: [PATCH 2/5] PianoRoll: initial undo/redo support Most editing operations in PianoRoll are now tracked by the revised journalling and thus are included in global undo/redo. --- src/gui/piano_roll.cpp | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/gui/piano_roll.cpp b/src/gui/piano_roll.cpp index 41cdc060e..567ae0803 100644 --- a/src/gui/piano_roll.cpp +++ b/src/gui/piano_roll.cpp @@ -2,7 +2,7 @@ * piano_roll.cpp - implementation of piano-roll which is used for actual * writing of melodies * - * Copyright (c) 2004-2010 Tobias Doerffel + * Copyright (c) 2004-2014 Tobias Doerffel * Copyright (c) 2008 Andrew Kelley * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net @@ -1369,6 +1369,15 @@ void pianoRoll::keyReleaseEvent( QKeyEvent * _ke ) m_editMode = m_ctrlMode; update(); break; + + // update after undo/redo + case Qt::Key_Z: + case Qt::Key_R: + if( validPattern() && _ke->modifiers() == Qt::ControlModifier ) + { + update(); + } + break; } } @@ -1535,6 +1544,7 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me ) // area if( edit_note == true ) { + m_pattern->addJournalCheckPoint(); // scribble note edit changes mouseMoveEvent( _me ); return; @@ -1552,6 +1562,7 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me ) if( it == notes.begin()-1 ) { is_new_note = true; + m_pattern->addJournalCheckPoint(); m_pattern->setType( pattern::MelodyPattern ); // then set new note @@ -1597,8 +1608,7 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me ) // ops (move, resize) after this // code-block it = notes.begin(); - while( it != notes.end() && - *it != created_new_note ) + while( it != notes.end() && *it != created_new_note ) { ++it; } @@ -1677,6 +1687,7 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me ) RESIZE_AREA_WIDTH && m_currentNote->length() > 0 ) { + m_pattern->addJournalCheckPoint(); // then resize the note m_action = ActionResizeNote; @@ -1686,6 +1697,11 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me ) } else { + if( !created_new_note ) + { + m_pattern->addJournalCheckPoint(); + } + // otherwise move it m_action = ActionMoveNote; @@ -1741,6 +1757,7 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me ) m_mouseDownRight = true; if( it != notes.begin()-1 ) { + m_pattern->addJournalCheckPoint(); if( ( *it )->length() > 0 ) { m_pattern->removeNote( *it ); @@ -2595,7 +2612,6 @@ void pianoRoll::dragNotes( int x, int y, bool alt, bool shift ) if( ( *it )->selected() ) { - if( m_action == ActionMoveNote ) { // moving note @@ -3599,7 +3615,12 @@ void pianoRoll::pasteNotes() // remove selection and select the newly pasted notes clearSelectedNotes(); - + + if( !list.isEmpty() ) + { + m_pattern->addJournalCheckPoint(); + } + for( int i = 0; !list.item( i ).isNull(); ++i ) { // create the note @@ -3635,7 +3656,8 @@ void pianoRoll::deleteSelectedNotes() } bool update_after_delete = false; - + + m_pattern->addJournalCheckPoint(); // get note-vector of current pattern const NoteVector & notes = m_pattern->notes(); From 1f203a10f9fa30910ebf9cf286a8c064bf45c16e Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Tue, 7 Jan 2014 23:49:07 +0100 Subject: [PATCH 3/5] Widgets/Knob: only restore journalling state on release of left mouse button Do not try to restore the journalling state of the underlying model when releasing e.g the right mouse button as this causes a crash otherwise. --- src/gui/widgets/knob.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/gui/widgets/knob.cpp b/src/gui/widgets/knob.cpp index 9f6840349..333925106 100644 --- a/src/gui/widgets/knob.cpp +++ b/src/gui/widgets/knob.cpp @@ -488,9 +488,16 @@ void knob::mouseMoveEvent( QMouseEvent * _me ) -void knob::mouseReleaseEvent( QMouseEvent * /* _me*/ ) +void knob::mouseReleaseEvent( QMouseEvent* event ) { - model()->restoreJournallingState(); + if( event && event->button() == Qt::LeftButton ) + { + AutomatableModel *thisModel = model(); + if( thisModel ) + { + thisModel->restoreJournallingState(); + } + } m_buttonPressed = false; From 4641a8001b5ebd98087870c0fb25c094e3189d76 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Tue, 7 Jan 2014 23:50:27 +0100 Subject: [PATCH 4/5] 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 ) From 4f9ec51d7aa88c25249811af8cb7bc378576935a Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Tue, 7 Jan 2014 23:51:31 +0100 Subject: [PATCH 5/5] JournallingObject: header cleanups No need to include various QtCore header files anymore. Fix resulting missing headers in some other files. --- include/AutomationPattern.h | 1 + include/JournallingObject.h | 9 +++------ include/MidiPort.h | 4 ++-- plugins/vst_base/VstPlugin.h | 1 + 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/include/AutomationPattern.h b/include/AutomationPattern.h index 1d144d9bd..3c403cd17 100644 --- a/include/AutomationPattern.h +++ b/include/AutomationPattern.h @@ -27,6 +27,7 @@ #ifndef _AUTOMATION_PATTERN_H #define _AUTOMATION_PATTERN_H +#include #include #include "track.h" diff --git a/include/JournallingObject.h b/include/JournallingObject.h index 1be811d20..3f6e47a82 100644 --- a/include/JournallingObject.h +++ b/include/JournallingObject.h @@ -25,14 +25,11 @@ #ifndef _JOURNALLING_OBJECT_H #define _JOURNALLING_OBJECT_H -#include "lmms_basics.h" -#include "export.h" -#include "SerializingObject.h" - -#include -#include #include +#include "lmms_basics.h" +#include "SerializingObject.h" + class EXPORT JournallingObject : public SerializingObject { diff --git a/include/MidiPort.h b/include/MidiPort.h index a5b1aee0e..f1a1a3e83 100644 --- a/include/MidiPort.h +++ b/include/MidiPort.h @@ -2,7 +2,7 @@ * MidiPort.h - abstraction of MIDI ports which are part of LMMS's MIDI- * sequencing system * - * Copyright (c) 2005-2009 Tobias Doerffel + * Copyright (c) 2005-2010 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -28,7 +28,7 @@ #include #include -#include +#include #include "midi.h" #include "AutomatableModel.h" diff --git a/plugins/vst_base/VstPlugin.h b/plugins/vst_base/VstPlugin.h index 992e59ff0..705c61511 100644 --- a/plugins/vst_base/VstPlugin.h +++ b/plugins/vst_base/VstPlugin.h @@ -25,6 +25,7 @@ #ifndef _VST_PLUGIN_H #define _VST_PLUGIN_H +#include #include #include #include