X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fmeterbridge.cc;h=aa2046a948e2976469703eb602a18a27d4b95143;hb=eedeb4949d16e5415ba790a042c5db6d83580b36;hp=dc06dc2415c48f99066b108084cded7fd9607e36;hpb=c23a537b6189692b9b8b56de20889cfa596fb0de;p=ardour.git diff --git a/gtk2_ardour/meterbridge.cc b/gtk2_ardour/meterbridge.cc index dc06dc2415..aa2046a948 100644 --- a/gtk2_ardour/meterbridge.cc +++ b/gtk2_ardour/meterbridge.cc @@ -40,9 +40,11 @@ #include "ardour/audio_track.h" #include "ardour/midi_track.h" +#include "ardour/route_sorters.h" #include "meterbridge.h" +#include "keyboard.h" #include "monitor_section.h" #include "public_editor.h" #include "ardour_ui.h" @@ -50,15 +52,19 @@ #include "route_sorter.h" #include "actions.h" #include "gui_thread.h" +#include "meter_patterns.h" +#include "timers.h" #include "i18n.h" using namespace ARDOUR; +using namespace ARDOUR_UI_UTILS; using namespace PBD; using namespace Gtk; using namespace Glib; using namespace Gtkmm2ext; using namespace std; +using namespace ArdourMeter; using PBD::atoi; @@ -74,86 +80,109 @@ Meterbridge::instance () return _instance; } -/* copy from gtk2_ardour/mixer_ui.cc -- TODO consolidate - * used by Meterbridge::set_session() below - */ -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 (MixerSort) < b->order_key (MixerSort); - } -}; - -/* modified version of above - * used in Meterbridge::sync_order_keys() - */ -struct MeterOrderRouteSorter { - bool operator() (MeterStrip *ma, MeterStrip *mb) { - boost::shared_ptr a = ma->route(); - boost::shared_ptr b = mb->route(); - 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 (MixerSort) < b->order_key (MixerSort); - } -}; - - Meterbridge::Meterbridge () : Window (Gtk::WINDOW_TOPLEVEL) , VisibilityTracker (*((Gtk::Window*) this)) , _visible (false) , _show_busses (false) + , metrics_left (1, MeterPeak) + , metrics_right (2, MeterPeak) + , cur_max_width (-1) { set_name ("Meter Bridge"); + m_width = default_width; + m_height = default_height; + m_root_x = 1; + m_root_y = 1; + update_title (); set_wmclass (X_("ardour_mixer"), PROGRAM_NAME); Gdk::Geometry geom; geom.max_width = 1<<16; - geom.max_height = 1024; // XXX see FastMeter::max_pattern_metric_size - set_geometry_hints(*((Gtk::Window*) this), geom, Gdk::HINT_MAX_SIZE); - - /* disable "maximize" button because it overrides size hint from above, - * this also keeps window on top.. - */ - set_type_hint(Gdk::WINDOW_TYPE_HINT_UTILITY); + geom.max_height = max_height; + geom.min_width = 40; + geom.min_height = -1; + geom.height_inc = 16; + geom.width_inc = 1; + assert(max_height % 16 == 0); + set_geometry_hints(*((Gtk::Window*) this), geom, Gdk::HINT_MIN_SIZE | Gdk::HINT_MAX_SIZE | Gdk::HINT_RESIZE_INC); + + set_border_width (0); + + metrics_vpacker_left.pack_start (metrics_left, true, true); + metrics_vpacker_left.pack_start (metrics_spacer_left, false, false); + metrics_spacer_left.set_size_request(-1, 0); + metrics_spacer_left.set_spacing(0); + + metrics_vpacker_right.pack_start (metrics_right, true, true); + metrics_vpacker_right.pack_start (metrics_spacer_right, false, false); + metrics_spacer_right.set_size_request(-1, 0); + metrics_spacer_right.set_spacing(0); signal_delete_event().connect (sigc::mem_fun (*this, &Meterbridge::hide_window)); signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler)); - Route::SyncOrderKeys.connect (*this, invalidator (*this), boost::bind (&Meterbridge::sync_order_keys, this, _1), gui_context()); - + Route::SyncOrderKeys.connect (*this, invalidator (*this), boost::bind (&Meterbridge::sync_order_keys, this), gui_context()); MeterStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Meterbridge::remove_strip, this, _1), gui_context()); - - global_hpacker.set_spacing(3); - scroller.add (global_hpacker); + MeterStrip::MetricChanged.connect (*this, invalidator (*this), boost::bind(&Meterbridge::resync_order, this), gui_context()); + MeterStrip::ConfigurationChanged.connect (*this, invalidator (*this), boost::bind(&Meterbridge::queue_resize, this), gui_context()); + + /* work around ScrolledWindowViewport alignment mess Part one */ + Gtk::HBox * yspc = manage (new Gtk::HBox()); + yspc->set_size_request(-1, 1); + Gtk::VBox * xspc = manage (new Gtk::VBox()); + xspc->pack_start(meterarea, true, true); + xspc->pack_start(*yspc, false, false); + yspc->show(); + xspc->show(); + + meterarea.set_spacing(0); + scroller.set_shadow_type(Gtk::SHADOW_NONE); + scroller.set_border_width(0); + scroller.add (*xspc); scroller.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_NEVER); - global_vpacker.pack_start (scroller, true, true); + + global_hpacker.pack_start (metrics_vpacker_left, false, false); + global_hpacker.pack_start (scroller, true, true); + global_hpacker.pack_start (metrics_vpacker_right, false, false); + + global_vpacker.pack_start (global_hpacker, true, true); add (global_vpacker); - global_hpacker.show(); + metrics_left.show(); + metrics_right.show(); + + metrics_vpacker_left.show(); + metrics_spacer_left.show(); + metrics_vpacker_right.show(); + metrics_spacer_right.show(); + + meterarea.show(); global_vpacker.show(); + global_hpacker.show(); scroller.show(); + + /* the return of the ScrolledWindowViewport mess: + * remove shadow from scrollWindow's viewport + * see http://www.mail-archive.com/gtkmm-list@gnome.org/msg03509.html + */ + Gtk::Viewport* viewport = (Gtk::Viewport*) scroller.get_child(); + viewport->set_shadow_type(Gtk::SHADOW_NONE); + viewport->set_border_width(0); + + UI::instance()->theme_changed.connect (sigc::mem_fun(*this, &Meterbridge::on_theme_changed)); + UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &Meterbridge::on_theme_changed)); + UIConfiguration::instance().DPIReset.connect (sigc::mem_fun (*this, &Meterbridge::on_theme_changed)); } Meterbridge::~Meterbridge () { + while (_metrics.size() > 0) { + delete (_metrics.back()); + _metrics.pop_back(); + } } void @@ -200,7 +229,9 @@ void Meterbridge::set_window_pos_and_size () { resize (m_width, m_height); - move (m_root_x, m_root_y); + if (m_root_x >= 0 && m_root_y >= 0) { + move (m_root_x, m_root_y); + } } void @@ -213,6 +244,7 @@ Meterbridge::get_window_pos_and_size () bool Meterbridge::hide_window (GdkEventAny *ev) { + if (!_visible) return 0; get_window_pos_and_size(); _visible = false; return just_hide_it(ev, static_cast(this)); @@ -224,7 +256,7 @@ Meterbridge::on_key_press_event (GdkEventKey* ev) if (gtk_window_propagate_key_event (GTK_WINDOW(gobj()), ev)) { return true; } - return forward_key_press (ev); + return relay_key_press (ev, this); } bool @@ -237,6 +269,137 @@ Meterbridge::on_key_release_event (GdkEventKey* ev) return true; } +bool +Meterbridge::on_scroll_event (GdkEventScroll* ev) +{ + switch (ev->direction) { + case GDK_SCROLL_LEFT: + scroll_left (); + return true; + case GDK_SCROLL_UP: + if (ev->state & Keyboard::TertiaryModifier) { + scroll_left (); + return true; + } + return false; + + case GDK_SCROLL_RIGHT: + scroll_right (); + return true; + + case GDK_SCROLL_DOWN: + if (ev->state & Keyboard::TertiaryModifier) { + scroll_right (); + return true; + } + return false; + } + + return false; +} + +void +Meterbridge::scroll_left () +{ + if (!scroller.get_hscrollbar()) return; + Adjustment* adj = scroller.get_hscrollbar()->get_adjustment(); + /* stupid GTK: can't rely on clamping across versions */ + scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment())); +} + +void +Meterbridge::scroll_right () +{ + if (!scroller.get_hscrollbar()) return; + Adjustment* adj = scroller.get_hscrollbar()->get_adjustment(); + /* stupid GTK: can't rely on clamping across versions */ + scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment())); +} + +void +Meterbridge::on_size_request (Gtk::Requisition* r) +{ + meter_clear_pattern_cache(3); + Gtk::Window::on_size_request(r); + + Gdk::Geometry geom; + Gtk::Requisition mr = meterarea.size_request(); + + geom.max_width = mr.width + metrics_left.get_width() + metrics_right.get_width(); + geom.max_width = std::max(50, geom.max_width); + geom.max_height = max_height; + + if (cur_max_width != geom.max_width) { + cur_max_width = geom.max_width; + /* height resizes are 'heavy' since the metric areas and meter-patterns + * are re-generated. limit to 16px steps. */ + geom.height_inc = 16; + geom.width_inc = 1; + geom.min_width = 40; + geom.min_height = -1; + set_geometry_hints(*((Gtk::Window*) this), geom, Gdk::HINT_MIN_SIZE | Gdk::HINT_MAX_SIZE | Gdk::HINT_RESIZE_INC); + } +} + +void +Meterbridge::on_size_allocate (Gtk::Allocation& a) +{ + const Gtk::Scrollbar * hsc = scroller.get_hscrollbar(); + + /* switch left/right edge patterns depending on horizontal scroll-position */ + if (scroller.get_hscrollbar_visible() && hsc) { + if (!scroll_connection.connected()) { + scroll_connection = scroller.get_hscrollbar()->get_adjustment()->signal_value_changed().connect(sigc::mem_fun (*this, &Meterbridge::on_scroll)); + scroller.get_hscrollbar()->get_adjustment()->signal_changed().connect(sigc::mem_fun (*this, &Meterbridge::on_scroll)); + } + gint scrollbar_spacing; + gtk_widget_style_get (GTK_WIDGET (scroller.gobj()), + "scrollbar-spacing", &scrollbar_spacing, NULL); + const int h = hsc->get_height() + scrollbar_spacing + 1; + metrics_spacer_left.set_size_request(-1, h); + metrics_spacer_right.set_size_request(-1, h); + } else { + metrics_spacer_left.set_size_request(-1, 0); + metrics_spacer_right.set_size_request(-1, 0); + } + Gtk::Window::on_size_allocate(a); +} + +void +Meterbridge::on_scroll() +{ + if (!scroller.get_hscrollbar()) return; + + Adjustment* adj = scroller.get_hscrollbar()->get_adjustment(); + int leftend = adj->get_value(); + int rightend = scroller.get_width() + leftend; + + int mm_left = _mm_left; + int mm_right = _mm_right; + ARDOUR::MeterType mt_left = _mt_left; + ARDOUR::MeterType mt_right = _mt_right; + + for (unsigned int i = 0; i < _metrics.size(); ++i) { + int sx, dx = 0, dy = 0; + int mm = _metrics[i]->get_metric_mode(); + sx = (mm & 2) ? _metrics[i]->get_width() : 0; + + _metrics[i]->translate_coordinates(meterarea, sx, 0, dx, dy); + + if (dx < leftend && !(mm&2)) { + mm_left = mm; + mt_left = _metrics[i]->meter_type(); + } + if (dx > rightend && (mm&2)) { + mm_right = mm; + mt_right = _metrics[i]->meter_type(); + break; + } + } + metrics_left.set_metric_mode(mm_left, mt_left); + metrics_right.set_metric_mode(mm_right, mt_right); +} + void Meterbridge::set_session (Session* s) { @@ -246,15 +409,20 @@ Meterbridge::set_session (Session* s) return; } + metrics_left.set_session(s); + metrics_right.set_session(s); + XMLNode* node = _session->instant_xml(X_("Meterbridge")); if (node) { set_state (*node); } update_title (); - _show_busses = _session->config.get_show_busses_in_meterbridge(); + _show_busses = _session->config.get_show_busses_on_meterbridge(); + _show_master = _session->config.get_show_master_on_meterbridge(); + _show_midi = _session->config.get_show_midi_on_meterbridge(); - SignalOrderRouteSorter sorter; + ARDOUR::SignalOrderRouteSorter sorter; boost::shared_ptr routes = _session->get_routes(); RouteList copy(*routes); @@ -265,10 +433,11 @@ Meterbridge::set_session (Session* s) _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Meterbridge::update_title, this), gui_context()); _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Meterbridge::update_title, this), gui_context()); _session->config.ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Meterbridge::parameter_changed, this, _1), gui_context()); + Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Meterbridge::parameter_changed, this, _1), gui_context()); if (_visible) { show_window(); - ActionManager::check_toggleaction ("/Common/toggle-meterbridge"); + present (); } start_updating (); } @@ -278,8 +447,8 @@ Meterbridge::session_going_away () { ENSURE_GUI_THREAD (*this, &Meterbridge::session_going_away); - for (list::iterator i = strips.begin(); i != strips.end(); ++i) { - delete (*i); + for (list::iterator i = strips.begin(); i != strips.end(); ++i) { + delete ((*i).s); } strips.clear (); @@ -348,26 +517,24 @@ Meterbridge::set_state (const XMLNode& node) XMLNode& Meterbridge::get_state (void) { + char buf[32]; XMLNode* node = new XMLNode ("Meterbridge"); - if (is_realized()) { - Glib::RefPtr win = get_window(); - + if (is_realized() && _visible) { get_window_pos_and_size (); - - XMLNode* geometry = new XMLNode ("geometry"); - char buf[32]; - snprintf(buf, sizeof(buf), "%d", m_width); - geometry->add_property(X_("x_size"), string(buf)); - snprintf(buf, sizeof(buf), "%d", m_height); - geometry->add_property(X_("y_size"), string(buf)); - snprintf(buf, sizeof(buf), "%d", m_root_x); - geometry->add_property(X_("x_pos"), string(buf)); - snprintf(buf, sizeof(buf), "%d", m_root_y); - geometry->add_property(X_("y_pos"), string(buf)); - node->add_child_nocopy (*geometry); } + XMLNode* geometry = new XMLNode ("geometry"); + snprintf(buf, sizeof(buf), "%d", m_width); + geometry->add_property(X_("x_size"), string(buf)); + snprintf(buf, sizeof(buf), "%d", m_height); + geometry->add_property(X_("y_size"), string(buf)); + snprintf(buf, sizeof(buf), "%d", m_root_x); + geometry->add_property(X_("x_pos"), string(buf)); + snprintf(buf, sizeof(buf), "%d", m_root_y); + geometry->add_property(X_("y_pos"), string(buf)); + node->add_child_nocopy (*geometry); + node->add_property ("show-meterbridge", _visible ? "yes" : "no"); return *node; } @@ -376,7 +543,7 @@ Meterbridge::get_state (void) gint Meterbridge::start_updating () { - fast_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (sigc::mem_fun(*this, &Meterbridge::fast_update_strips)); + fast_screen_update_connection = Timers::super_rapid_connect (sigc::mem_fun(*this, &Meterbridge::fast_update_strips)); return 0; } @@ -393,8 +560,9 @@ Meterbridge::fast_update_strips () if (!is_mapped () || !_session) { return; } - for (list::iterator i = strips.begin(); i != strips.end(); ++i) { - (*i)->fast_update (); + for (list::iterator i = strips.begin(); i != strips.end(); ++i) { + if (!(*i).visible) continue; + (*i).s->fast_update (); } } @@ -411,14 +579,15 @@ Meterbridge::add_strips (RouteList& routes) continue; } - strip = new MeterStrip (*this, _session, route); - strips.push_back (strip); + strip = new MeterStrip (_session, route); + strips.push_back (MeterBridgeStrip(strip)); + route->active_changed.connect (*this, invalidator (*this), boost::bind (&Meterbridge::resync_order, this), gui_context ()); - global_hpacker.pack_start (*strip, false, false); + meterarea.pack_start (*strip, false, false); strip->show(); } - sync_order_keys(MixerSort); + resync_order(); } void @@ -428,65 +597,203 @@ Meterbridge::remove_strip (MeterStrip* strip) return; } - list::iterator i; - if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) { - strips.erase (i); + list::iterator i; + for (list::iterator i = strips.begin(); i != strips.end(); ++i) { + if ( (*i).s == strip) { + strips.erase (i); + break; + } } + + resync_order(); } void -Meterbridge::sync_order_keys (RouteSortOrderKey src) +Meterbridge::sync_order_keys () { + Glib::Threads::Mutex::Lock lm (_resync_mutex); + MeterOrderRouteSorter sorter; - std::list copy (strips); - copy.sort(sorter); + strips.sort(sorter); int pos = 0; - for (list::iterator i = copy.begin(); i != copy.end(); ++i) { + int vis = 0; + MeterStrip * last = 0; -#if 0 // TODO subscribe to route active,inactive changes, merge w/ bus - if (! (*i)->route()->active()) { - (*i)->hide(); - } else { - (*i)->show(); - } -#endif + unsigned int metrics = 0; + MeterType lmt = MeterPeak; + bool have_midi = false; + metrics_left.set_metric_mode(1, lmt); - // TODO simplyfy, abstract ->is_bus() - if ((*i)->route()->is_master()) { - /* always show master */ - (*i)->show(); + for (list::iterator i = strips.begin(); i != strips.end(); ++i) { + + if (! (*i).s->route()->active()) { + (*i).s->hide(); + (*i).visible = false; } - else if (boost::dynamic_pointer_cast((*i)->route()) == 0 - && boost::dynamic_pointer_cast((*i)->route()) == 0 + else if ((*i).s->route()->is_master()) { + if (_show_master) { + (*i).s->show(); + (*i).visible = true; + vis++; + } else { + (*i).s->hide(); + (*i).visible = false; + } + } + else if (boost::dynamic_pointer_cast((*i).s->route()) == 0 + && boost::dynamic_pointer_cast((*i).s->route()) == 0 ) { /* non-master bus */ if (_show_busses) { - (*i)->show(); + (*i).s->show(); + (*i).visible = true; + vis++; } else { - (*i)->hide(); + (*i).s->hide(); + (*i).visible = false; + } + } + else if (boost::dynamic_pointer_cast((*i).s->route())) { + if (_show_midi) { + (*i).s->show(); + (*i).visible = true; + vis++; + } else { + (*i).s->hide(); + (*i).visible = false; } } else { - (*i)->show(); + (*i).s->show(); + (*i).visible = true; + vis++; } + (*i).s->set_tick_bar(0); + + MeterType nmt = (*i).s->meter_type(); + if (nmt == MeterKrms) nmt = MeterPeak; // identical metrics + if (vis == 1) { + (*i).s->set_tick_bar(1); + } + if ((*i).visible && nmt != lmt && vis == 1) { + lmt = nmt; + metrics_left.set_metric_mode(1, lmt); + } else if ((*i).visible && nmt != lmt) { - if (pos%8 == 0) { - (*i)->display_metrics(true); - } else { - (*i)->display_metrics(false); + if (last) { + last->set_tick_bar(last->get_tick_bar() | 2); + } + (*i).s->set_tick_bar((*i).s->get_tick_bar() | 1); + + if (_metrics.size() <= metrics) { + _metrics.push_back(new MeterStrip(have_midi ? 2 : 3, lmt)); + meterarea.pack_start (*_metrics[metrics], false, false); + _metrics[metrics]->set_session(_session); + _metrics[metrics]->show(); + } else { + _metrics[metrics]->set_metric_mode(have_midi ? 2 : 3, lmt); + } + meterarea.reorder_child(*_metrics[metrics], pos++); + metrics++; + + lmt = nmt; + + if (_metrics.size() <= metrics) { + _metrics.push_back(new MeterStrip(1, lmt)); + meterarea.pack_start (*_metrics[metrics], false, false); + _metrics[metrics]->set_session(_session); + _metrics[metrics]->show(); + } else { + _metrics[metrics]->set_metric_mode(1, lmt); + } + meterarea.reorder_child(*_metrics[metrics], pos++); + metrics++; + have_midi = false; + } + + if ((*i).visible && (*i).s->has_midi()) { + have_midi = true; + } + + meterarea.reorder_child(*((*i).s), pos++); + if ((*i).visible) { + last = (*i).s; } - global_hpacker.reorder_child(*(*i), pos++); } + + if (last) { + last->set_tick_bar(last->get_tick_bar() | 2); + } + + metrics_right.set_metric_mode(have_midi ? 2 : 3, lmt); + + while (_metrics.size() > metrics) { + meterarea.remove(*_metrics.back()); + delete (_metrics.back()); + _metrics.pop_back(); + } + + _mm_left = metrics_left.get_metric_mode(); + _mt_left = metrics_left.meter_type(); + _mm_right = metrics_right.get_metric_mode(); + _mt_right = metrics_right.meter_type(); + + on_scroll(); + queue_resize(); +} + +void +Meterbridge::resync_order() +{ + sync_order_keys(); } void Meterbridge::parameter_changed (std::string const & p) { - if (p == "show-busses-in-meterbridge") { - _show_busses = _session->config.get_show_busses_in_meterbridge(); - sync_order_keys(MixerSort); + if (p == "show-busses-on-meterbridge") { + _show_busses = _session->config.get_show_busses_on_meterbridge(); + resync_order(); + } + else if (p == "show-master-on-meterbridge") { + _show_master = _session->config.get_show_master_on_meterbridge(); + resync_order(); + } + else if (p == "show-midi-on-meterbridge") { + _show_midi = _session->config.get_show_midi_on_meterbridge(); + resync_order(); + } + else if (p == "meter-line-up-level") { + meter_clear_pattern_cache(); + } + else if (p == "show-rec-on-meterbridge") { + scroller.queue_resize(); + } + else if (p == "show-mute-on-meterbridge") { + scroller.queue_resize(); + } + else if (p == "show-solo-on-meterbridge") { + scroller.queue_resize(); } + else if (p == "show-name-on-meterbridge") { + scroller.queue_resize(); + } + else if (p == "meterbridge-label-height") { + scroller.queue_resize(); + } + else if (p == "show-monitor-on-meterbridge") { + scroller.queue_resize(); + } + else if (p == "track-name-number") { + scroller.queue_resize(); + } +} + +void +Meterbridge::on_theme_changed () +{ + meter_clear_pattern_cache(); }