Bug2531: need progress indication when discarding undo history...
... You may see this also when abandoning lots of redo history, and not only when doing compaction. If in compaction you discard much undo and also much redo, you may see two progresses. It's debatable whether this might have been better implemented by reuse of ProjectFileIO::DeleteBlocks instead, putting callbacks to a progress indicator in the function InSet(). But I wanted to avoid more dependency onto ProjectFileIO. Doing real work in DeleteBlocks() is supposed to happen only if there is a bug elsewhere that allowed orphans. So, still no progress indicator there.
This commit is contained in:
parent
dfd313f8fa
commit
8a86fe139f
|
@ -135,6 +135,13 @@ public:
|
|||
/*! @return ids of all sample blocks created by this factory and still extant */
|
||||
virtual SampleBlockIDs GetActiveBlockIDs() = 0;
|
||||
|
||||
//! Type of function that is informed when a block is about to be deleted
|
||||
using BlockDeletionCallback = std::function< void(const SampleBlock&) >;
|
||||
|
||||
//! Install a callback, returning the previously installed callback
|
||||
virtual BlockDeletionCallback SetBlockDeletionCallback(
|
||||
BlockDeletionCallback callback ) = 0;
|
||||
|
||||
protected:
|
||||
// The override should throw more informative exceptions on error than the
|
||||
// default InconsistencyException thrown by Create
|
||||
|
|
|
@ -147,6 +147,9 @@ public:
|
|||
sampleFormat srcformat,
|
||||
const wxChar **attrs) override;
|
||||
|
||||
BlockDeletionCallback SetBlockDeletionCallback(
|
||||
BlockDeletionCallback callback ) override;
|
||||
|
||||
private:
|
||||
friend SqliteSampleBlock;
|
||||
|
||||
|
@ -159,6 +162,8 @@ private:
|
|||
using AllBlocksMap =
|
||||
std::map< SampleBlockID, std::weak_ptr< SqliteSampleBlock > >;
|
||||
AllBlocksMap mAllBlocks;
|
||||
|
||||
BlockDeletionCallback mCallback;
|
||||
};
|
||||
|
||||
SqliteSampleBlockFactory::SqliteSampleBlockFactory( AudacityProject &project )
|
||||
|
@ -272,6 +277,14 @@ SampleBlockPtr SqliteSampleBlockFactory::DoCreateFromXML(
|
|||
return sb;
|
||||
}
|
||||
|
||||
auto SqliteSampleBlockFactory::SetBlockDeletionCallback(
|
||||
BlockDeletionCallback callback ) -> BlockDeletionCallback
|
||||
{
|
||||
auto result = mCallback;
|
||||
mCallback = std::move( callback );
|
||||
return result;
|
||||
}
|
||||
|
||||
SqliteSampleBlock::SqliteSampleBlock(
|
||||
const std::shared_ptr<SqliteSampleBlockFactory> &pFactory)
|
||||
: mpFactory(pFactory)
|
||||
|
@ -287,6 +300,10 @@ SqliteSampleBlock::SqliteSampleBlock(
|
|||
|
||||
SqliteSampleBlock::~SqliteSampleBlock()
|
||||
{
|
||||
auto &callback = mpFactory->mCallback;
|
||||
if (callback)
|
||||
GuardedCall( [&]{ callback( *this ); } );
|
||||
|
||||
if (IsSilent()) {
|
||||
// The block object was constructed but failed to Load() or Commit().
|
||||
// Or it's a silent block with no row in the database.
|
||||
|
|
|
@ -36,6 +36,7 @@ UndoManager
|
|||
#include "NoteTrack.h" // for Sonify* function declarations
|
||||
#include "Diags.h"
|
||||
#include "Tags.h"
|
||||
#include "widgets/ProgressDialog.h"
|
||||
|
||||
|
||||
#include <unordered_set>
|
||||
|
@ -180,8 +181,51 @@ void UndoManager::RemoveStateAt(int n)
|
|||
}
|
||||
|
||||
|
||||
//! Just to find a denominator for a progress indicator.
|
||||
/*! This estimate procedure should in fact be exact */
|
||||
size_t UndoManager::EstimateRemovedBlocks(size_t begin, size_t end)
|
||||
{
|
||||
// Collect ids that survive
|
||||
SampleBlockIDSet wontDelete;
|
||||
auto f = [&](const auto &p){
|
||||
InspectBlocks(*p->state.tracks, {}, &wontDelete);
|
||||
};
|
||||
auto first = stack.begin(), last = stack.end();
|
||||
std::for_each( first, first + begin, f );
|
||||
std::for_each( first + end, last, f );
|
||||
|
||||
// Collect ids that won't survive (and are not negative pseudo ids)
|
||||
SampleBlockIDSet seen, mayDelete;
|
||||
std::for_each( first + begin, first + end, [&](const auto &p){
|
||||
auto &tracks = *p->state.tracks;
|
||||
InspectBlocks(tracks, [&]( const SampleBlock &block ){
|
||||
auto id = block.GetBlockID();
|
||||
if ( id > 0 && !wontDelete.count( id ) )
|
||||
mayDelete.insert( id );
|
||||
},
|
||||
&seen);
|
||||
} );
|
||||
return mayDelete.size();
|
||||
}
|
||||
|
||||
void UndoManager::RemoveStates(size_t begin, size_t end)
|
||||
{
|
||||
// Install a callback function that updates a progress indicator
|
||||
unsigned long long nToDelete = EstimateRemovedBlocks(begin, end),
|
||||
nDeleted = 0;
|
||||
ProgressDialog dialog{ XO("Progress"), XO("Discarding undo/redo history"),
|
||||
pdlgHideStopButton | pdlgHideCancelButton
|
||||
};
|
||||
auto callback = [&](const SampleBlock &){
|
||||
dialog.Update(++nDeleted, nToDelete);
|
||||
};
|
||||
auto &trackFactory = WaveTrackFactory::Get( mProject );
|
||||
auto &pSampleBlockFactory = trackFactory.GetSampleBlockFactory();
|
||||
auto prevCallback =
|
||||
pSampleBlockFactory->SetBlockDeletionCallback(callback);
|
||||
auto cleanup = finally([&]{ pSampleBlockFactory->SetBlockDeletionCallback( prevCallback ); });
|
||||
|
||||
// Wrap the whole in a savepoint for better performance
|
||||
Optional<TransactionScope> pTrans;
|
||||
auto pConnection = ConnectionPtr::Get(mProject).mpConnection.get();
|
||||
if (pConnection)
|
||||
|
@ -196,8 +240,14 @@ void UndoManager::RemoveStates(size_t begin, size_t end)
|
|||
--saved;
|
||||
}
|
||||
|
||||
// Success, commit the savepoint
|
||||
if (pTrans)
|
||||
pTrans->Commit();
|
||||
|
||||
// Check sanity
|
||||
wxASSERT_MSG(
|
||||
nDeleted == 0 || // maybe bypassing all deletions
|
||||
nDeleted == nToDelete, "Block count was misestimated");
|
||||
}
|
||||
|
||||
void UndoManager::ClearStates()
|
||||
|
|
|
@ -172,6 +172,8 @@ class AUDACITY_DLL_API UndoManager final
|
|||
// void Debug(); // currently unused
|
||||
|
||||
private:
|
||||
size_t EstimateRemovedBlocks(size_t begin, size_t end);
|
||||
|
||||
void RemoveStateAt(int n);
|
||||
|
||||
AudacityProject &mProject;
|
||||
|
|
Loading…
Reference in New Issue