| @@ -38,7 +38,7 @@ public: | |||||
| { | { | ||||
| clear_visible_focus(); | clear_visible_focus(); | ||||
| up_box( FL_NO_BOX ); | up_box( FL_NO_BOX ); | ||||
| when(FL_WHEN_ENTER_KEY); | |||||
| when(FL_WHEN_ENTER_KEY_ALWAYS); | |||||
| } | } | ||||
| void up_box ( Fl_Boxtype b ) { _up_box = b; } | void up_box ( Fl_Boxtype b ) { _up_box = b; } | ||||
| @@ -734,6 +734,33 @@ Chain::resize ( int X, int Y, int W, int H ) | |||||
| /*****************/ | |||||
| /* Import/Export */ | |||||
| /*****************/ | |||||
| void | |||||
| Chain::snapshot ( void *v ) | |||||
| { | |||||
| ((Chain*)v)->snapshot(); | |||||
| } | |||||
| void | |||||
| Chain::snapshot ( void ) | |||||
| { | |||||
| log_children(); | |||||
| } | |||||
| bool | |||||
| Chain::do_export ( const char *filename ) | |||||
| { | |||||
| MESSAGE( "Exporting chain state" ); | |||||
| Loggable::snapshot_callback( &Chain::snapshot, this ); | |||||
| Loggable::snapshot( filename ); | |||||
| return true; | |||||
| } | |||||
| /**********/ | /**********/ | ||||
| /* Engine */ | /* Engine */ | ||||
| /**********/ | /**********/ | ||||
| @@ -57,6 +57,9 @@ class Chain : public Fl_Group, public Loggable { | |||||
| private: | private: | ||||
| static void snapshot ( void *v ); | |||||
| void snapshot ( void ); | |||||
| void cb_handle(Fl_Widget*); | void cb_handle(Fl_Widget*); | ||||
| static void cb_handle(Fl_Widget*, void*); | static void cb_handle(Fl_Widget*, void*); | ||||
| @@ -104,6 +107,8 @@ public: | |||||
| bool insert ( Module *m, Module *n ); | bool insert ( Module *m, Module *n ); | ||||
| void add_control ( Controller_Module *m ); | void add_control ( Controller_Module *m ); | ||||
| bool do_export ( const char *filename ); | |||||
| void initialize_with_default ( void ); | void initialize_with_default ( void ); | ||||
| bool can_configure_outputs ( Module *m, int n ) const; | bool can_configure_outputs ( Module *m, int n ) const; | ||||
| @@ -251,6 +251,16 @@ void Mixer::cb_menu(Fl_Widget* o) { | |||||
| command_add_strip(); | command_add_strip(); | ||||
| } | } | ||||
| } | } | ||||
| else if ( !strcmp( picked, "&Mixer/&Import Strip" ) ) | |||||
| { | |||||
| const char *s = fl_file_chooser( "Export strip to filename:", "*.strip", NULL, 0 ); | |||||
| if ( s ) | |||||
| { | |||||
| if (! Mixer_Strip::import_strip( s ) ) | |||||
| fl_alert( "%s", "Failed to import strip!" ); | |||||
| } | |||||
| } | |||||
| else if (! strcmp( picked, "&Mixer/&Rows/One") ) | else if (! strcmp( picked, "&Mixer/&Rows/One") ) | ||||
| { | { | ||||
| rows( 1 ); | rows( 1 ); | ||||
| @@ -382,6 +392,7 @@ Mixer::Mixer ( int X, int Y, int W, int H, const char *L ) : | |||||
| o->add( "&Project/&Quit", FL_CTRL + 'q', 0, 0 ); | o->add( "&Project/&Quit", FL_CTRL + 'q', 0, 0 ); | ||||
| o->add( "&Mixer/&Add Strip", 'a', 0, 0 ); | o->add( "&Mixer/&Add Strip", 'a', 0, 0 ); | ||||
| o->add( "&Mixer/Add &N Strips" ); | o->add( "&Mixer/Add &N Strips" ); | ||||
| o->add( "&Mixer/&Import Strip" ); | |||||
| o->add( "&Mixer/&Rows/One", '1', 0, 0 ); | o->add( "&Mixer/&Rows/One", '1', 0, 0 ); | ||||
| o->add( "&Mixer/&Rows/Two", '2', 0, 0 ); | o->add( "&Mixer/&Rows/Two", '2', 0, 0 ); | ||||
| o->add( "&Mixer/&Rows/Three", '3', 0, 0 ); | o->add( "&Mixer/&Rows/Three", '3', 0, 0 ); | ||||
| @@ -47,7 +47,6 @@ private: | |||||
| Fl_Color system_colors[3]; | Fl_Color system_colors[3]; | ||||
| Mixer_Strip* track_by_name ( const char *name ); | Mixer_Strip* track_by_name ( const char *name ); | ||||
| char * get_unique_track_name ( const char *name ); | |||||
| void snapshot ( void ); | void snapshot ( void ); | ||||
| static void snapshot ( void *v ) { ((Mixer*)v)->snapshot(); } | static void snapshot ( void *v ) { ((Mixer*)v)->snapshot(); } | ||||
| @@ -74,7 +73,9 @@ protected: | |||||
| public: | public: | ||||
| int min_h ( void ) const { return Mixer_Strip::min_h() + (18 * 2); } | |||||
| char * get_unique_track_name ( const char *name ); | |||||
| int min_h ( void ) const { return Mixer_Strip::min_h() + (18 * 2); } | |||||
| void rows ( int n ); | void rows ( int n ); | ||||
| virtual void resize ( int X, int Y, int W, int H ); | virtual void resize ( int X, int Y, int W, int H ); | ||||
| @@ -56,6 +56,7 @@ | |||||
| #include <FL/Fl_Menu_Button.H> | #include <FL/Fl_Menu_Button.H> | ||||
| #include "FL/test_press.H" | #include "FL/test_press.H" | ||||
| #include "FL/menu_popup.H" | #include "FL/menu_popup.H" | ||||
| #include <FL/Fl_File_Chooser.H> | |||||
| extern Mixer *mixer; | extern Mixer *mixer; | ||||
| @@ -242,7 +243,7 @@ void Mixer_Strip::cb_handle(Fl_Widget* o) { | |||||
| else if ( o == name_field ) | else if ( o == name_field ) | ||||
| { | { | ||||
| name( name_field->value() ); | name( name_field->value() ); | ||||
| take_focus(); | |||||
| Fl::focus( this ); | |||||
| } | } | ||||
| else if ( o == width_button ) | else if ( o == width_button ) | ||||
| { | { | ||||
| @@ -261,8 +262,12 @@ void Mixer_Strip::cb_handle(Fl_Widget* o, void* v) { | |||||
| } | } | ||||
| void | void | ||||
| Mixer_Strip::name ( const char *name ) { | |||||
| Mixer_Strip::name ( const char *name ) | |||||
| { | |||||
| if ( this->name() && !strcmp( name, this->name() ) ) | |||||
| return; | |||||
| name = mixer->get_unique_track_name( name ); | |||||
| char *s = strdup( name ); | char *s = strdup( name ); | ||||
| @@ -521,6 +526,43 @@ Mixer_Strip::draw ( void ) | |||||
| Fl_Group::draw_box( FL_UP_FRAME, x(), y(), w(), h(), Fl::focus() == this ? Fl_Group::selection_color() : FL_BLACK ); | Fl_Group::draw_box( FL_UP_FRAME, x(), y(), w(), h(), Fl::focus() == this ? Fl_Group::selection_color() : FL_BLACK ); | ||||
| } | } | ||||
| /*****************/ | |||||
| /* Import/Export */ | |||||
| /*****************/ | |||||
| void | |||||
| Mixer_Strip::snapshot ( void *v ) | |||||
| { | |||||
| ((Mixer_Strip*)v)->snapshot(); | |||||
| } | |||||
| void | |||||
| Mixer_Strip::snapshot ( void ) | |||||
| { | |||||
| log_children(); | |||||
| } | |||||
| bool | |||||
| Mixer_Strip::export_strip ( const char *filename ) | |||||
| { | |||||
| MESSAGE( "Exporting chain state" ); | |||||
| Loggable::snapshot_callback( &Mixer_Strip::snapshot, this ); | |||||
| Loggable::snapshot( filename ); | |||||
| return true; | |||||
| } | |||||
| bool | |||||
| Mixer_Strip::import_strip ( const char *filename ) | |||||
| { | |||||
| MESSAGE( "Importing new chain state" ); | |||||
| Loggable::begin_relative_id_mode(); | |||||
| int r = Loggable::replay( filename ); | |||||
| Loggable::end_relative_id_mode(); | |||||
| return r; | |||||
| } | |||||
| void | void | ||||
| @@ -559,6 +601,20 @@ Mixer_Strip::menu_cb ( const Fl_Menu_ *m ) | |||||
| redraw(); | redraw(); | ||||
| } | } | ||||
| else if ( !strcmp( picked, "/Export Strip" ) ) | |||||
| { | |||||
| char *suggested_name; | |||||
| asprintf( &suggested_name, "%s.strip", name() ); | |||||
| const char *s = fl_file_chooser( "Export strip to filename:", "*.strip", suggested_name, 0 ); | |||||
| free( suggested_name ); | |||||
| if ( s ) | |||||
| export_strip( s ); | |||||
| fl_message( "Strip exported." ); | |||||
| } | |||||
| else if ( ! strcmp( picked, "/Remove" ) ) | else if ( ! strcmp( picked, "/Remove" ) ) | ||||
| { | { | ||||
| if ( Fl::event_shift() || 1 == fl_choice( "Are you sure you want to remove this strip?\n\n(this action cannot be undone)", "Cancel", "Remove", NULL ) ) | if ( Fl::event_shift() || 1 == fl_choice( "Are you sure you want to remove this strip?\n\n(this action cannot be undone)", "Cancel", "Remove", NULL ) ) | ||||
| @@ -598,6 +654,7 @@ Mixer_Strip::menu ( void ) const | |||||
| { "Move Left", '[', 0, 0 }, | { "Move Left", '[', 0, 0 }, | ||||
| { "Move Right", ']', 0, 0 }, | { "Move Right", ']', 0, 0 }, | ||||
| { "Color", 0, 0, 0 }, | { "Color", 0, 0, 0 }, | ||||
| { "Export Strip", 0, 0, 0 }, | |||||
| { "Rename", FL_CTRL + 'n', 0, 0 }, | { "Rename", FL_CTRL + 'n', 0, 0 }, | ||||
| { "Remove", FL_Delete, 0, 0 }, | { "Remove", FL_Delete, 0, 0 }, | ||||
| { 0 }, | { 0 }, | ||||
| @@ -125,6 +125,10 @@ private: | |||||
| static void menu_cb ( Fl_Widget *w, void *v ); | static void menu_cb ( Fl_Widget *w, void *v ); | ||||
| Fl_Menu_Button & menu ( void ) const; | Fl_Menu_Button & menu ( void ) const; | ||||
| static void snapshot ( void *v ); | |||||
| void snapshot ( void ); | |||||
| bool export_strip ( const char *filename ); | |||||
| protected: | protected: | ||||
| void get ( Log_Entry &e ) const; | void get ( Log_Entry &e ) const; | ||||
| @@ -135,6 +139,8 @@ protected: | |||||
| public: | public: | ||||
| static bool import_strip ( const char *filename ); | |||||
| void command_move_left ( void ); | void command_move_left ( void ); | ||||
| void command_move_right ( void ); | void command_move_right ( void ); | ||||
| void command_close ( void ); | void command_close ( void ); | ||||
| @@ -88,6 +88,9 @@ Loggable::block_end ( void ) | |||||
| Loggable * | Loggable * | ||||
| Loggable::find ( unsigned int id ) | Loggable::find ( unsigned int id ) | ||||
| { | { | ||||
| if ( _relative_id ) | |||||
| id += _relative_id; | |||||
| return _loggables[ id ].loggable; | return _loggables[ id ].loggable; | ||||
| } | } | ||||
| @@ -329,6 +332,25 @@ Loggable::escape ( const char *s ) | |||||
| return r; | return r; | ||||
| } | } | ||||
| unsigned int Loggable::_relative_id = 0; | |||||
| /* calls to do_this() between invocation of this method and | |||||
| * end_relative_id_mode() will have all their IDs made relative to the | |||||
| * highest available ID at this time of this call. Non-Mixer uses | |||||
| * this to allow importing of module chains */ | |||||
| void | |||||
| Loggable::begin_relative_id_mode ( void ) | |||||
| { | |||||
| _relative_id = ++_log_id; | |||||
| } | |||||
| void | |||||
| Loggable::end_relative_id_mode ( void ) | |||||
| { | |||||
| _relative_id = 0; | |||||
| } | |||||
| /** 'do' a message like "Audio_Region 0xF1 set :r 123" */ | /** 'do' a message like "Audio_Region 0xF1 set :r 123" */ | ||||
| bool | bool | ||||
| Loggable::do_this ( const char *s, bool reverse ) | Loggable::do_this ( const char *s, bool reverse ) | ||||
| @@ -395,6 +417,9 @@ Loggable::do_this ( const char *s, bool reverse ) | |||||
| ASSERT( _class_map[ std::string( classname ) ], "Journal contains an object of class \"%s\", but I don't know how to create such objects.", classname ); | ASSERT( _class_map[ std::string( classname ) ], "Journal contains an object of class \"%s\", but I don't know how to create such objects.", classname ); | ||||
| { | { | ||||
| if ( _relative_id ) | |||||
| id += _relative_id; | |||||
| /* create */ | /* create */ | ||||
| Loggable *l = _class_map[ std::string( classname ) ]( e, id ); | Loggable *l = _class_map[ std::string( classname ) ]( e, id ); | ||||
| l->log_create(); | l->log_create(); | ||||
| @@ -89,6 +89,8 @@ class Loggable | |||||
| private: | private: | ||||
| static unsigned int _relative_id; | |||||
| unsigned int _id; | unsigned int _id; | ||||
| Log_Entry *_old_state; | Log_Entry *_old_state; | ||||
| @@ -183,6 +185,10 @@ public: | |||||
| virtual void log_children ( void ) const { return; } | virtual void log_children ( void ) const { return; } | ||||
| static void begin_relative_id_mode ( void ); | |||||
| static void end_relative_id_mode ( void ); | |||||
| static bool do_this ( const char *s, bool reverse ); | static bool do_this ( const char *s, bool reverse ); | ||||
| static int dirty ( void ) { return _dirty; } | static int dirty ( void ) { return _dirty; } | ||||