13
0

abstract concept of a history owner from ARDOUR::Session into libpbd

This commit is contained in:
Paul Davis 2024-06-27 21:39:28 -06:00
parent f80395fc2f
commit d30c8a1286
3 changed files with 286 additions and 0 deletions

183
libs/pbd/history_owner.cc Normal file
View File

@ -0,0 +1,183 @@
#include <string>
#include <vector>
#include "pbd/command.h"
#include "pbd/compose.h"
#include "pbd/debug.h"
#include "pbd/error.h"
#include "pbd/history_owner.h"
#include "pbd/stateful_diff_command.h"
#include "pbd/undo.h"
using namespace std;
using namespace PBD;
HistoryOwner::HistoryOwner (std::string const & str)
: _name (str)
, _current_trans (nullptr)
{
}
HistoryOwner::~HistoryOwner()
{
delete _current_trans;
}
void
HistoryOwner::add_commands (vector<Command*> const & cmds)
{
for (vector<Command*>::const_iterator i = cmds.begin(); i != cmds.end(); ++i) {
add_command (*i);
}
}
void
HistoryOwner::add_command (Command* const cmd)
{
assert (_current_trans);
if (!_current_trans) {
error << "Attempted to add an UNDO command without a current transaction. ignoring command (" << cmd->name() << ")" << endl;
return;
}
DEBUG_TRACE (DEBUG::UndoHistory,
string_compose ("Current Undo Transaction %1, adding command: %2\n",
_current_trans->name (),
cmd->name ()));
_current_trans->add_command (cmd);
}
PBD::StatefulDiffCommand*
HistoryOwner::add_stateful_diff_command (std::shared_ptr<PBD::StatefulDestructible> sfd)
{
PBD::StatefulDiffCommand* cmd = new PBD::StatefulDiffCommand (sfd);
add_command (cmd);
return cmd;
}
void
HistoryOwner::begin_reversible_command (const string& name)
{
begin_reversible_command (g_quark_from_string (name.c_str ()));
}
/** Begin a reversible command using a GQuark to identify it.
* begin_reversible_command() and commit_reversible_command() calls may be nested,
* but there must be as many begin...()s as there are commit...()s.
*/
void
HistoryOwner::begin_reversible_command (GQuark q)
{
if (_current_trans) {
#ifndef NDEBUG
cerr << "An UNDO transaction was started while a prior command was underway. Aborting command (" << g_quark_to_string (q) << ") and prior (" << _current_trans->name() << ")" << "\n";
#else
PBD::warning << "An UNDO transaction was started while a prior command was underway. Aborting command (" << g_quark_to_string (q) << ") and prior (" << _current_trans->name() << ")" << endmsg;
#endif
abort_reversible_command();
assert (false);
return;
}
/* If nested begin/commit pairs are used, we create just one UndoTransaction
to hold all the commands that are committed. This keeps the order of
commands correct in the history.
*/
if (_current_trans == 0) {
DEBUG_TRACE (DEBUG::UndoHistory, string_compose ("Begin Reversible Command, new transaction: %1\n", g_quark_to_string (q)));
/* start a new transaction */
assert (_current_trans_quarks.empty ());
_current_trans = new UndoTransaction();
_current_trans->set_name (g_quark_to_string (q));
} else {
DEBUG_TRACE (DEBUG::UndoHistory, string_compose ("Begin Reversible Command, current transaction: %1\n", _current_trans->name ()));
}
_current_trans_quarks.push_front (q);
}
void
HistoryOwner::abort_reversible_command ()
{
if (!_current_trans) {
return;
}
DEBUG_TRACE (DEBUG::UndoHistory, string_compose ("Abort Reversible Command: %1\n", _current_trans->name ()));
_current_trans->clear();
delete _current_trans;
_current_trans = nullptr;
_current_trans_quarks.clear();
}
bool
HistoryOwner::abort_empty_reversible_command ()
{
if (!collected_undo_commands ()) {
abort_reversible_command ();
return true;
}
return false;
}
void
HistoryOwner::commit_reversible_command (Command *cmd)
{
assert (_current_trans);
assert (!_current_trans_quarks.empty ());
if (!_current_trans) {
return;
}
struct timeval now;
if (cmd) {
DEBUG_TRACE (DEBUG::UndoHistory,
string_compose ("Current Undo Transaction %1, adding command: %2\n",
_current_trans->name (),
cmd->name ()));
_current_trans->add_command (cmd);
}
DEBUG_TRACE (DEBUG::UndoHistory,
string_compose ("Commit Reversible Command, current transaction: %1\n",
_current_trans->name ()));
_current_trans_quarks.pop_front ();
if (!_current_trans_quarks.empty ()) {
DEBUG_TRACE (DEBUG::UndoHistory,
string_compose ("Commit Reversible Command, transaction is not "
"top-level, current transaction: %1\n",
_current_trans->name ()));
/* the transaction we're committing is not the top-level one */
return;
}
if (_current_trans->empty()) {
/* no commands were added to the transaction, so just get rid of it */
DEBUG_TRACE (DEBUG::UndoHistory,
string_compose ("Commit Reversible Command, No commands were "
"added to current transaction: %1\n",
_current_trans->name ()));
delete _current_trans;
_current_trans = nullptr;
return;
}
gettimeofday (&now, 0);
_current_trans->set_timestamp (now);
DEBUG_TRACE (DEBUG::UndoHistory,
string_compose ("Commit Reversible Command, add to history %1\n",
_current_trans->name ()));
_history.add (_current_trans);
_current_trans = nullptr;
}
bool
HistoryOwner::operation_in_progress (GQuark op) const
{
return (find (_current_trans_quarks.begin(), _current_trans_quarks.end(), op) != _current_trans_quarks.end());
}

