abstract concept of a history owner from ARDOUR::Session into libpbd
This commit is contained in:
parent
f80395fc2f
commit
d30c8a1286
183
libs/pbd/history_owner.cc
Normal file
183
libs/pbd/history_owner.cc
Normal 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());
|
||||
}
|
||||
|
102
libs/pbd/pbd/history_owner.h
Normal file
102
libs/pbd/pbd/history_owner.h
Normal 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__ */
|
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user