From 892d641987b2d12f189add957d76324c57fa25d5 Mon Sep 17 00:00:00 2001 From: Ben Loftis Date: Fri, 11 Sep 2020 13:10:30 -0500 Subject: [PATCH] Playlist UI tweaks: Add (select, share, copy, steal) operations to the playlist selector (under Advanced... menu) Use the same sorting for the menu list and playlist_selector items Implement Copy, Share and Steal playlist actions. Need a RouteTimeAxis for this. Rename persistent dialog buttons to make it clear you can Revert --- gtk2_ardour/playlist_selector.cc | 171 +++++++++++++++++++------------ gtk2_ardour/playlist_selector.h | 22 +++- gtk2_ardour/route_time_axis.cc | 9 -- gtk2_ardour/route_ui.cc | 56 ++++++++-- gtk2_ardour/route_ui.h | 9 +- 5 files changed, 180 insertions(+), 87 deletions(-) diff --git a/gtk2_ardour/playlist_selector.cc b/gtk2_ardour/playlist_selector.cc index 8eb4cb8ca8..9cfdaac8b6 100644 --- a/gtk2_ardour/playlist_selector.cc +++ b/gtk2_ardour/playlist_selector.cc @@ -25,11 +25,13 @@ #include "ardour/audio_track.h" #include "ardour/audioplaylist.h" #include "ardour/midi_playlist.h" +#include "ardour/playlist_factory.h" #include "ardour/session_playlist.h" #include +#include "public_editor.h" #include "playlist_selector.h" #include "route_ui.h" #include "gui_thread.h" @@ -45,7 +47,8 @@ using namespace PBD; PlaylistSelector::PlaylistSelector () : ArdourDialog (_("Playlists")) { - rui = 0; + _tav = 0; + _mode = plSelect; set_name ("PlaylistSelectorWindow"); set_modal(false); @@ -55,6 +58,7 @@ PlaylistSelector::PlaylistSelector () model = TreeStore::create (columns); tree.set_model (model); tree.append_column (_("Playlists grouped by track"), columns.text); + tree.set_headers_visible (false); scroller.add (tree); scroller.set_policy (POLICY_AUTOMATIC, POLICY_AUTOMATIC); @@ -64,21 +68,25 @@ PlaylistSelector::PlaylistSelector () get_vbox()->pack_start (scroller); - Button* close_btn = add_button (_("Close"), RESPONSE_CANCEL); - Button* ok_btn = add_button (_("OK"), RESPONSE_OK); + get_vbox()->show_all(); + + Button* close_btn = add_button (_("Revert"), RESPONSE_CANCEL); + Button* ok_btn = add_button (_("Done"), RESPONSE_OK); close_btn->signal_clicked().connect (sigc::mem_fun(*this, &PlaylistSelector::close_button_click)); ok_btn->signal_clicked().connect (sigc::mem_fun(*this, &PlaylistSelector::ok_button_click)); } -void PlaylistSelector::set_rui(RouteUI* ruix) +void PlaylistSelector::set_tav(RouteTimeAxisView* tavx, plMode mode) { - if (rui == ruix) { + _mode = mode; + + if (_tav == tavx) { return; } - rui = ruix; + _tav = tavx; - boost::shared_ptr this_track = rui->track(); + boost::shared_ptr this_track = _tav->track(); if (this_track) { this_track->PlaylistAdded.connect( @@ -109,6 +117,9 @@ PlaylistSelector::clear_map () delete x->second; } trpl_map.clear (); + if (current_playlist) { + current_playlist.reset(); + } } bool @@ -124,10 +135,14 @@ PlaylistSelector::on_unmap_event (GdkEventAny* ev) void PlaylistSelector::redisplay() { + if (!_tav ) { + return; + } + vector item; string str; - set_title (string_compose (_("Playlist for %1"), rui->route()->name())); + set_title (string_compose (_("Playlist for %1"), _tav->route()->name())); clear_map (); select_connection.disconnect (); @@ -136,13 +151,9 @@ PlaylistSelector::redisplay() _session->playlists()->foreach (this, &PlaylistSelector::add_playlist_to_map); - boost::shared_ptr this_track = rui->track(); + boost::shared_ptr this_track = _tav->track(); - TreeModel::Row others = *(model->append ()); - - others[columns.text] = _("Other tracks"); - boost::shared_ptr proxy = others[columns.playlist]; - proxy.reset (); + boost::shared_ptr proxy; if (this_track->playlist()) { current_playlist = this_track->playlist(); @@ -167,72 +178,82 @@ PlaylistSelector::redisplay() bool have_selected = false; TreePath this_path; - if (tr == this_track) { + //make a heading for each other track, if needed + if (tr != this_track && _mode != plSelect) { row = *(model->prepend()); row[columns.text] = nodename; boost::shared_ptr proxy = row[columns.playlist]; proxy.reset (); - } else { - row = *(model->append (others.children())); - row[columns.text] = nodename; - boost::shared_ptr proxy = row[columns.playlist]; - proxy.reset (); } /* Now insert all the playlists for this diskstream/track in a subtree */ + vector > pls = *(x->second); - list >* pls = x->second; + /* sort the playlists to match the order they appear in the track menu */ + PlaylistSorterByID cmp; + sort (pls.begin(), pls.end(), cmp); - for (list >::iterator p = pls->begin(); p != pls->end(); ++p) { + for (vector >::iterator p = pls.begin(); p != pls.end(); ++p) { TreeModel::Row child_row; - child_row = *(model->append (row.children())); - child_row[columns.text] = (*p)->name(); - child_row[columns.playlist] = *p; + if (tr == this_track && _mode==plSelect) { + child_row = *(model->append()); + } else if (tr != this_track && _mode != plSelect) { + child_row = *(model->append (row.children())); + } + + if (child_row) { + child_row[columns.text] = (*p)->name(); + child_row[columns.playlist] = *p; - if (*p == this_track->playlist()) { - selected_row = child_row; - have_selected = true; + if (*p == this_track->playlist()) { + selected_row = child_row; + have_selected = true; + } } } - if (have_selected) { tree.get_selection()->select (selected_row); } } - // Add unassigned (imported) playlists to the list - list > unassigned; - _session->playlists()->unassigned (unassigned); + if (_mode != plSelect) { + // Add unassigned (imported) playlists to the list + list > unassigned; + _session->playlists()->unassigned (unassigned); - TreeModel::Row row; - TreeModel::Row selected_row; - bool have_selected = false; - TreePath this_path; + if ( unassigned.begin() != unassigned.end() ) { + + TreeModel::Row row; + TreeModel::Row selected_row; + bool have_selected = false; + TreePath this_path; - row = *(model->append (others.children())); - row[columns.text] = _("Imported"); - proxy = row[columns.playlist]; - proxy.reset (); + row = *(model->append ()); + row[columns.text] = _("Imported"); + proxy = row[columns.playlist]; + proxy.reset (); - for (list >::iterator p = unassigned.begin(); p != unassigned.end(); ++p) { - TreeModel::Row child_row; + for (list >::iterator p = unassigned.begin(); p != unassigned.end(); ++p) { + TreeModel::Row child_row; - child_row = *(model->append (row.children())); - child_row[columns.text] = (*p)->name(); - child_row[columns.playlist] = *p; + child_row = *(model->append (row.children())); + child_row[columns.text] = (*p)->name(); + child_row[columns.playlist] = *p; - if (*p == this_track->playlist()) { - selected_row = child_row; - have_selected = true; + if (*p == this_track->playlist()) { + selected_row = child_row; + have_selected = true; + } + + if (have_selected) { + tree.get_selection()->select (selected_row); + } + } } - - if (have_selected) { - tree.get_selection()->select (selected_row); - } - } - + } //if !plSelect + show_all (); select_connection = tree.get_selection()->signal_changed().connect (sigc::mem_fun(*this, &PlaylistSelector::selection_changed)); } @@ -244,16 +265,16 @@ PlaylistSelector::add_playlist_to_map (boost::shared_ptr pl) return; } - if (!rui) { + if (!_tav) { return; } - if (rui->is_midi_track ()) { + if (_tav->is_midi_track ()) { if (boost::dynamic_pointer_cast (pl) == 0) { return; } } else { - assert (rui->is_audio_track ()); + assert (_tav->is_audio_track ()); if (boost::dynamic_pointer_cast (pl) == 0) { return; } @@ -262,7 +283,7 @@ PlaylistSelector::add_playlist_to_map (boost::shared_ptr pl) TrackPlaylistMap::iterator x; if ((x = trpl_map.find (pl->get_orig_track_id ())) == trpl_map.end()) { - x = trpl_map.insert (trpl_map.end(), make_pair (pl->get_orig_track_id(), new list >)); + x = trpl_map.insert (trpl_map.end(), make_pair (pl->get_orig_track_id(), new vector >)); } x->second->push_back (pl); @@ -277,17 +298,19 @@ PlaylistSelector::playlist_added() void PlaylistSelector::close_button_click () { - if (rui && current_playlist) { - rui->track ()->use_playlist (rui->is_audio_track () ? DataType::AUDIO : DataType::MIDI, current_playlist); + if (_tav && current_playlist) { + _tav->track ()->use_playlist (_tav->is_audio_track () ? DataType::AUDIO : DataType::MIDI, current_playlist); } - rui = 0; + _tav = 0; + clear_map (); hide (); } void PlaylistSelector::ok_button_click() { - rui = 0; + _tav = 0; + clear_map (); hide(); } @@ -304,20 +327,36 @@ PlaylistSelector::selection_changed () TreeModel::iterator iter = tree.get_selection()->get_selected(); - if (!iter || rui == 0) { + if (!iter || _tav == 0) { /* nothing selected */ return; } if ((pl = ((*iter)[columns.playlist])) != 0) { - if (rui->is_audio_track () && boost::dynamic_pointer_cast (pl) == 0) { + if (_tav->is_audio_track () && boost::dynamic_pointer_cast (pl) == 0) { return; } - if (rui->is_midi_track () && boost::dynamic_pointer_cast (pl) == 0) { + if (_tav->is_midi_track () && boost::dynamic_pointer_cast (pl) == 0) { return; } - rui->track ()->use_playlist (rui->is_audio_track () ? DataType::AUDIO : DataType::MIDI, pl); + switch (_mode) { + /* @Robin: I dont see a way to undo these playlist actions */ + case plCopy: { + boost::shared_ptr playlist = PlaylistFactory::create (pl, string_compose ("%1.1", pl->name())); + /* playlist->reset_shares (); @Robin is this needed? */ + _tav->track ()->use_playlist (_tav->is_audio_track () ? DataType::AUDIO : DataType::MIDI, playlist); + } break; + case plShare: + _tav->track ()->use_playlist (_tav->is_audio_track () ? DataType::AUDIO : DataType::MIDI, pl, false); /* share pl but do NOT set me as the owner */ + break; + case plSteal: + _tav->track ()->use_playlist (_tav->is_audio_track () ? DataType::AUDIO : DataType::MIDI, pl); /* share the playlist and set ME as the owner */ + break; + case plSelect: + _tav->use_playlist (NULL, pl); //call route_ui::use_playlist because it is group-aware + break; + } } } diff --git a/gtk2_ardour/playlist_selector.h b/gtk2_ardour/playlist_selector.h index 344996b3ed..3b7b4cd9f3 100644 --- a/gtk2_ardour/playlist_selector.h +++ b/gtk2_ardour/playlist_selector.h @@ -41,24 +41,40 @@ namespace ARDOUR { class RouteUI; +struct PlaylistSorterByID { + bool operator() (boost::shared_ptr a, boost::shared_ptr b) const { + return a->sort_id() < b->sort_id(); + } +}; + class PlaylistSelector : public ArdourDialog { public: PlaylistSelector (); ~PlaylistSelector (); + enum plMode { + plSelect, + plCopy, + plShare, + plSteal + }; + void redisplay(); - void set_rui(RouteUI*); + void set_tav(RouteTimeAxisView*, plMode in); protected: bool on_unmap_event (GdkEventAny*); private: - typedef std::map >*> TrackPlaylistMap; + typedef std::map >*> TrackPlaylistMap; Gtk::ScrolledWindow scroller; TrackPlaylistMap trpl_map; - RouteUI* rui; + + RouteTimeAxisView* _tav; + + plMode _mode; sigc::connection select_connection; PBD::ScopedConnectionList signal_connections; diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index 123ffda131..f833d9dd02 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -1380,7 +1380,6 @@ RouteTimeAxisView::paste (samplepos_t pos, const Selection& selection, PasteCont return true; } - void RouteTimeAxisView::update_playlist_tip () { @@ -1411,14 +1410,6 @@ RouteTimeAxisView::update_playlist_tip () set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name())); } - -void -RouteTimeAxisView::show_playlist_selector () -{ - _editor.playlist_selector().set_rui(this); - _editor.playlist_selector().redisplay(); -} - void RouteTimeAxisView::map_frozen () { diff --git a/gtk2_ardour/route_ui.cc b/gtk2_ardour/route_ui.cc index d7a952c406..0ace28a964 100644 --- a/gtk2_ardour/route_ui.cc +++ b/gtk2_ardour/route_ui.cc @@ -1811,7 +1811,7 @@ void RouteUI::set_route_active (bool a, bool apply_to_selection) { if (apply_to_selection) { - ARDOUR_UI::instance()->the_editor().get_selection().tracks.foreach_route_ui (boost::bind (&RouteTimeAxisView::set_route_active, _1, a, false)); + ARDOUR_UI::instance()->the_editor().get_selection().tracks.foreach_route_ui (boost::bind (&RouteUI::set_route_active, _1, a, false)); } else { _route->set_active (a, this); } @@ -2552,7 +2552,7 @@ RouteUI::build_playlist_menu () vector > playlists_tr = _session->playlists()->playlists_for_track (tr); /* sort the playlists */ - PlaylistSorter cmp; + PlaylistSorterByID cmp; sort (playlists_tr.begin(), playlists_tr.end(), cmp); /* add the playlists to the menu */ @@ -2566,6 +2566,9 @@ RouteUI::build_playlist_menu () } } + playlist_items.push_back (SeparatorElem()); + playlist_items.push_back (MenuElem(_("Select ..."), sigc::mem_fun(*this, &RouteUI::show_playlist_selector))); + playlist_items.push_back (SeparatorElem()); playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::rename_current_playlist))); playlist_items.push_back (SeparatorElem()); @@ -2576,8 +2579,8 @@ RouteUI::build_playlist_menu () } else { // Use a label which tells the user what is happening - playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(editor, &PublicEditor::new_playlists), this))); - playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(editor, &PublicEditor::copy_playlists), this))); + playlist_items.push_back (MenuElem (_("New Group Playlist"), sigc::bind(sigc::mem_fun(editor, &PublicEditor::new_playlists), this))); + playlist_items.push_back (MenuElem (_("Copy Group Playlist"), sigc::bind(sigc::mem_fun(editor, &PublicEditor::copy_playlists), this))); } @@ -2585,7 +2588,12 @@ RouteUI::build_playlist_menu () playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(editor, &PublicEditor::clear_playlists), this))); playlist_items.push_back (SeparatorElem()); - playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteUI::show_playlist_selector))); + Menu* advanced_menu = manage (new Menu); + MenuList& advanced_items = advanced_menu->items(); + advanced_items.push_back (MenuElem(_("Copy from ..."), sigc::mem_fun(*this, &RouteUI::show_playlist_copy_selector))); + advanced_items.push_back (MenuElem(_("Share with ..."), sigc::mem_fun(*this, &RouteUI::show_playlist_share_selector))); + advanced_items.push_back (MenuElem(_("Steal from ..."), sigc::mem_fun(*this, &RouteUI::show_playlist_steal_selector))); + playlist_items.push_back (MenuElem (_("Advanced"), *advanced_menu)); } void @@ -2594,7 +2602,7 @@ RouteUI::use_playlist (RadioMenuItem *item, boost::weak_ptr wpl) assert (is_track()); // exit if we were triggered by deactivating the old playlist - if (!item->get_active()) { + if (item && !item->get_active()) { return; } @@ -2659,7 +2667,41 @@ RouteUI::use_playlist (RadioMenuItem *item, boost::weak_ptr wpl) void RouteUI::show_playlist_selector () { - ARDOUR_UI::instance()->the_editor().playlist_selector().show_for (this); + RouteTimeAxisView* rtv = dynamic_cast (this); + if (rtv) { + ARDOUR_UI::instance()->the_editor().playlist_selector().set_tav(rtv, PlaylistSelector::plSelect); + ARDOUR_UI::instance()->the_editor().playlist_selector().redisplay (); + } +} + +void +RouteUI::show_playlist_copy_selector () +{ + RouteTimeAxisView* rtv = dynamic_cast (this); + if (rtv) { + ARDOUR_UI::instance()->the_editor().playlist_selector().set_tav(rtv, PlaylistSelector::plCopy); + ARDOUR_UI::instance()->the_editor().playlist_selector().redisplay (); + } +} + +void +RouteUI::show_playlist_share_selector () +{ + RouteTimeAxisView* rtv = dynamic_cast (this); + if (rtv) { + ARDOUR_UI::instance()->the_editor().playlist_selector().set_tav(rtv, PlaylistSelector::plShare); + ARDOUR_UI::instance()->the_editor().playlist_selector().redisplay (); + } +} + +void +RouteUI::show_playlist_steal_selector () +{ + RouteTimeAxisView* rtv = dynamic_cast (this); + if (rtv) { + ARDOUR_UI::instance()->the_editor().playlist_selector().set_tav(rtv, PlaylistSelector::plSteal); + ARDOUR_UI::instance()->the_editor().playlist_selector().redisplay (); + } } void diff --git a/gtk2_ardour/route_ui.h b/gtk2_ardour/route_ui.h index 3ceb3c0324..fc2776f52e 100644 --- a/gtk2_ardour/route_ui.h +++ b/gtk2_ardour/route_ui.h @@ -161,6 +161,8 @@ public: void use_new_playlist (bool prompt, std::vector > const&, bool copy); void clear_playlist (); + void use_playlist (Gtk::RadioMenuItem* item, boost::weak_ptr wpl); + /* used by EditorRoutes */ static Gtkmm2ext::ActiveState solo_active_state (boost::shared_ptr); static Gtkmm2ext::ActiveState solo_isolate_active_state (boost::shared_ptr); @@ -239,9 +241,13 @@ protected: std::string playlist_tip () const; void build_playlist_menu (); - void use_playlist (Gtk::RadioMenuItem* item, boost::weak_ptr wpl); Gtk::Menu* playlist_action_menu; + void show_playlist_selector (); + void show_playlist_copy_selector (); + void show_playlist_share_selector (); + void show_playlist_steal_selector (); + Gtk::CheckMenuItem* denormal_menu_item; static void set_showing_sends_to (boost::shared_ptr); @@ -285,7 +291,6 @@ private: void set_sends_gain_to_zero (); void set_sends_gain_to_unity (); - void show_playlist_selector (); void rename_current_playlist (); void parameter_changed (std::string const&);