View File

@ -0,0 +1,102 @@
#ifndef __libpbd_history_owner_h__
#define __libpbd_history_owner_h__
#include <string>
#include <vector>
#include <list>
#include <glib.h>
#include "pbd/undo.h"
#include "pbd/libpbd_visibility.h"
namespace PBD {
class Command;
class LIBPBD_API HistoryOwner
{
public:
HistoryOwner (std::string const & name);
virtual ~HistoryOwner();
/** begin collecting undo information
*
* This call must always be followed by either
* begin_reversible_command() or commit_reversible_command()
*
* @param cmd_name human readable name for the undo operation
*/
void begin_reversible_command (const std::string& cmd_name);
void begin_reversible_command (GQuark);
/** abort an open undo command
* This must only be called after begin_reversible_command ()
*/
void abort_reversible_command ();
/** finalize an undo command and commit pending transactions
*
* This must only be called after begin_reversible_command ()
* @param cmd (additional) command to add
*/
void commit_reversible_command (PBD::Command* cmd = 0);
void add_command (PBD::Command *const cmd);
/** create an StatefulDiffCommand from the given object and add it to the stack.
*
* This function must only be called after begin_reversible_command.
* Failing to do so may lead to a crash.
*
* @param sfd the object to diff
* @returns the allocated StatefulDiffCommand (already added via add_command)
*/
PBD::StatefulDiffCommand* add_stateful_diff_command (std::shared_ptr<PBD::StatefulDestructible> sfd);
/** @return The list of operations that are currently in progress */
std::list<GQuark> const & current_operations () {
return _current_trans_quarks;
}
bool operation_in_progress (GQuark) const;
/**
* Test if any undo commands were added since the
* call to begin_reversible_command ()
*
* This is useful to determine if an undoable
* action was performed before adding additional
* information (e.g. selection changes) to the
* undo transaction.
*
* @return true if undo operation is valid but empty
*/
bool collected_undo_commands () const {
return _current_trans && !_current_trans->empty ();
}
PBD::UndoTransaction* current_reversible_command() { return _current_trans; }
/**
* Abort reversible command IFF no undo changes
* have been collected.
* @return true if undo operation was aborted.
*/
bool abort_empty_reversible_command ();
void add_commands (std::vector<PBD::Command*> const & cmds);
protected:
std::string _name;
PBD::UndoHistory _history;
/** current undo transaction, or 0 */
PBD::UndoTransaction* _current_trans;
/** GQuarks to describe the reversible commands that are currently in progress.
* These may be nested, in which case more recently-started commands are toward
* the front of the list.
*/
std::list<GQuark> _current_trans_quarks;
};
} /* namespace PBD */
#endif /* __libpbd_history_owner_h__ */

View File

@ -43,6 +43,7 @@ libpbd_sources = [
'file_utils.cc',
'fpu.cc',
'glib_event_source.cc',
'history_owner.cc',
'id.cc',
'inflater.cc',
'locale_guard.cc',