diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 37da07335b..3cde505335 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -251,7 +251,6 @@ Editor::Editor () , editor_summary_pane (get_v_paned ("editor_summary_pane")) , inspector_home (get_container ("inspector_home")) , _master_bus_ui_home (get_container ("master_bus_ui_home")) - , _compact_meter_bridge_home (get_container ("compact_meter_bridge_home")) , vpacker (get_v_box ("vpacker")) , _tool_marker_button (get_waves_button ("tool_marker_button")) , _tool_zoom_button (get_waves_button ("tool_zoom_button")) @@ -315,7 +314,8 @@ Editor::Editor () /* we are a singleton */ PublicEditor::_instance = this; - _compact_meter_bridge_home.add (_compact_meter_bridge); + get_container ("compact_meter_bridge_home").add (_compact_meter_bridge); + get_container ("mixer_bridge_view_home").add (_mixer_bridge_view); _have_idled = false; @@ -1281,6 +1281,7 @@ Editor::set_session (Session *t) _routes->set_session (_session); _locations->set_session (_session); _compact_meter_bridge.set_session (_session); + _mixer_bridge_view.set_session (_session); if (rhythm_ferret) { rhythm_ferret->set_session (_session); diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 4494a34114..0b7cf3d9f2 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -58,6 +58,7 @@ #include "editor_items.h" #include "region_selection.h" #include "compact_meter_bridge.h" +#include "mixer_bridge_view.h" namespace Gtkmm2ext { class TearOff; @@ -688,7 +689,6 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD Gtk::Container& inspector_home; Gtk::Container& _master_bus_ui_home; - Gtk::Container& _compact_meter_bridge_home; MasterBusUI* _master_bus_ui; Gtk::VBox& vpacker; @@ -1901,6 +1901,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD MixerStrip *current_mixer_strip; CompactMeterbridge _compact_meter_bridge; + MixerBridgeView _mixer_bridge_view; bool show_editor_mixer_when_tracks_arrive; void cms_new (boost::shared_ptr); void current_mixer_strip_hidden (); diff --git a/gtk2_ardour/editor_mixer.cc b/gtk2_ardour/editor_mixer.cc index d584783c98..b9fc1d5407 100644 --- a/gtk2_ardour/editor_mixer.cc +++ b/gtk2_ardour/editor_mixer.cc @@ -255,6 +255,7 @@ void Editor::track_mixer_selection () { Mixer_UI::instance()->selection().RoutesChanged.connect (sigc::mem_fun (*this, &Editor::follow_mixer_selection)); + _mixer_bridge_view.track_editor_selection (); } void diff --git a/gtk2_ardour/icons/mixer_monitor_input.png b/gtk2_ardour/icons/mixer_monitor_input.png index 18624ee4de..dd9c954ceb 100644 Binary files a/gtk2_ardour/icons/mixer_monitor_input.png and b/gtk2_ardour/icons/mixer_monitor_input.png differ diff --git a/gtk2_ardour/icons/mixer_mute.png b/gtk2_ardour/icons/mixer_mute.png index 6624a0204a..b2fa83560f 100644 Binary files a/gtk2_ardour/icons/mixer_mute.png and b/gtk2_ardour/icons/mixer_mute.png differ diff --git a/gtk2_ardour/icons/mixer_mute_implicit.png b/gtk2_ardour/icons/mixer_mute_implicit.png index 177bc64719..3690d93b18 100644 Binary files a/gtk2_ardour/icons/mixer_mute_implicit.png and b/gtk2_ardour/icons/mixer_mute_implicit.png differ diff --git a/gtk2_ardour/icons/mixer_record.png b/gtk2_ardour/icons/mixer_record.png index f8c6886ab6..55edb2ef1c 100644 Binary files a/gtk2_ardour/icons/mixer_record.png and b/gtk2_ardour/icons/mixer_record.png differ diff --git a/gtk2_ardour/icons/mixer_solo.png b/gtk2_ardour/icons/mixer_solo.png index ac258874ec..f599b4919a 100644 Binary files a/gtk2_ardour/icons/mixer_solo.png and b/gtk2_ardour/icons/mixer_solo.png differ diff --git a/gtk2_ardour/icons/mixer_solo_safe.png b/gtk2_ardour/icons/mixer_solo_safe.png index 4192219daf..c4407b889c 100644 Binary files a/gtk2_ardour/icons/mixer_solo_safe.png and b/gtk2_ardour/icons/mixer_solo_safe.png differ diff --git a/gtk2_ardour/icons/mixer_strip_meter_marks.png b/gtk2_ardour/icons/mixer_strip_meter_marks.png index 9899fd0795..c46b826184 100644 Binary files a/gtk2_ardour/icons/mixer_strip_meter_marks.png and b/gtk2_ardour/icons/mixer_strip_meter_marks.png differ diff --git a/gtk2_ardour/mixer_bridge_view.cc b/gtk2_ardour/mixer_bridge_view.cc new file mode 100644 index 0000000000..b5e3b5d2bd --- /dev/null +++ b/gtk2_ardour/mixer_bridge_view.cc @@ -0,0 +1,411 @@ +/* + Copyright (C) 2014 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifdef WAF_BUILD +#include "gtk2ardour-config.h" +#endif + +#include +#include + +#include + +#include + +#include +#include +#include + +#include "ardour/debug.h" +#include "ardour/midi_track.h" +#include "ardour/route_group.h" +#include "ardour/session.h" + +#include "ardour/audio_track.h" +#include "ardour/midi_track.h" + +#include "mixer_bridge_view.h" + +#include "keyboard.h" +#include "monitor_section.h" +#include "public_editor.h" +#include "ardour_ui.h" +#include "utils.h" +#include "route_sorter.h" +#include "actions.h" +#include "gui_thread.h" +#include "global_signals.h" +#include "meter_patterns.h" + +#include "i18n.h" + +using namespace ARDOUR; +using namespace PBD; +using namespace Gtk; +using namespace Glib; +using namespace Gtkmm2ext; +using namespace std; +using namespace ArdourMeter; + +using PBD::atoi; + +struct SignalOrderRouteSorter { + bool operator() (boost::shared_ptr a, boost::shared_ptr b) { + if (a->is_master() || a->is_monitor()) { + /* "a" is a special route (master, monitor, etc), and comes + * last in the mixer ordering + */ + return false; + } else if (b->is_master() || b->is_monitor()) { + /* everything comes before b */ + return true; + } + return a->order_key () < b->order_key (); + } +}; + +MixerBridgeView::MixerBridgeView () + : Gtk::EventBox() + , WavesUI ("mixer_bridge_view.xml", *this) + , _mixer_strips_home (get_box ("mixer_strips_home")) + , _following_editor_selection (false) +{ + set_attributes (*this, *xml_tree ()->root (), XMLNodeMap ()); + signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler)); + Route::SyncOrderKeys.connect (*this, invalidator (*this), boost::bind (&MixerBridgeView::sync_order_keys, this), gui_context()); + MixerStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&MixerBridgeView::remove_strip, this, _1), gui_context()); +} + +MixerBridgeView::~MixerBridgeView () +{ +} + +void +MixerBridgeView::set_session (Session* s) +{ + SessionHandlePtr::set_session (s); + + if (!_session) { + return; + } + + boost::shared_ptr routes = _session->get_routes(); + + add_strips(*routes); + + _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&MixerBridgeView::add_strips, this, _1), gui_context()); + + start_updating (); +} + +void +MixerBridgeView::track_editor_selection () +{ + PublicEditor::instance().get_selection().TracksChanged.connect (sigc::mem_fun (*this, &MixerBridgeView::follow_editor_selection)); +} + +void +MixerBridgeView::session_going_away () +{ + ENSURE_GUI_THREAD (*this, &MixerBridgeView::session_going_away); + + for (std::map , MixerStrip*>::iterator i = _strips.begin(); i != _strips.end(); ++i) { + delete (*i).second; + } + + _strips.clear (); + stop_updating (); + + SessionHandlePtr::session_going_away (); + _session = 0; +} + +gint +MixerBridgeView::start_updating () +{ + fast_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (sigc::mem_fun(*this, &MixerBridgeView::fast_update_strips)); + return 0; +} + +gint +MixerBridgeView::stop_updating () +{ + fast_screen_update_connection.disconnect(); + return 0; +} + +void +MixerBridgeView::fast_update_strips () +{ + if (!is_mapped () || !_session) { + return; + } + for (std::map , MixerStrip*>::iterator i = _strips.begin(); i != _strips.end(); ++i) { + (*i).second->fast_update (); + } +} + +void +MixerBridgeView::add_strips (RouteList& routes) +{ + // First detach all the prviously added strips from the ui tree. + for (std::map, MixerStrip*>::iterator i = _strips.begin(); i != _strips.end(); ++i) { + _mixer_strips_home.remove (*(*i).second); // we suppose _mixer_strips_home is + // the parnet. + } + + // Now create the strips for newly added routes + for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) { + boost::shared_ptr route = (*x); + if (route->is_auditioner() || route->is_monitor() || route->is_master()) { + continue; + } + + MixerStrip* strip = strip = new MixerStrip (*ARDOUR_UI::instance()->the_mixer(), _session, route, "mixer_strip.xml"); + strip->signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &MixerBridgeView::strip_button_release_event), strip)); + _strips [route] = strip; + strip->show(); + } + + // Now sort the session's routes and pack the strips accordingly + SignalOrderRouteSorter sorter; + RouteList copy(*_session->get_routes()); + copy.sort(sorter); + + for (RouteList::iterator x = copy.begin(); x != copy.end(); ++x) { + boost::shared_ptr route = (*x); + if (route->is_auditioner() || route->is_monitor() || route->is_master()) { + continue; + } + std::map , MixerStrip*>::iterator i = _strips.find (route); + if (i != _strips.end ()) { + _mixer_strips_home.pack_start (*(*i).second, false, false); + } + } +} + +void +MixerBridgeView::remove_strip (MixerStrip* strip) +{ + if (_session && _session->deletion_in_progress()) { + return; + } + + boost::shared_ptr route = strip->route (); + std::map , MixerStrip*>::iterator i = _strips.find (route); + if (i != _strips.end ()) { + _strips.erase (i); + } +} + +void +MixerBridgeView::sync_order_keys () +{ + Glib::Threads::Mutex::Lock lm (_resync_mutex); + + if (!_session) { + return; + } + + SignalOrderRouteSorter sorter; + boost::shared_ptr routes = _session->get_routes(); + + for (std::map, MixerStrip*>::iterator i = _strips.begin(); i != _strips.end(); ++i) { + _mixer_strips_home.remove (*(*i).second); // we suppose _mixer_strips_home is + // the parnet. + } + + RouteList copy(*routes); + copy.sort(sorter); + + for (RouteList::iterator x = copy.begin(); x != copy.end(); ++x) { + boost::shared_ptr route = (*x); + if (route->is_auditioner() || route->is_monitor() || route->is_master()) { + continue; + } + std::map , MixerStrip*>::iterator i = _strips.find (route); + if (i != _strips.end ()) { + _mixer_strips_home.pack_start (*(*i).second, false, false); + } + } +} + +void +MixerBridgeView::follow_editor_selection () +{ + if (_following_editor_selection) { + return; + } + + _following_editor_selection = true; + _selection.block_routes_changed (true); + + TrackSelection& s (PublicEditor::instance().get_selection().tracks); + + _selection.clear_routes (); + + for (TrackViewList::iterator i = s.begin(); i != s.end(); ++i) { + RouteTimeAxisView* rtav = dynamic_cast (*i); + if (rtav) { + MixerStrip* ms = strip_by_route (rtav->route()); + if (ms) { + _selection.add (ms); + } + } + } + + _following_editor_selection = false; + _selection.block_routes_changed (false); +} + +void +MixerBridgeView::set_route_targets_for_operation () +{ + _route_targets.clear (); + + if (!_selection.empty()) { + _route_targets = _selection.routes; + return; + } + + /* nothing selected ... try to get mixer strip at mouse */ + + int x, y; + get_pointer (x, y); + + MixerStrip* ms = strip_by_x (x); + + if (ms) { + _route_targets.insert (ms); + } +} + +void +MixerBridgeView::toggle_midi_input_active (bool flip_others) +{ + boost::shared_ptr rl (new RouteList); + bool onoff = false; + + set_route_targets_for_operation (); + + for (RouteUISelection::iterator r = _route_targets.begin(); r != _route_targets.end(); ++r) { + boost::shared_ptr mt = (*r)->midi_track(); + + if (mt) { + rl->push_back ((*r)->route()); + onoff = !mt->input_active(); + } + } + + _session->set_exclusive_input_active (rl, onoff, flip_others); +} + +bool +MixerBridgeView::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip) +{ + if (ev->button == 1) { + if (_selection.selected (strip)) { + /* primary-click: toggle selection state of strip */ + if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { + _selection.remove (strip); + } + } else { + if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { + _selection.add (strip); + } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::RangeSelectModifier)) { + + if (!_selection.selected(strip)) { + + /* extend selection */ + + vector tmp; + bool accumulate = false; + + tmp.push_back (strip); + + for (std::map, MixerStrip*>::iterator i = _strips.begin(); i != _strips.end(); ++i) { + if ((*i).second == strip) { + /* hit clicked strip, start accumulating till we hit the first + selected strip + */ + if (accumulate) { + /* done */ + break; + } else { + accumulate = true; + } + } else if (_selection.selected ((*i).second)) { + /* hit selected strip. if currently accumulating others, + we're done. if not accumulating others, start doing so. + */ + if (accumulate) { + /* done */ + break; + } else { + accumulate = true; + } + } else { + if (accumulate) { + tmp.push_back ((*i).second); + } + } + } + + for (vector::iterator i = tmp.begin(); i != tmp.end(); ++i) { + _selection.add (*i); + } + } + + } else { + _selection.set (strip); + } + } + } + + return true; +} + + +MixerStrip* +MixerBridgeView::strip_by_route (boost::shared_ptr route) +{ + std::map , MixerStrip*>::iterator i = _strips.find (route); + if (i != _strips.end ()) { + return (*i).second; + } + + return 0; +} + +MixerStrip* +MixerBridgeView::strip_by_x (int x) +{ + for (std::map, MixerStrip*>::iterator i = _strips.begin(); i != _strips.end(); ++i) { + int x1, x2, y; + + (*i).second->translate_coordinates (*this, 0, 0, x1, y); + x2 = x1 + (*i).second->get_width(); + + if (x >= x1 && x <= x2) { + return (*i).second; + } + } + + return 0; +} diff --git a/gtk2_ardour/mixer_bridge_view.h b/gtk2_ardour/mixer_bridge_view.h new file mode 100644 index 0000000000..997283b49e --- /dev/null +++ b/gtk2_ardour/mixer_bridge_view.h @@ -0,0 +1,83 @@ +/* + Copyright (C) 2014 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef __ardour_mixer_bridge_view_h__ +#define __ardour_mixer_bridge_view_h__ + +#include + +#include +#include +#include +#include + +#include "ardour/ardour.h" +#include "ardour/types.h" +#include "ardour/session_handle.h" + +#include "pbd/stateful.h" +#include "pbd/signals.h" + +#include "gtkmm2ext/visibility_tracker.h" + +#include "waves_ui.h" +#include "mixer_strip.h" +#include "mixer_actor.h" + +class MixerBridgeView : + public Gtk::EventBox, + public WavesUI, + public PBD::ScopedConnectionList, + public ARDOUR::SessionHandlePtr, + public MixerActor +{ + public: + MixerBridgeView (); + ~MixerBridgeView(); + void set_session (ARDOUR::Session *); + void track_editor_selection (); + + protected: + void set_route_targets_for_operation (); + void toggle_midi_input_active (bool flip_others); + + private: + Gtk::Box& _mixer_strips_home; + bool _following_editor_selection; + + gint start_updating (); + gint stop_updating (); + + sigc::connection fast_screen_update_connection; + void fast_update_strips (); + + void add_strips (ARDOUR::RouteList&); + void remove_strip (MixerStrip *); + + void session_going_away (); + void sync_order_keys (); + void follow_editor_selection (); + bool strip_button_release_event (GdkEventButton*, MixerStrip*); + MixerStrip* strip_by_route (boost::shared_ptr route); + MixerStrip* strip_by_x (int x); + + std::map , MixerStrip*> _strips; + mutable Glib::Threads::Mutex _resync_mutex; +}; + +#endif //__ardour_mixer_bridge_view_h__ diff --git a/gtk2_ardour/mono_panner.cc b/gtk2_ardour/mono_panner.cc index 7ce9a9d88b..059892e1b0 100644 --- a/gtk2_ardour/mono_panner.cc +++ b/gtk2_ardour/mono_panner.cc @@ -105,20 +105,18 @@ MonoPanner::set_tooltip () _tooltip.set_tip (buf); } -bool -MonoPanner::on_expose_event (GdkEventExpose*) +void +MonoPanner::render (cairo_t* cr) { - Cairo::RefPtr context = get_window()->create_cairo_context(); unsigned pos = (unsigned)(rint (100.0 * position_control->get_value ())); /* 0..100 */ double x = (get_width() - _knob_image[pos]->get_width())/2.0; double y = (get_height() - _knob_image[pos]->get_height())/2.0; - cairo_rectangle (context->cobj(), x, y, _knob_image[pos]->get_width(), _knob_image[pos]->get_height()); + cairo_rectangle (cr, x, y, _knob_image[pos]->get_width(), _knob_image[pos]->get_height()); - gdk_cairo_set_source_pixbuf (context->cobj(), _knob_image[pos]->gobj(), x, y); - cairo_fill (context->cobj()); - return true; + gdk_cairo_set_source_pixbuf (cr, _knob_image[pos]->gobj(), x, y); + cairo_fill (cr); } bool diff --git a/gtk2_ardour/mono_panner.h b/gtk2_ardour/mono_panner.h index e8bcb6a08e..8b2d2a4b50 100644 --- a/gtk2_ardour/mono_panner.h +++ b/gtk2_ardour/mono_panner.h @@ -48,7 +48,7 @@ class MonoPanner : public PannerInterface sigc::signal StopGesture; protected: - bool on_expose_event (GdkEventExpose*); + void render (cairo_t* cr); bool on_button_press_event (GdkEventButton*); bool on_button_release_event (GdkEventButton*); bool on_motion_notify_event (GdkEventMotion*); diff --git a/gtk2_ardour/panner_interface.h b/gtk2_ardour/panner_interface.h index 1aef6e4654..8e91737789 100644 --- a/gtk2_ardour/panner_interface.h +++ b/gtk2_ardour/panner_interface.h @@ -21,10 +21,10 @@ #define __gtk_ardour_panner_interface_h__ #include -#include #include #include "gtkmm2ext/persistent_tooltip.h" #include "pbd/destructible.h" +#include "gtkmm2ext/cairo_widget.h" namespace ARDOUR { class Panner; @@ -48,7 +48,7 @@ private: /** Parent class for some panner UI classes that contains some common code */ -class PannerInterface : public Gtk::DrawingArea, public PBD::Destructible +class PannerInterface : public CairoWidget, public PBD::Destructible { public: PannerInterface (boost::shared_ptr); diff --git a/gtk2_ardour/stereo_panner.cc b/gtk2_ardour/stereo_panner.cc index 4191e81708..a3d74a58ab 100644 --- a/gtk2_ardour/stereo_panner.cc +++ b/gtk2_ardour/stereo_panner.cc @@ -112,20 +112,18 @@ StereoPanner::set_tooltip () _tooltip.set_tip (buf); } -bool -StereoPanner::on_expose_event (GdkEventExpose*) +void +StereoPanner::render (cairo_t* cr) { - Cairo::RefPtr context = get_window()->create_cairo_context(); unsigned pos = (unsigned)(rint (100.0 * position_control->get_value ())); /* 0..100 */ double x = (get_width() - _knob_image[pos]->get_width())/2.0; double y = (get_height() - _knob_image[pos]->get_height())/2.0; - cairo_rectangle (context->cobj(), x, y, _knob_image[pos]->get_width(), _knob_image[pos]->get_height()); + cairo_rectangle (cr, x, y, _knob_image[pos]->get_width(), _knob_image[pos]->get_height()); - gdk_cairo_set_source_pixbuf (context->cobj(), _knob_image[pos]->gobj(), x, y); - cairo_fill (context->cobj()); - return true; + gdk_cairo_set_source_pixbuf (cr, _knob_image[pos]->gobj(), x, y); + cairo_fill (cr); } bool diff --git a/gtk2_ardour/stereo_panner.h b/gtk2_ardour/stereo_panner.h index 6e1b79bf96..6fa7f16097 100644 --- a/gtk2_ardour/stereo_panner.h +++ b/gtk2_ardour/stereo_panner.h @@ -51,7 +51,7 @@ class StereoPanner : public PannerInterface sigc::signal StopWidthGesture; protected: - bool on_expose_event (GdkEventExpose*); + void render (cairo_t* cr); bool on_button_press_event (GdkEventButton*); bool on_button_release_event (GdkEventButton*); bool on_motion_notify_event (GdkEventMotion*); diff --git a/gtk2_ardour/ui/editor_window.xml b/gtk2_ardour/ui/editor_window.xml index 8ca876527a..240b13402d 100644 --- a/gtk2_ardour/ui/editor_window.xml +++ b/gtk2_ardour/ui/editor_window.xml @@ -203,21 +203,30 @@ - - - + + + + + + + + + + - - - + + + table.yexpand="true" + bgnormal="#3E3E3E"> @@ -274,7 +284,10 @@ - + diff --git a/gtk2_ardour/ui/mixer_bridge_view.xml b/gtk2_ardour/ui/mixer_bridge_view.xml new file mode 100644 index 0000000000..9ea3b23e3a --- /dev/null +++ b/gtk2_ardour/ui/mixer_bridge_view.xml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/gtk2_ardour/ui/mixer_gain_meter.xml b/gtk2_ardour/ui/mixer_gain_meter.xml index 1aef03a6d3..ca0c4308c5 100644 --- a/gtk2_ardour/ui/mixer_gain_meter.xml +++ b/gtk2_ardour/ui/mixer_gain_meter.xml @@ -3,7 +3,7 @@ - +