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
This commit is contained in:
Ben Loftis 2020-09-11 13:10:30 -05:00
parent a51e51b4f2
commit 892d641987
5 changed files with 180 additions and 87 deletions

View File

@ -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 <gtkmm2ext/gtk_ui.h>
#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<Track> this_track = rui->track();
boost::shared_ptr<Track> 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<const char*> 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<Track> this_track = rui->track();
boost::shared_ptr<Track> this_track = _tav->track();
TreeModel::Row others = *(model->append ());
others[columns.text] = _("Other tracks");
boost::shared_ptr<Playlist> proxy = others[columns.playlist];
proxy.reset ();
boost::shared_ptr<Playlist> 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<Playlist> proxy = row[columns.playlist];
proxy.reset ();
} else {
row = *(model->append (others.children()));
row[columns.text] = nodename;
boost::shared_ptr<Playlist> proxy = row[columns.playlist];
proxy.reset ();
}
/* Now insert all the playlists for this diskstream/track in a subtree */
vector<boost::shared_ptr<Playlist> > pls = *(x->second);
list<boost::shared_ptr<Playlist> >* 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<boost::shared_ptr<Playlist> >::iterator p = pls->begin(); p != pls->end(); ++p) {
for (vector<boost::shared_ptr<Playlist> >::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<boost::shared_ptr<Playlist> > unassigned;
_session->playlists()->unassigned (unassigned);
if (_mode != plSelect) {
// Add unassigned (imported) playlists to the list
list<boost::shared_ptr<Playlist> > 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<boost::shared_ptr<Playlist> >::iterator p = unassigned.begin(); p != unassigned.end(); ++p) {
TreeModel::Row child_row;
for (list<boost::shared_ptr<Playlist> >::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<Playlist> pl)
return;
}
if (!rui) {
if (!_tav) {
return;
}
if (rui->is_midi_track ()) {
if (_tav->is_midi_track ()) {
if (boost::dynamic_pointer_cast<MidiPlaylist> (pl) == 0) {
return;
}
} else {
assert (rui->is_audio_track ());
assert (_tav->is_audio_track ());
if (boost::dynamic_pointer_cast<AudioPlaylist> (pl) == 0) {
return;
}
@ -262,7 +283,7 @@ PlaylistSelector::add_playlist_to_map (boost::shared_ptr<Playlist> 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<boost::shared_ptr<Playlist> >));
x = trpl_map.insert (trpl_map.end(), make_pair (pl->get_orig_track_id(), new vector<boost::shared_ptr<Playlist> >));
}
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<AudioPlaylist> (pl) == 0) {
if (_tav->is_audio_track () && boost::dynamic_pointer_cast<AudioPlaylist> (pl) == 0) {
return;
}
if (rui->is_midi_track () && boost::dynamic_pointer_cast<MidiPlaylist> (pl) == 0) {
if (_tav->is_midi_track () && boost::dynamic_pointer_cast<MidiPlaylist> (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> 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;
}
}
}

View File

@ -41,24 +41,40 @@ namespace ARDOUR {
class RouteUI;
struct PlaylistSorterByID {
bool operator() (boost::shared_ptr<ARDOUR::Playlist> a, boost::shared_ptr<ARDOUR::Playlist> 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<PBD::ID,std::list<boost::shared_ptr<ARDOUR::Playlist> >*> TrackPlaylistMap;
typedef std::map<PBD::ID,std::vector<boost::shared_ptr<ARDOUR::Playlist> >*> TrackPlaylistMap;
Gtk::ScrolledWindow scroller;
TrackPlaylistMap trpl_map;
RouteUI* rui;
RouteTimeAxisView* _tav;
plMode _mode;
sigc::connection select_connection;
PBD::ScopedConnectionList signal_connections;

View File

@ -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 ()
{

View File

@ -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<boost::shared_ptr<Playlist> > 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<Playlist> 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<Playlist> wpl)
void
RouteUI::show_playlist_selector ()
{
ARDOUR_UI::instance()->the_editor().playlist_selector().show_for (this);
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (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<RouteTimeAxisView*> (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<RouteTimeAxisView*> (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<RouteTimeAxisView*> (this);
if (rtv) {
ARDOUR_UI::instance()->the_editor().playlist_selector().set_tav(rtv, PlaylistSelector::plSteal);
ARDOUR_UI::instance()->the_editor().playlist_selector().redisplay ();
}
}
void

View File

@ -161,6 +161,8 @@ public:
void use_new_playlist (bool prompt, std::vector<boost::shared_ptr<ARDOUR::Playlist> > const&, bool copy);
void clear_playlist ();
void use_playlist (Gtk::RadioMenuItem* item, boost::weak_ptr<ARDOUR::Playlist> wpl);
/* used by EditorRoutes */
static Gtkmm2ext::ActiveState solo_active_state (boost::shared_ptr<ARDOUR::Stripable>);
static Gtkmm2ext::ActiveState solo_isolate_active_state (boost::shared_ptr<ARDOUR::Stripable>);
@ -239,9 +241,13 @@ protected:
std::string playlist_tip () const;
void build_playlist_menu ();
void use_playlist (Gtk::RadioMenuItem* item, boost::weak_ptr<ARDOUR::Playlist> 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<ARDOUR::Route>);
@ -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&);