From 15a579774c7bc0cc432783f276a470c8bdd705dd Mon Sep 17 00:00:00 2001 From: Jonathan Moore Liles Date: Sun, 11 Jan 2009 00:35:20 -0600 Subject: [PATCH] Implement storage and loading of unjournaled state for Loggable objects. --- Timeline/Log_Entry.C | 29 ++++++++++ Timeline/Log_Entry.H | 2 + Timeline/Loggable.C | 129 +++++++++++++++++++++++++++++++++++-------- Timeline/Loggable.H | 20 ++++--- Timeline/Project.C | 1 - Timeline/Track.C | 31 +++++++++-- Timeline/Track.H | 7 ++- 7 files changed, 179 insertions(+), 40 deletions(-) diff --git a/Timeline/Log_Entry.C b/Timeline/Log_Entry.C index f352f2c..9fbddbc 100644 --- a/Timeline/Log_Entry.C +++ b/Timeline/Log_Entry.C @@ -90,6 +90,35 @@ unescape ( char *s ) *r = '\0'; } +/** return a dynamically allocated string representing this log entry */ +char * +Log_Entry::print ( void ) const +{ + /* FIXME: gross over-allocation */ + char *r = (char*)malloc( 1024 ); + + r[0] = 0; + + for ( int i = 0; i < size(); ++i ) + { + const char *s, *v; + + get( i, &s, &v ); + + /* FIXME: arbitrary limit */ + char t[1024]; + snprintf( t, sizeof( t ), "%s %s%s", s, v, size() == i + 1 ? "" : " " ); + + strcat( r, t ); + } + + char *r2 = (char*)malloc( strlen( r ) + 1 ); + + strcpy( r2, r ); + + return r2; +} + /** sigh. parse a string of ":name value :name value" pairs into an * array of strings, one per pair */ // FIXME: doesn't handle the case of :name ":foo bar", nested quotes diff --git a/Timeline/Log_Entry.H b/Timeline/Log_Entry.H index bb8ecae..6808e30 100644 --- a/Timeline/Log_Entry.H +++ b/Timeline/Log_Entry.H @@ -67,6 +67,8 @@ public: void get ( int n, const char **name, const char **value ) const; char **sa ( void ); + char *print ( void ) const; + /* #define ADD ( type, format, exp ) \ */ /* void add ( const char *name, type v ) \ */ /* { \ */ diff --git a/Timeline/Loggable.C b/Timeline/Loggable.C index 3c58c3a..e1c86c5 100644 --- a/Timeline/Loggable.C +++ b/Timeline/Loggable.C @@ -50,6 +50,8 @@ off_t Loggable::_undo_offset = 0; size_t Loggable::_loggables_size = 0; Loggable ** Loggable::_loggables; +std::map Loggable::_loggables_unjournaled; + std::map Loggable::_class_map; std::queue Loggable::_transaction; @@ -59,6 +61,14 @@ void *Loggable::_progress_callback_arg = NULL; snapshot_func *Loggable::_snapshot_callback = NULL; void *Loggable::_snapshot_callback_arg = NULL; + + +Loggable::~Loggable ( ) +{ + _loggables[ _id - 1 ] = NULL; +} + + /** ensure that _loggables array is big enough for /n/ elements */ @@ -121,6 +131,8 @@ Loggable::open ( const char *filename ) return false; } + load_unjournaled_state(); + if ( newer( "snapshot", filename ) ) { MESSAGE( "Loading snapshot" ); @@ -146,6 +158,33 @@ Loggable::open ( const char *filename ) return true; } +bool +Loggable::load_unjournaled_state ( void ) +{ + FILE *fp; + + fp = fopen( "unjournaled", "r" ); + + if ( ! fp ) + return false; + + unsigned int id; + char buf[BUFSIZ]; + + while ( fscanf( fp, "%X set %[^\n]\n", &id, buf ) == 2 ) + { + Log_Entry *e = new Log_Entry( buf ); + + _loggables_unjournaled[ id - 1 ] = e; + + Loggable *l = Loggable::find( id ); + } + + fclose( fp ); + + return true; +} + #include #include @@ -214,11 +253,45 @@ Loggable::close ( void ) } } + save_unjournaled_state(); + _log_id = 0; return true; } + +/** save out unjournaled state for all loggables */ +bool +Loggable::save_unjournaled_state ( void ) +{ + + /* FIXME: check for errors */ + FILE *fp = fopen( "unjournaled", "w" ); + + /* write out the unjournaled state of all currently active + * loggables */ + for ( int i = 0; i < _log_id - 1; ++i ) + { + Log_Entry *e = _loggables_unjournaled[ i ]; + + if ( e ) + { + char *s = e->print(); + + fprintf( fp, "0x%X set %s\n", i + 1, s ); + + free( s ); + } + } + + /* write out the remembered state of inactive loggables. */ + + fclose( fp ); + + return true; +} + /** must be called after construction in create() methods */ void Loggable::update_id ( unsigned int id ) @@ -338,6 +411,12 @@ Loggable::do_this ( const char *s, bool reverse ) /* create */ Loggable *l = _class_map[ std::string( classname ) ]( e, id ); l->log_create(); + + /* we're now creating a loggable. Apply any unjournaled + * state it may have had in the past under this log ID */ + + if ( _loggables_unjournaled[ id - 1 ] ) + l->set( *_loggables_unjournaled[ id - 1 ] ); } } @@ -391,29 +470,6 @@ Loggable::undo ( void ) _undo_offset = uo; } -/** Make all loggable ids consecutive. This invalidates any existing - * journal or snapshot, so you *must* write out a new one after - * performing this operation*/ -void -Loggable::compact_ids ( void ) -{ - unsigned int id = 0; - for ( unsigned int i = 0; i < _log_id; ++i ) - if ( _loggables[ i ] ) - { - ++id; - - if ( _loggables[ id - 1 ] ) - continue; - - _loggables[ id - 1 ] = _loggables[ i ]; - _loggables[ i ] = NULL; - _loggables[ id - 1 ]->_id = id; - } - - _log_id = id; -} - /** write a snapshot of the current state of all loggable objects to * file handle /fp/ */ bool @@ -468,7 +524,6 @@ Loggable::compact ( void ) fseek( _fp, 0, SEEK_SET ); ftruncate( fileno( _fp ), 0 ); - compact_ids(); if ( ! snapshot( _fp ) ) FATAL( "Could not write snapshot!" ); @@ -654,11 +709,37 @@ Loggable::log_create ( void ) const Loggable::flush(); } +/** record this loggable's unjournaled state in memory */ +void +Loggable::record_unjournaled ( void ) const +{ + Log_Entry *e = new Log_Entry(); + + get_unjournaled( *e ); + + Log_Entry **le = &_loggables_unjournaled[ _id - 1 ]; + + if ( *le ) + delete *le; + + if ( e->size() ) + { + *le = e; + DMESSAGE( "logging %s", (*le)->print() ); + } + else + /* don't waste space on loggables with no unjournaled properties */ + *le = NULL; +} + /** Log object destruction. *Must* be called at the beginning of the * destructors of leaf classes */ void Loggable::log_destroy ( void ) const { + /* the unjournaled state may have changed: make a note of it. */ + record_unjournaled(); + if ( ! _fp ) /* tearing down... don't bother */ return; diff --git a/Timeline/Loggable.H b/Timeline/Loggable.H index 0a8e8d5..8e5bcb2 100644 --- a/Timeline/Loggable.H +++ b/Timeline/Loggable.H @@ -73,6 +73,7 @@ class Loggable static size_t _loggables_size; static Loggable ** _loggables; + static std::map _loggables_unjournaled; static std::map _class_map; @@ -94,13 +95,14 @@ private: static void ensure_size ( size_t n ); - void log_print( const Log_Entry *o, const Log_Entry *n ) const; + void log_print ( const Log_Entry *o, const Log_Entry *n ) const; static void log ( const char *fmt, ... ); static void flush ( void ); - static bool snapshot( FILE * fp ); - static bool snapshot( const char *name ); + static bool snapshot ( FILE * fp ); + static bool snapshot ( const char *name ); + static bool save_unjournaled_state ( void ); static bool replay ( FILE *fp ); void init ( bool loggable=true ) @@ -125,7 +127,8 @@ private: /* not implemented */ const Loggable & operator= ( const Loggable &rhs ); - static void compact_ids ( void ); + void record_unjournaled ( void ) const; + static bool load_unjournaled_state ( void ); public: @@ -153,10 +156,7 @@ public: void update_id ( unsigned int id ); - virtual ~Loggable ( ) - { - _loggables[ _id - 1 ] = NULL; - } + virtual ~Loggable ( ); static void @@ -167,6 +167,10 @@ public: /* log messages for journal */ virtual void get ( Log_Entry &e ) const = 0; + virtual void get_unjournaled ( Log_Entry & ) const + { + /* implementation optional */ + } virtual void set ( Log_Entry &e ) = 0; virtual const char *class_name ( void ) const = 0; diff --git a/Timeline/Project.C b/Timeline/Project.C index f9c0ba2..fe56016 100644 --- a/Timeline/Project.C +++ b/Timeline/Project.C @@ -176,7 +176,6 @@ Project::close ( void ) tle->save_timeline_settings(); Loggable::close(); - // write_info(); _is_open = false; diff --git a/Timeline/Track.C b/Timeline/Track.C index b863270..c7deb4a 100644 --- a/Timeline/Track.C +++ b/Timeline/Track.C @@ -73,21 +73,25 @@ Track::Track ( ) : Fl_Group( 0, 0, 1, 1 ) timeline->add_track( this ); } - Track::~Track ( ) { Loggable::block_start(); + /* must destroy sequences first to preserve proper log order */ + takes->clear(); + control->clear(); + annotation->clear(); + delete sequence(); + takes = NULL; control = NULL; annotation = NULL; - solo( false ); - - Fl_Group::clear(); - log_destroy(); + /* ensure that soloing accounting is performed */ + solo( false ); + timeline->remove_track( this ); /* give up our ports */ @@ -101,6 +105,7 @@ Track::~Track ( ) Loggable::block_end(); } + #include "FL/Boxtypes.H" void @@ -258,6 +263,12 @@ Track::set ( Log_Entry &e ) } else if ( ! strcmp( s, ":show-all-takes" ) ) show_all_takes( atoi( v ) ); + else if ( ! strcmp( s, ":solo" ) ) + solo( atoi( v ) ); + else if ( ! strcmp( s, ":mute" ) ) + mute( atoi( v ) ); + else if ( ! strcmp( s, ":arm" ) ) + armed( atoi( v ) ); else if ( ! strcmp( s, ":sequence" ) ) { int i; @@ -290,11 +301,19 @@ Track::get ( Log_Entry &e ) const e.add( ":name", _name ); e.add( ":sequence", sequence() ); e.add( ":selected", _selected ); + e.add( ":color", (unsigned long)color()); +} + +void +Track::get_unjournaled ( Log_Entry &e ) const +{ e.add( ":height", size() ); e.add( ":inputs", input.size() ); e.add( ":outputs", output.size() ); - e.add( ":color", (unsigned long)color()); e.add( ":show-all-takes", _show_all_takes ); + e.add( ":armed", armed() ); + e.add( ":mute", mute() ); + e.add( ":solo", solo() ); } void diff --git a/Timeline/Track.H b/Timeline/Track.H index 28e894c..20ff13d 100644 --- a/Timeline/Track.H +++ b/Timeline/Track.H @@ -104,6 +104,7 @@ private: protected: void get ( Log_Entry &e ) const; + void get_unjournaled ( Log_Entry &e ) const; void set ( Log_Entry &e ); public: @@ -177,11 +178,15 @@ public: const char * name ( void ) const { return _name; } bool mute ( void ) const { return mute_button->value(); } + void mute ( bool b ) { mute_button->value( b ); } bool solo ( void ) const { return solo_button->value(); } + void solo ( bool b ); + bool armed ( void ) const { return record_button->value(); } + void armed ( bool b ) { record_button->value( b ); } + bool selected ( void ) const { return _selected; } - void solo ( bool b ); static void cb_input_field ( Fl_Widget *w, void *v ); void cb_input_field ( void );