#include "ardour/audio_track.h"
#include "ardour/midi_track.h"
#include "ardour/route.h"
+#include "ardour/selection.h"
#include "ardour/session.h"
#include "ardour/solo_isolate_control.h"
#include "ardour/utils.h"
#include "gtkmm2ext/cell_renderer_pixbuf_toggle.h"
#include "gtkmm2ext/treeutils.h"
+#include "widgets/tooltips.h"
+
#include "actions.h"
#include "ardour_ui.h"
#include "audio_time_axis.h"
#include "mixer_strip.h"
#include "plugin_setup_dialog.h"
#include "route_sorter.h"
-#include "tooltips.h"
#include "vca_time_axis.h"
#include "utils.h"
using namespace std;
using namespace ARDOUR;
+using namespace ArdourWidgets;
using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtk;
, _queue_tv_update (0)
, _menu (0)
, old_focus (0)
- , selection_countdown (0)
, name_editable (0)
{
static const int column_width = 22;
TreeViewColumn* solo_isolate_state_column = manage (new TreeViewColumn("SI", *solo_iso_renderer));
solo_isolate_state_column->add_attribute(solo_iso_renderer->property_state(), _columns.solo_isolate_state);
- solo_isolate_state_column->add_attribute(solo_iso_renderer->property_visible(), _columns.solo_visible);
+ solo_isolate_state_column->add_attribute(solo_iso_renderer->property_visible(), _columns.solo_lock_iso_visible);
solo_isolate_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
solo_isolate_state_column->set_alignment(ALIGN_CENTER);
solo_isolate_state_column->set_expand(false);
TreeViewColumn* solo_safe_state_column = manage (new TreeViewColumn(_("SS"), *solo_safe_renderer));
solo_safe_state_column->add_attribute(solo_safe_renderer->property_state(), _columns.solo_safe_state);
- solo_safe_state_column->add_attribute(solo_safe_renderer->property_visible(), _columns.solo_visible);
+ solo_safe_state_column->add_attribute(solo_safe_renderer->property_visible(), _columns.solo_lock_iso_visible);
solo_safe_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
solo_safe_state_column->set_alignment(ALIGN_CENTER);
solo_safe_state_column->set_expand(false);
_display.set_name (X_("EditGroupList"));
_display.set_rules_hint (true);
_display.set_size_request (100, -1);
- _display.add_object_drag (_columns.stripable.index(), "routes");
CellRendererText* name_cell = dynamic_cast<CellRendererText*> (_display.get_column_cell_renderer (_name_column));
active_col->set_sizing (TREE_VIEW_COLUMN_FIXED);
active_col->set_fixed_width (30);
active_col->set_alignment (ALIGN_CENTER);
+ active_col->add_attribute (active_cell->property_visible(), _columns.no_vca);
_model->signal_row_deleted().connect (sigc::mem_fun (*this, &EditorRoutes::row_deleted));
_model->signal_rows_reordered().connect (sigc::mem_fun (*this, &EditorRoutes::reordered));
_display.set_enable_search (false);
Route::PluginSetup.connect_same_thread (*this, boost::bind (&EditorRoutes::plugin_setup, this, _1, _2, _3));
- PresentationInfo::Change.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::presentation_info_changed, this, _1), gui_context());
}
bool
/* arm counter so that ::selection_filter() will deny selecting anything for the
* next two attempts to change selection status.
*/
- selection_countdown = 2;
_scroller.grab_focus ();
Keyboard::magic_widget_grab_focus ();
return false;
bool
EditorRoutes::leave_notify (GdkEventCrossing*)
{
- selection_countdown = 0;
-
if (old_focus) {
old_focus->grab_focus ();
old_focus = 0;
Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
TimeAxisView* tv = row[_columns.tv];
- RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
+ StripableTimeAxisView* stv = dynamic_cast<StripableTimeAxisView*> (tv);
- if (!rtv) {
+ if (!stv || !stv->stripable()) {
return;
}
- boost::shared_ptr<AutomationControl> ac = rtv->route()->rec_enable_control();
+ boost::shared_ptr<AutomationControl> ac = stv->stripable()->rec_enable_control();
if (ac) {
ac->set_value (!ac->get_value(), Controllable::UseGroup);
{
Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
TimeAxisView* tv = row[_columns.tv];
- RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
+ StripableTimeAxisView* stv = dynamic_cast<StripableTimeAxisView*> (tv);
- if (!rtv) {
+ if (!stv || !stv->stripable()) {
return;
}
- boost::shared_ptr<AutomationControl> ac (rtv->route()->rec_safe_control());
+ boost::shared_ptr<AutomationControl> ac (stv->stripable()->rec_safe_control());
if (ac) {
ac->set_value (!ac->get_value(), Controllable::UseGroup);
Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
TimeAxisView *tv = row[_columns.tv];
- RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
+ StripableTimeAxisView* stv = dynamic_cast<StripableTimeAxisView*> (tv);
- if (!rtv) {
+ if (!stv || !stv->stripable()) {
return;
}
- boost::shared_ptr<AutomationControl> ac (rtv->route()->mute_control());
+ boost::shared_ptr<AutomationControl> ac (stv->stripable()->mute_control());
if (ac) {
ac->set_value (!ac->get_value(), Controllable::UseGroup);
Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
TimeAxisView *tv = row[_columns.tv];
- RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
+ StripableTimeAxisView* stv = dynamic_cast<StripableTimeAxisView*> (tv);
- if (!rtv) {
+ if (!stv || !stv->stripable()) {
return;
}
- boost::shared_ptr<AutomationControl> ac (rtv->route()->solo_control());
+ boost::shared_ptr<AutomationControl> ac (stv->stripable()->solo_control());
if (ac) {
ac->set_value (!ac->get_value(), Controllable::UseGroup);
Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
TimeAxisView *tv = row[_columns.tv];
- RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
+ StripableTimeAxisView* stv = dynamic_cast<StripableTimeAxisView*> (tv);
- if (!rtv) {
+ if (!stv || !stv->stripable()) {
return;
}
- boost::shared_ptr<AutomationControl> ac (rtv->route()->solo_isolate_control());
+ boost::shared_ptr<AutomationControl> ac (stv->stripable()->solo_isolate_control());
if (ac) {
ac->set_value (!ac->get_value(), Controllable::UseGroup);
Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
TimeAxisView *tv = row[_columns.tv];
- RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
+ StripableTimeAxisView* stv = dynamic_cast<StripableTimeAxisView*> (tv);
- if (!rtv) {
+ if (!stv || !stv->stripable()) {
return;
}
- boost::shared_ptr<AutomationControl> ac (rtv->route()->solo_safe_control());
+ boost::shared_ptr<AutomationControl> ac (stv->stripable()->solo_safe_control());
if (ac) {
ac->set_value (!ac->get_value(), Controllable::UseGroup);
items.push_back (MenuElem (_("Only Show Tracks with Regions Under Playhead"), sigc::mem_fun (*this, &EditorRoutes::show_tracks_with_regions_at_playhead)));
}
-void
-EditorRoutes::show_menu ()
-{
- if (_menu == 0) {
- build_menu ();
- }
-
- _menu->popup (1, gtk_get_current_event_time());
-}
-
void
EditorRoutes::redisplay_real ()
{
}
}
- _display.set_model (Glib::RefPtr<ListStore>());
+ {
+ PBD::Unwinder<bool> uw (_ignore_selection_change, true);
+ _display.set_model (Glib::RefPtr<ListStore>());
+ }
for (list<TimeAxisView*>::iterator x = tavs.begin(); x != tavs.end(); ++x) {
row[_columns.is_track] = false;
row[_columns.is_input_active] = false;
row[_columns.is_midi] = false;
+ row[_columns.no_vca] = false;
} else if (rtav) {
midi_trk= boost::dynamic_pointer_cast<MidiTrack> (stripable);
row[_columns.is_track] = (boost::dynamic_pointer_cast<Track> (stripable) != 0);
-
+ row[_columns.no_vca] = true;
if (midi_trk) {
row[_columns.is_input_active] = midi_trk->input_active ();
row[_columns.stripable] = stripable;
row[_columns.mute_state] = RouteUI::mute_active_state (_session, stripable);
row[_columns.solo_state] = RouteUI::solo_active_state (stripable);
- row[_columns.solo_visible] = true;
+ row[_columns.solo_visible] = !stripable->is_master ();
+ row[_columns.solo_lock_iso_visible] = row[_columns.solo_visible] && row[_columns.no_vca];
row[_columns.solo_isolate_state] = RouteUI::solo_isolate_active_state (stripable);
row[_columns.solo_safe_state] = RouteUI::solo_safe_active_state (stripable);
row[_columns.name_editable] = true;
update_input_active_display ();
update_active_display ();
- _display.set_model (_model);
+ {
+ PBD::Unwinder<bool> uw (_ignore_selection_change, true);
+ _display.set_model (_model);
+ }
/* now update route order keys from the treeview/track display order */
TreeModel::Children rows = _model->children();
TreeModel::Children::iterator ri;
+ PBD::Unwinder<bool> uw (_ignore_selection_change, true);
+
for (ri = rows.begin(); ri != rows.end(); ++ri) {
if ((*ri)[_columns.tv] == tv) {
PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
break;
}
}
-
- /* the deleted signal for the treeview/model will take
- care of any updates.
- */
}
void
(*i)[_columns.visible] = tv->marked_for_display ();
}
- /* force route order keys catch up with visibility changes
- */
+ /* force route order keys catch up with visibility changes */
sync_presentation_info_from_treeview ();
}
DEBUG_TRACE (DEBUG::OrderKeys, "editor sync presentation info from treeview\n");
- TreeModel::Children::iterator ri;
bool change = false;
PresentationInfo::order_t order = 0;
- bool master_is_first = false;
- uint32_t count = 0;
-
- OrderingKeys sorted;
- const size_t cmp_max = rows.size ();
PresentationInfo::ChangeSuspender cs;
- // special case master if it's got PI order 0 lets keep it there
- if (_session->master_out() && (_session->master_out()->presentation_info().order() == 0)) {
- order++;
- master_is_first = true;
- }
-
- for (ri = rows.begin(); ri != rows.end(); ++ri) {
-
+ for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri) {
boost::shared_ptr<Stripable> stripable = (*ri)[_columns.stripable];
bool visible = (*ri)[_columns.visible];
- /* Monitor and Auditioner do not get their presentation
- * info reset here.
- */
-
+#ifndef NDEBUG // these should not exist in the treeview
+ assert (stripable);
if (stripable->is_monitor() || stripable->is_auditioner()) {
+ assert (0);
continue;
}
+#endif
stripable->presentation_info().set_hidden (!visible);
- /* special case master if it's got PI order 0 lets keep it there
- * but still allow master to move if first non-master route has
- * presentation order 1
- */
- if ((count == 0) && master_is_first && (stripable->presentation_info().order() == 1)) {
- master_is_first = false; // someone has moved master
- order = 0;
- }
-
- if (stripable->is_master() && master_is_first) {
- if (count) {
- continue;
- } else {
- count++;
- continue;
- }
- }
-
if (order != stripable->presentation_info().order()) {
stripable->set_presentation_order (order);
change = true;
}
-
- sorted.push_back (OrderKeys (order, stripable, cmp_max));
-
++order;
- ++count;
- }
-
- if (!change) {
- // VCA (and Mixbus) special cases according to SortByNewDisplayOrder
- uint32_t n = 0;
- SortByNewDisplayOrder cmp;
- sort (sorted.begin(), sorted.end(), cmp);
- for (OrderingKeys::iterator sr = sorted.begin(); sr != sorted.end(); ++sr, ++n) {
- if (sr->old_display_order != n) {
- change = true;
- }
- }
- if (change) {
- n = 0;
- for (OrderingKeys::iterator sr = sorted.begin(); sr != sorted.end(); ++sr, ++n) {
- if (sr->stripable->presentation_info().order() != n) {
- sr->stripable->set_presentation_order (n);
- }
- }
- }
}
-}
-void
-EditorRoutes::presentation_info_changed (PropertyChange const & what_changed)
-{
- PropertyChange soh;
- soh.add (Properties::selected);
- soh.add (Properties::order);
- soh.add (Properties::hidden);
+ change |= _session->ensure_stripable_sort_order ();
- if (what_changed.contains (soh)) {
- sync_treeview_from_presentation_info (what_changed);
+ if (change) {
+ _session->set_dirty();
}
}
TreeModel::Children rows = _model->children();
- if (what_changed.contains (hidden_or_order)) {
+ bool changed = false;
+ if (what_changed.contains (hidden_or_order)) {
vector<int> neworder;
uint32_t old_order = 0;
- bool changed = false;
if (rows.empty()) {
return;
}
- OrderingKeys sorted;
- const size_t cmp_max = rows.size ();
-
+ TreeOrderKeys sorted;
for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
boost::shared_ptr<Stripable> stripable = (*ri)[_columns.stripable];
/* use global order */
- sorted.push_back (OrderKeys (old_order, stripable, cmp_max));
+ sorted.push_back (TreeOrderKey (old_order, stripable));
}
- SortByNewDisplayOrder cmp;
+ TreeOrderKeySorter cmp;
sort (sorted.begin(), sorted.end(), cmp);
neworder.assign (sorted.size(), 0);
uint32_t n = 0;
- for (OrderingKeys::iterator sr = sorted.begin(); sr != sorted.end(); ++sr, ++n) {
+ for (TreeOrderKeys::iterator sr = sorted.begin(); sr != sorted.end(); ++sr, ++n) {
neworder[n] = sr->old_display_order;
}
}
- if (what_changed.contains (Properties::selected)) {
-
- TrackViewList tvl;
+ if (changed || what_changed.contains (Properties::selected)) {
+ /* by the time this is invoked, the GUI Selection model has
+ * already updated itself.
+ */
PBD::Unwinder<bool> uw (_ignore_selection_change, true);
- /* step one: set the treeview model selection state */
+ /* set the treeview model selection state */
for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri) {
boost::shared_ptr<Stripable> stripable = (*ri)[_columns.stripable];
- if (stripable && stripable->presentation_info().selected()) {
- TimeAxisView* tav = (*ri)[_columns.tv];
- if (tav) {
- tvl.push_back (tav);
- }
+ if (stripable && stripable->is_selected()) {
_display.get_selection()->select (*ri);
} else {
_display.get_selection()->unselect (*ri);
}
}
-
- /* step two: set the Selection (for stripables/routes) */
- _editor->get_selection().set (tvl);
-
- /* step three, tell the editor */
- _editor->track_selection_changed ();
}
redisplay ();
EditorRoutes::button_press (GdkEventButton* ev)
{
if (Keyboard::is_context_menu_event (ev)) {
- show_menu ();
+ if (_menu == 0) {
+ build_menu ();
+ }
+ _menu->popup (ev->button, ev->time);
return true;
}
bool
EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const& model, TreeModel::Path const& path, bool /*selected*/)
{
- if (selection_countdown) {
- if (--selection_countdown == 0) {
- return true;
- } else {
- /* no selection yet ... */
- return false;
- }
- }
-
TreeModel::iterator iter = model->get_iter (path);
if (iter) {
boost::shared_ptr<Stripable> stripable = (*iter)[_columns.stripable];
- if (boost::dynamic_pointer_cast<VCA> (stripable)) {
- return false;
- }
}
return true;
}
-struct PresentationInfoRouteSorter
-{
- bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
- if (a->is_master()) {
- /* master before everything else */
- return true;
- } else if (b->is_master()) {
- /* everything else before master */
- return false;
- }
- return a->presentation_info().order () < b->presentation_info().order ();
- }
-};
-
-struct PresentationInfoVCASorter
-{
- bool operator() (boost::shared_ptr<VCA> a, boost::shared_ptr<VCA> b) {
- return a->presentation_info().order () < b->presentation_info().order ();
- }
-};
-
void
EditorRoutes::initial_display ()
{
if (!_session) {
- _model->clear ();
+ clear ();
return;
}
sync_treeview_from_presentation_info (Properties::order);
}
-void
-EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
- int x, int y,
- const SelectionData& data,
- guint info, guint time)
-{
- if (data.get_target() == "GTK_TREE_MODEL_ROW") {
- _display.on_drag_data_received (context, x, y, data, info, time);
- return;
- }
-
- context->drag_finish (true, false, time);
-}
-
struct ViewStripable {
TimeAxisView* tav;
boost::shared_ptr<Stripable> stripable;
return;
}
- sl.sort (Stripable::PresentationOrderSorter());
+ sl.sort (Stripable::Sorter());
std::list<ViewStripable> view_stripables;
/* build a list that includes time axis view information */
for (StripableList::const_iterator sli = sl.begin(); sli != sl.end(); ++sli) {
- TimeAxisView* tv = _editor->axis_view_from_stripable (*sli);
+ TimeAxisView* tv = _editor->time_axis_view_from_stripable (*sli);
view_stripables.push_back (ViewStripable (tv, *sli));
}
while (vsi != view_stripables.end()) {
- if (vsi->stripable->presentation_info().selected()) {
+ if (vsi->stripable->is_selected()) {
if (unselected_neighbour != view_stripables.end()) {
--vsi;
- if (vsi->stripable->presentation_info().selected()) {
+ if (vsi->stripable->is_selected()) {
if (unselected_neighbour != view_stripables.end()) {
void
EditorRoutes::clear ()
{
+ PBD::Unwinder<bool> uw (_ignore_selection_change, true);
_display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
_model->clear ();
_display.set_model (_model);
void
EditorRoutes::show_tracks_with_regions_at_playhead ()
{
- boost::shared_ptr<RouteList> const r = _session->get_routes_with_regions_at (_session->transport_frame ());
+ boost::shared_ptr<RouteList> const r = _session->get_routes_with_regions_at (_session->transport_sample ());
set<TimeAxisView*> show;
for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
- TimeAxisView* tav = _editor->axis_view_from_stripable (*i);
+ TimeAxisView* tav = _editor->time_axis_view_from_stripable (*i);
if (tav) {
show.insert (tav);
}