diff --git a/gtk2_ardour/strip_import_dialog.cc b/gtk2_ardour/strip_import_dialog.cc index 4748861823..d6c4af8c65 100644 --- a/gtk2_ardour/strip_import_dialog.cc +++ b/gtk2_ardour/strip_import_dialog.cc @@ -16,22 +16,660 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "strip_import_dialog.h" +#include +#include +#include "pbd/basename.h" +#include "pbd/file_utils.h" +#include "pbd/replace_all.h" + +#include "ardour/directory_names.h" +#include "ardour/filename_extensions.h" +#include "ardour/filesystem_paths.h" +#include "ardour/recent_sessions.h" #include "ardour/session.h" +#include "gtkmm2ext/utils.h" + +#include "widgets/ardour_button.h" +#include "widgets/ardour_dropdown.h" +#include "widgets/ardour_spacer.h" + +#include "strip_import_dialog.h" + #include "pbd/i18n.h" using namespace Gtk; using namespace std; using namespace PBD; using namespace ARDOUR; +using namespace ArdourWidgets; StripImportDialog::StripImportDialog (Session* s) : ArdourDialog (_("Import Track/Bus State")) + , _add_rid (0) + , _add_eid (0) { + set_session (s); + + _open_button = manage (new Button (Stock::GO_FORWARD)); + _ok_button = manage (new Button (Stock::OK)); + + get_action_area ()->pack_start (_info_text); + add_button (Stock::CANCEL, RESPONSE_CANCEL); + get_action_area ()->pack_end (*_open_button); + get_action_area ()->pack_end (*_ok_button); + + _open_button->show (); + _ok_button->hide (); + + _open_button->signal_clicked ().connect (mem_fun (*this, &StripImportDialog::maybe_switch_to_import_page), false); + _ok_button->signal_clicked ().connect (mem_fun (*this, &StripImportDialog::ok_activated), false); + + _open_button->set_sensitive (false); + _ok_button->set_sensitive (false); + + get_vbox ()->pack_start (_page_file); + setup_file_page (); } StripImportDialog::~StripImportDialog () { + _notebook_connection.disconnect (); + _chooser_connection.disconnect (); +} + +/* **************************************************************************** + * Page One .. pick file to import + */ + +void +StripImportDialog::setup_file_page () +{ + /* file-chooser */ + _chooser.set_size_request (450, 300); + _chooser.set_current_folder (poor_mans_glob (Config->get_default_session_parent_dir ())); + + FileFilter tracks_filter; + tracks_filter.add_pattern (string_compose (X_("*%1"), routestate_suffix)); + tracks_filter.set_name (string_compose (_("%1 tracks"), PROGRAM_NAME)); + _chooser.add_filter (tracks_filter); + + FileFilter template_filter; + template_filter.add_pattern (string_compose (X_("*%1"), template_suffix)); + template_filter.set_name (string_compose (_("%1 tracks"), PROGRAM_NAME)); + _chooser.add_filter (template_filter); + + FileFilter session_filter; + session_filter.add_pattern (string_compose (X_("*%1"), ARDOUR::statefile_suffix)); + session_filter.set_name (string_compose (_("%1 sessions"), PROGRAM_NAME)); + _chooser.add_filter (session_filter); + + FileFilter all_filter; + all_filter.add_pattern (string_compose (X_("*%1"), ARDOUR::statefile_suffix)); + all_filter.add_pattern (string_compose (X_("*%1"), template_suffix)); + all_filter.add_pattern (string_compose (X_("*%1"), routestate_suffix)); + all_filter.set_name (_("All supported files")); + _chooser.add_filter (all_filter); + _chooser.set_filter (all_filter); + + Gtkmm2ext::add_volume_shortcuts (_chooser); + + _chooser_connection = _chooser.signal_selection_changed ().connect (mem_fun (*this, &StripImportDialog::file_selection_changed)); + _chooser.signal_file_activated ().connect (sigc::mem_fun (*this, &StripImportDialog::maybe_switch_to_import_page)); + + _notebook.append_page (_chooser, _("File")); + + guint page = 1; + + /* recent Sessions */ + ARDOUR::RecentSessions rs; + ARDOUR::read_recent_sessions (rs); + + if (!rs.empty ()) { + _recent_model = TreeStore::create (_columns); + + /* setup model - compare to SessionDialog::redisplay_recent_sessions */ + for (auto const& [_, dir] : rs) { + string dirname = dir; + if (dirname.empty ()) { + continue; + } + if (dirname[dirname.length () - 1] == '/') { + dirname = dirname.substr (0, dirname.length () - 1); + } + /* check whether session still exists */ + if (!Glib::file_test (dirname, Glib::FILE_TEST_EXISTS)) { + /* session doesn't exist */ + continue; + } + /* now get available states for this session */ + vector state_file_names = Session::possible_states (dirname); + if (state_file_names.empty ()) { + /* no state file? */ + continue; + } + TreeModel::Row row = *(_recent_model->append ()); + if (state_file_names.size () > 1) { + row[_columns.name] = PBD::basename_nosuffix (dirname); + row[_columns.path] = ""; + for (auto const& snap : state_file_names) { + Gtk::TreeModel::Row child_row = *(_recent_model->append (row.children ())); + child_row[_columns.name] = snap; + child_row[_columns.path] = Glib::build_filename (dirname, snap + statefile_suffix); + } + } else { + row[_columns.name] = state_file_names.front (); + row[_columns.path] = Glib::build_filename (dirname, state_file_names.front () + statefile_suffix); + } + } + + _recent_treeview.set_model (_recent_model); + _recent_treeview.append_column (_("Session Name"), _columns.name); + _recent_treeview.set_headers_visible (true); + _recent_treeview.get_selection ()->set_mode (SELECTION_SINGLE); + + _recent_treeview.get_selection ()->signal_changed ().connect (sigc::bind (sigc::mem_fun (*this, &StripImportDialog::treeview_selection_changed), &_recent_treeview, Snapshot)); + _recent_treeview.signal_row_activated ().connect ([&] (const TreeModel::Path&, TreeViewColumn*) { maybe_switch_to_import_page (); }); + + _recent_scroller.set_policy (POLICY_AUTOMATIC, POLICY_AUTOMATIC); + _recent_scroller.add (_recent_treeview); + + _notebook.append_page (_recent_scroller, _("Recent Sessions")); + + _notebook_type[page] = Snapshot; + _notebook_content[page++] = &_recent_treeview; + } + + /* template list */ + vector templates; + find_session_templates (templates, false); + + if (templates.size () > 0) { + _template_model = ListStore::create (_columns); + setup_model (_template_model, templates); + + _template_treeview.set_model (_template_model); + _template_treeview.append_column (_("Name"), _columns.name); + _template_treeview.set_headers_visible (true); + + _template_treeview.get_selection ()->signal_changed ().connect (sigc::bind (sigc::mem_fun (*this, &StripImportDialog::treeview_selection_changed), &_template_treeview, Template)); + _template_treeview.signal_row_activated ().connect ([&] (const TreeModel::Path&, TreeViewColumn*) { maybe_switch_to_import_page (); }); + + _template_scroller.set_policy (POLICY_AUTOMATIC, POLICY_AUTOMATIC); + _template_scroller.add (_template_treeview); + + _notebook.append_page (_template_scroller, _("Session Templates")); + + _notebook_type[page] = Template; + _notebook_content[page++] = &_template_treeview; + } + + templates.clear (); + Searchpath local_path (_session->path ()); + local_path.add_subdirectory_to_paths (routestates_dir_name); + find_presets (local_path, templates); + if (templates.size () > 0) { + _local_pset_model = ListStore::create (_columns); + setup_model (_local_pset_model, templates); + + _local_pset_treeview.set_model (_local_pset_model); + _local_pset_treeview.append_column (_("Name"), _columns.name); + _local_pset_treeview.set_headers_visible (true); + + _local_pset_treeview.get_selection ()->signal_changed ().connect (sigc::bind (sigc::mem_fun (*this, &StripImportDialog::treeview_selection_changed), &_local_pset_treeview, RouteState)); + _local_pset_treeview.signal_row_activated ().connect ([&] (const TreeModel::Path&, TreeViewColumn*) { maybe_switch_to_import_page (); }); + + _local_pset_scroller.set_policy (POLICY_AUTOMATIC, POLICY_AUTOMATIC); + _local_pset_scroller.add (_local_pset_treeview); + + _notebook.append_page (_local_pset_scroller, _("Local Presets")); + + _notebook_type[page] = RouteState; + _notebook_content[page++] = &_local_pset_treeview; + } + + templates.clear (); + Searchpath global_path (ardour_data_search_path ()); + global_path.add_subdirectory_to_paths (routestates_dir_name); + find_presets (global_path, templates); + if (templates.size () > 0) { + _global_pset_model = ListStore::create (_columns); + setup_model (_global_pset_model, templates); + + _global_pset_treeview.set_model (_global_pset_model); + _global_pset_treeview.append_column (_("Name"), _columns.name); + _global_pset_treeview.set_headers_visible (true); + + _global_pset_treeview.get_selection ()->signal_changed ().connect (sigc::bind (sigc::mem_fun (*this, &StripImportDialog::treeview_selection_changed), &_global_pset_treeview, RouteState)); + _global_pset_treeview.signal_row_activated ().connect ([&] (const TreeModel::Path&, TreeViewColumn*) { maybe_switch_to_import_page (); }); + + _global_pset_scroller.set_policy (POLICY_AUTOMATIC, POLICY_AUTOMATIC); + _global_pset_scroller.add (_global_pset_treeview); + + _notebook.append_page (_global_pset_scroller, _("Global Presets")); + + _notebook_type[page] = RouteState; + _notebook_content[page++] = &_global_pset_treeview; + } + + _notebook_connection = _notebook.signal_switch_page ().connect (sigc::mem_fun (*this, &StripImportDialog::page_changed)); + + _page_file.pack_start (_notebook); + _page_file.show_all (); + + if (!rs.empty ()) { + _notebook.set_current_page (1); + } else if (page > 1) { + _notebook.set_current_page (page - 1); + } +} + +void +StripImportDialog::page_changed (GtkNotebookPage*, guint page) +{ + switch (page) { + case 0: + file_selection_changed (); + break; + default: + treeview_selection_changed (_notebook_content.at (page), _notebook_type.at (page)); + break; + } +} + +void +StripImportDialog::setup_model (Glib::RefPtr model, vector const& templates) +{ + for (auto const& t : templates) { + TreeModel::Row row; + row = *(model->append ()); + + row[_columns.name] = t.name; + row[_columns.path] = t.path; + } +} + +void +StripImportDialog::find_presets (Searchpath const& search_path, vector& template_info) +{ + vector templates; + + /* clang-format off */ + find_paths_matching_filter (templates, + search_path, + [] (const string& str, void*) { return Glib::file_test (str, Glib::FILE_TEST_IS_DIR); }, + 0, true, true); + /* clang-format on */ + + if (templates.empty ()) { + return; + } + + for (vector::iterator i = templates.begin (); i != templates.end (); ++i) { + string file = session_template_dir_to_file (*i); + + TemplateInfo rti; + rti.name = Glib::path_get_basename (*i); + rti.path = *i; + + template_info.push_back (rti); + } + std::sort (template_info.begin (), template_info.end ()); +} + +void +StripImportDialog::file_selection_changed () +{ + parse_route_state (_chooser.get_filename ()); +} + +static string +template_dir_to_file (string const& dir, string const& suffix) +{ + return Glib::build_filename (dir, Glib::path_get_basename (dir) + suffix); +} + +void +StripImportDialog::treeview_selection_changed (Gtk::TreeView* treeview, SelectionType t) +{ + Gtk::TreeModel::const_iterator selection = treeview->get_selection ()->get_selected (); + if (selection) { + switch (t) { + case Template: + parse_route_state (template_dir_to_file ((*selection)[_columns.path], template_suffix)); + break; + case RouteState: + parse_route_state (template_dir_to_file ((*selection)[_columns.path], routestate_suffix)); + break; + case Snapshot: + parse_route_state ((*selection)[_columns.path]); + break; + } + } else { + parse_route_state (""); + } +} + +void +StripImportDialog::parse_route_state (std::string const& path) +{ + _extern_map.clear (); + _path = path; + + if (path.empty ()) { + goto out; + } + if (!Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) { + goto out; + } + + _extern_map = _session->parse_route_state (path, _match_pbd_id); + +out: + if (_extern_map.empty ()) { + _info_text.set_text (""); + _info_text.hide (); + } else { + _info_text.set_text (string_compose (P_("%1 Track", "%1 Tracks", _extern_map.size ()), _extern_map.size ())); + _info_text.show (); + } + _open_button->set_sensitive (!_extern_map.empty ()); +} + +void +StripImportDialog::maybe_switch_to_import_page () +{ + if (_extern_map.empty ()) { + return; + } + + // TODO cleanup managed items on _page_file + + /* -> next page */ + setup_strip_import_page (); + get_vbox ()->remove (_page_file); + get_vbox ()->pack_start (_page_strip); + + _info_text.hide (); + _open_button->hide (); + _ok_button->show (); +} + +/* **************************************************************************** + * Page Two map Tracks/State + */ + +void +StripImportDialog::refill_import_table () +{ + Gtk::Label* l; + Gtkmm2ext::container_clear (_strip_table, true); + + _strip_table.set_spacings (3); + + Glib::RefPtr col_size_group (SizeGroup::create (SIZE_GROUP_HORIZONTAL)); + + l = manage (new Label (string_compose ("%1", _("Local Track/Bus")))); + l->set_use_markup (); + _strip_table.attach (*l, 0, 1, 0, 1, Gtk::FILL, Gtk::SHRINK); + col_size_group->add_widget (*l); + + l = manage (new Label (string_compose ("%1", _("External State")))); + l->set_use_markup (); + _strip_table.attach (*l, 2, 3, 0, 1, Gtk::FILL, Gtk::SHRINK); + col_size_group->add_widget (*l); + + /* clang-format off */ + _strip_table.attach (*manage (new ArdourVSpacer (1.0)), 1, 2, 0, 1, SHRINK , EXPAND | FILL, 8, 4); + _strip_table.attach (*manage (new ArdourHSpacer (1.0)), 0, 4, 1, 2, EXPAND | FILL, SHRINK, 4, 8); + /* clang-format on */ + + /* Refill table */ + int r = 1; + for (auto& [rid, eid] : _import_map) { + ++r; + if (_route_map.find (rid) != _route_map.end ()) { + l = manage (new Label (_route_map[rid], 0, 0.5)); + } else { + l = manage (new Label (_("New Track"), 0, 0.5)); + l->set_use_markup (); + } + _strip_table.attach (*l, 0, 1, r, r + 1, EXPAND | FILL, SHRINK); +#if 0 + l = manage (new Label (_extern_map[eid], 1.0, 0.5)); + _strip_table.attach (*l, 2, 3, r, r + 1, EXPAND | FILL, SHRINK); +#else + using namespace Menu_Helpers; + ArdourDropdown* dd = manage (new ArdourDropdown ()); + for (auto& [eid, ename] : _extern_map) { + dd->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (ename), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::change_mapping), dd, rid, eid, ename))); + } + dd->set_text (_extern_map[eid]); + _strip_table.attach (*dd, 2, 3, r, r + 1, EXPAND | FILL, SHRINK); +#endif + + ArdourButton* rm = manage (new ArdourButton ()); + rm->set_icon (ArdourIcon::CloseCross); + rm->signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &StripImportDialog::remove_mapping), rid)); + _strip_table.attach (*rm, 3, 4, r, r + 1, Gtk::SHRINK, Gtk::SHRINK, 4, 2); + } + + if (r > 1) { + ++r; + _strip_table.attach (*manage (new ArdourVSpacer (1.0)), 1, 2, 2, r, SHRINK, EXPAND | FILL, 8, 4); + ++r; + _strip_table.attach (*manage (new ArdourHSpacer (1.0)), 0, 4, r, r + 1, EXPAND | FILL, SHRINK, 4, 8); + } + + ++r; + + /* Add options */ + using namespace Menu_Helpers; + + _add_rid = PBD::ID (0); + _add_eid = PBD::ID (0); + + int64_t next_id = std::numeric_limits::max () - 1; + PBD::ID next_new = PBD::ID (next_id); + + while (_import_map.find (next_new) != _import_map.end ()) { + --next_id; + next_new = PBD::ID (next_id); + } + + /* accumulate both dropdowns, so colums are equally spaced */ + std::vector sizing_texts; + + _add_rid_dropdown = manage (new ArdourWidgets::ArdourDropdown ()); + _add_rid_dropdown->add_menu_elem (MenuElem (_(" -- New Track -- "), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::prepare_mapping), false, next_new, _("New Track")))); + sizing_texts.push_back (_(" -- New Track -- ")); + + for (auto& [rid, rname] : _route_map) { + if (_import_map.find (rid) != _import_map.end ()) { + continue; + } + _add_rid_dropdown->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (rname), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::prepare_mapping), false, rid, rname))); + sizing_texts.push_back (rname); + } + + _add_eid_dropdown = manage (new ArdourWidgets::ArdourDropdown ()); + for (auto& [eid, ename] : _extern_map) { + _add_eid_dropdown->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (ename), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::prepare_mapping), true, eid, ename))); + sizing_texts.push_back (ename); + } + + _add_rid_dropdown->set_sizing_texts (sizing_texts); + _add_eid_dropdown->set_sizing_texts (sizing_texts); + col_size_group->add_widget (*_add_rid_dropdown); + col_size_group->add_widget (*_add_eid_dropdown); + + _add_new_mapping = manage (new ArdourButton ()); + _add_new_mapping->set_icon (ArdourIcon::PlusSign); + _add_new_mapping->signal_clicked.connect (sigc::mem_fun (*this, &StripImportDialog::add_mapping)); + + /* clang-format off */ + _strip_table.attach (*_add_rid_dropdown, 0, 1, r, r + 1, EXPAND | FILL, SHRINK); + _strip_table.attach (*_add_eid_dropdown, 2, 3, r, r + 1, EXPAND | FILL, SHRINK); + _strip_table.attach (*_add_new_mapping, 3, 4, r, r + 1, Gtk::SHRINK, SHRINK); + /* clang-format on */ + + bool can_add = !_add_rid_dropdown->items ().empty () && !_add_eid_dropdown->items ().empty (); + + _add_rid_dropdown->set_sensitive (can_add); + _add_eid_dropdown->set_sensitive (can_add); + _add_new_mapping->set_sensitive (false); + + _ok_button->set_sensitive (!_import_map.empty ()); + + _strip_table.show_all (); +} + +void +StripImportDialog::idle_refill_import_table () +{ + Glib::signal_idle ().connect ([&] () { refill_import_table (); return false; }, Glib::PRIORITY_HIGH_IDLE + 10); +} + +void +StripImportDialog::change_mapping (ArdourDropdown* dd, PBD::ID const& rid, PBD::ID const& eid, std::string const& name) +{ + dd->set_text (name); + _import_map[rid] = eid; +} + +void +StripImportDialog::prepare_mapping (bool ext, PBD::ID const& id, std::string const& name) +{ + if (ext) { + _add_eid_dropdown->set_text (name); + _add_eid = id; + } else { + _add_rid_dropdown->set_text (name); + _add_rid = id; + } + + _add_new_mapping->set_sensitive (_add_rid != PBD::ID (0) && _add_eid != PBD::ID (0)); +} + +void +StripImportDialog::add_mapping () +{ + assert (_add_rid != PBD::ID (0)); + assert (_add_eid != PBD::ID (0)); + + _default_mapping = false; + _import_map[_add_rid] = _add_eid; + + idle_refill_import_table (); +} + +void +StripImportDialog::remove_mapping (PBD::ID const& id) +{ + if (1 == _import_map.erase (id)) { + _default_mapping = false; + idle_refill_import_table (); + } +} + +void +StripImportDialog::clear_mapping () +{ + _default_mapping = false; + _import_map.clear (); + idle_refill_import_table (); +} + +void +StripImportDialog::import_all_strips () +{ + _default_mapping = false; + _import_map.clear (); + + int64_t next_id = std::numeric_limits::max () - 1 - _extern_map.size (); + for (auto& [eid, ename] : _extern_map) { + PBD::ID next_new = PBD::ID (next_id++); + _import_map[next_new] = eid; + } + + idle_refill_import_table (); +} + +void +StripImportDialog::set_default_mapping (bool and_idle_update) +{ + _import_map.clear (); + _default_mapping = true; + + if (_match_pbd_id) { + /* try a 1:1 mapping */ + for (auto& [eid, ename] : _extern_map) { + if (_route_map.find (eid) != _route_map.end ()) { + _import_map[eid] = eid; + } + } + } else { + /* match by name */ + for (auto& [eid, ename] : _extern_map) { + // TODO consider building a reverse [pointer] map + for (auto& [rid, rname] : _route_map) { + if (ename == rname) { + _import_map[rid] = eid; + break; + } + } + } + } + if (and_idle_update) { + idle_refill_import_table (); + } +} + +void +StripImportDialog::setup_strip_import_page () +{ + _route_map.clear (); + + for (auto const& r : *_session->get_routes ()) { + _route_map[r->id ()] = r->name (); + } + + set_default_mapping (false); + + refill_import_table (); + + _clear_mapping = new ArdourButton (_("Clear Mapping")); + _reset_mapping = new ArdourButton (_match_pbd_id ? _("Reset - auto-map by ID") : _("Reset - auto-map by name")); + _import_strips = new ArdourButton (_("Import all as new tracks")); + + _clear_mapping->signal_clicked.connect (mem_fun (*this, &StripImportDialog::clear_mapping)); + _import_strips->signal_clicked.connect (mem_fun (*this, &StripImportDialog::import_all_strips)); + _reset_mapping->signal_clicked.connect (sigc::bind (mem_fun (*this, &StripImportDialog::set_default_mapping), true)); + + HBox* hbox = manage (new HBox ()); + hbox->set_spacing (4); + hbox->pack_start (*_clear_mapping, true, false); + hbox->pack_start (*_import_strips, true, false); + hbox->pack_start (*_reset_mapping, true, false); + + VBox* vbox = manage (new VBox ()); + vbox->pack_start (_strip_table, false, false, 4); + + _strip_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC); + _strip_scroller.add (*vbox); + + _page_strip.set_spacing (4); + _page_strip.pack_start (_strip_scroller); + _page_strip.pack_end (*hbox, false, false, 4); + _page_strip.show_all (); + + _ok_button->set_sensitive (true); // XXX +} + +void +StripImportDialog::ok_activated () +{ + _session->import_route_state (_path, _import_map); + ArdourDialog::on_response (RESPONSE_ACCEPT); } diff --git a/gtk2_ardour/strip_import_dialog.h b/gtk2_ardour/strip_import_dialog.h index 4fdd1ed0d3..9f1d6c2725 100644 --- a/gtk2_ardour/strip_import_dialog.h +++ b/gtk2_ardour/strip_import_dialog.h @@ -17,12 +17,21 @@ */ #pragma once +#include #include #include +#include #include +#include #include +#include +#include +#include "ardour/search_paths.h" +#include "pbd/id.h" + +#include "ardour/template_utils.h" #include "ardour_dialog.h" namespace ARDOUR @@ -30,9 +39,103 @@ namespace ARDOUR class Session; } -class StripImportDialog : public ArdourDialog, public PBD::ScopedConnectionList +namespace ArdourWidgets +{ + class ArdourButton; + class ArdourDropdown; +} + +class StripImportDialog : public ArdourDialog { public: StripImportDialog (ARDOUR::Session*); ~StripImportDialog (); + +private: + enum SelectionType { + Template, + RouteState, + Snapshot + }; + + void page_changed (GtkNotebookPage*, guint); + void setup_file_page (); + void file_selection_changed (); + void treeview_selection_changed (Gtk::TreeView*, SelectionType); + void parse_route_state (std::string const&); + void setup_model (Glib::RefPtr, std::vector const&); + void find_presets (PBD::Searchpath const&, std::vector&); + + void setup_strip_import_page (); + void refill_import_table (); + void idle_refill_import_table (); + void maybe_switch_to_import_page (); + void add_mapping (); + void change_mapping (ArdourWidgets::ArdourDropdown*, PBD::ID const&, PBD::ID const&, std::string const&); + void prepare_mapping (bool, PBD::ID const&, std::string const&); + void remove_mapping (PBD::ID const&); + void clear_mapping (); + void import_all_strips (); + void set_default_mapping (bool and_idle_update); + void update_sensitivity_ok (); + void ok_activated (); + + struct SessionTemplateColumns : public Gtk::TreeModel::ColumnRecord { + SessionTemplateColumns () + { + add (name); + add (path); + } + + Gtk::TreeModelColumn name; + Gtk::TreeModelColumn path; + }; + + SessionTemplateColumns _columns; + Glib::RefPtr _recent_model; + Glib::RefPtr _template_model; + Glib::RefPtr _local_pset_model; + Glib::RefPtr _global_pset_model; + + std::map _notebook_content; + std::map _notebook_type; + + Gtk::VBox _page_file; + Gtk::VBox _page_strip; + + Gtk::Notebook _notebook; + Gtk::FileChooserWidget _chooser; + Gtk::Button* _open_button; + Gtk::Button* _ok_button; + Gtk::Label _info_text; + Gtk::ScrolledWindow _recent_scroller; + Gtk::TreeView _recent_treeview; + Gtk::ScrolledWindow _template_scroller; + Gtk::TreeView _template_treeview; + Gtk::ScrolledWindow _local_pset_scroller; + Gtk::TreeView _local_pset_treeview; + Gtk::ScrolledWindow _global_pset_scroller; + Gtk::TreeView _global_pset_treeview; + + Gtk::Table _strip_table; + Gtk::ScrolledWindow _strip_scroller; + ArdourWidgets::ArdourDropdown* _add_rid_dropdown; + ArdourWidgets::ArdourDropdown* _add_eid_dropdown; + ArdourWidgets::ArdourButton* _add_new_mapping; + ArdourWidgets::ArdourButton* _clear_mapping; + ArdourWidgets::ArdourButton* _reset_mapping; + ArdourWidgets::ArdourButton* _import_strips; + + bool _match_pbd_id; + std::string _path; + std::map _extern_map; + std::map _route_map; + std::map _import_map; + + PBD::ID _add_rid; + PBD::ID _add_eid; + bool _default_mapping; + + sigc::connection _notebook_connection; + sigc::connection _chooser_connection; };