+ gain_watching = ARDOUR_UI::RapidScreenUpdate.connect (sigc::mem_fun (*this, &GainMeterBase::effective_gain_display));
+ }
+}
+
+void
+GainMeterBase::update_meters()
+{
+ char buf[32];
+ float mpeak = level_meter->update_meters();
+
+ if (mpeak > max_peak) {
+ max_peak = mpeak;
+ if (mpeak <= -200.0f) {
+ peak_display.set_label (_("-inf"));
+ } else {
+ snprintf (buf, sizeof(buf), "%.1f", mpeak);
+ peak_display.set_label (buf);
+ }
+
+ if (mpeak >= 0.0f) {
+ peak_display.set_name ("MixerStripPeakDisplayPeak");
+ }
+ }
+}
+
+void GainMeterBase::color_handler(bool dpi)
+{
+ color_changed = true;
+ dpi_changed = (dpi) ? true : false;
+ setup_meters();
+}
+
+void
+GainMeterBase::set_width (Width w, int len)
+{
+ _width = w;
+ level_meter->setup_meters (len);
+}
+
+
+void
+GainMeterBase::on_theme_changed()
+{
+ style_changed = true;
+}
+
+GainMeter::GainMeter (Session* s, int fader_length)
+ : GainMeterBase (s, slider, slider_desensitised, false, fader_length)
+ , gain_display_box(true, 0)
+ , hbox(true, 2)
+{
+ gain_display_box.pack_start (gain_display, true, true);
+
+ meter_metric_area.set_name ("AudioTrackMetrics");
+ set_size_request_to_display_given_text (meter_metric_area, "-127", 0, 0);
+
+ gain_automation_style_button.set_name ("MixerAutomationModeButton");
+ gain_automation_state_button.set_name ("MixerAutomationPlaybackButton");
+
+ ARDOUR_UI::instance()->set_tip (gain_automation_state_button, _("Fader automation mode"));
+ ARDOUR_UI::instance()->set_tip (gain_automation_style_button, _("Fader automation type"));
+
+ gain_automation_style_button.unset_flags (Gtk::CAN_FOCUS);
+ gain_automation_state_button.unset_flags (Gtk::CAN_FOCUS);
+
+ gain_automation_state_button.set_size_request(15, 15);
+ gain_automation_style_button.set_size_request(15, 15);
+
+ fader_vbox = manage (new Gtk::VBox());
+ fader_vbox->set_spacing (0);
+ fader_vbox->pack_start (*gain_slider, true, true);
+
+ fader_alignment.set (0.5, 0.5, 0.0, 1.0);
+ fader_alignment.add (*fader_vbox);
+
+ hbox.pack_start (fader_alignment, true, true);
+
+ set_spacing (2);
+
+ pack_start (gain_display_box, Gtk::PACK_SHRINK);
+ pack_start (hbox, Gtk::PACK_SHRINK);
+
+ meter_alignment.set (0.5, 0.5, 0.0, 1.0);
+ meter_alignment.add (*level_meter);
+
+ meter_metric_area.signal_expose_event().connect (
+ sigc::mem_fun(*this, &GainMeter::meter_metrics_expose));
+}
+
+void
+GainMeter::set_controls (boost::shared_ptr<Route> r,
+ boost::shared_ptr<PeakMeter> meter,
+ boost::shared_ptr<Amp> amp)
+{
+ if (meter_alignment.get_parent()) {
+ hbox.remove (meter_alignment);
+ }
+
+ if (peak_display.get_parent()) {
+ gain_display_box.remove (peak_display);
+ }
+
+ if (gain_automation_state_button.get_parent()) {
+ fader_vbox->remove (gain_automation_state_button);
+ }
+
+ GainMeterBase::set_controls (r, meter, amp);
+
+ if (_meter) {
+ _meter->ConfigurationChanged.connect (
+ model_connections, invalidator (*this), boost::bind (&GainMeter::meter_configuration_changed, this, _1), gui_context()
+ );
+
+ meter_configuration_changed (_meter->input_streams ());
+ }
+
+
+ /*
+ if we have a non-hidden route (ie. we're not the click or the auditioner),
+ pack some route-dependent stuff.
+ */
+
+ gain_display_box.pack_end (peak_display, true, true);
+ hbox.pack_start (meter_alignment, true, true);
+
+ if (r && !r->is_hidden()) {
+ fader_vbox->pack_start (gain_automation_state_button, false, false, 0);
+ }
+
+ setup_meters ();
+ hbox.show_all ();
+}
+
+int
+GainMeter::get_gm_width ()
+{
+ Gtk::Requisition sz;
+ hbox.size_request (sz);
+ return sz.width;
+}
+
+cairo_pattern_t*
+GainMeter::render_metrics (Gtk::Widget& w, vector<DataType> types)
+{
+ Glib::RefPtr<Gdk::Window> win (w.get_window());
+
+ gint width, height;
+ win->get_size (width, height);
+
+ cairo_surface_t* surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
+ cairo_t* cr = cairo_create (surface);
+ PangoLayout* layout = gtk_widget_create_pango_layout (w.gobj(), "");
+
+ cairo_move_to (cr, 0, 0);
+ cairo_rectangle (cr, 0, 0, width, height);
+ {
+ Gdk::Color c = w.get_style()->get_bg (Gtk::STATE_NORMAL);
+ cairo_set_source_rgb (cr, c.get_red_p(), c.get_green_p(), c.get_blue_p());
+ }
+ cairo_fill (cr);
+
+ for (vector<DataType>::const_iterator i = types.begin(); i != types.end(); ++i) {
+
+ Gdk::Color c;
+
+ if (types.size() > 1) {
+ /* we're overlaying more than 1 set of marks, so use different colours */
+ Gdk::Color c;
+ switch (*i) {
+ case DataType::AUDIO:
+ c = w.get_style()->get_fg (Gtk::STATE_NORMAL);
+ cairo_set_source_rgb (cr, c.get_red_p(), c.get_green_p(), c.get_blue_p());
+ break;
+ case DataType::MIDI:
+ c = w.get_style()->get_fg (Gtk::STATE_ACTIVE);
+ cairo_set_source_rgb (cr, c.get_red_p(), c.get_green_p(), c.get_blue_p());
+ break;
+ }
+ } else {
+ c = w.get_style()->get_fg (Gtk::STATE_NORMAL);
+ cairo_set_source_rgb (cr, c.get_red_p(), c.get_green_p(), c.get_blue_p());
+ }
+
+ vector<int> points;
+
+ switch (*i) {
+ case DataType::AUDIO:
+ points.push_back (-50);
+ points.push_back (-40);
+ points.push_back (-30);
+ points.push_back (-20);
+ points.push_back (-10);
+ points.push_back (-3);
+ points.push_back (0);
+ points.push_back (4);
+ break;
+
+ case DataType::MIDI:
+ points.push_back (0);
+ if (types.size() == 1) {
+ points.push_back (32);
+ } else {
+ /* tweak so as not to overlay the -30dB mark */
+ points.push_back (48);
+ }
+ points.push_back (64);
+ points.push_back (96);
+ points.push_back (127);
+ break;
+ }
+
+ char buf[32];
+
+ for (vector<int>::const_iterator j = points.begin(); j != points.end(); ++j) {
+
+ float fraction = 0;
+ switch (*i) {
+ case DataType::AUDIO:
+ fraction = log_meter (*j);
+ break;
+ case DataType::MIDI:
+ fraction = *j / 127.0;
+ break;
+ }
+
+ gint const pos = height - (gint) floor (height * fraction);
+
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to (cr, 0, pos);
+ cairo_line_to (cr, 4, pos);
+ cairo_stroke (cr);
+
+ snprintf (buf, sizeof (buf), "%d", abs (*j));
+ pango_layout_set_text (layout, buf, strlen (buf));
+
+ /* we want logical extents, not ink extents here */
+
+ int tw, th;
+ pango_layout_get_pixel_size (layout, &tw, &th);
+
+ int p = pos - (th / 2);
+ p = min (p, height - th);
+ p = max (p, 0);
+
+ cairo_move_to (cr, 6, p);
+ pango_cairo_show_layout (cr, layout);
+ }
+ }
+
+ cairo_pattern_t* pattern = cairo_pattern_create_for_surface (surface);
+ MetricPatterns::iterator p;
+
+ if ((p = metric_patterns.find (w.get_name())) != metric_patterns.end()) {
+ cairo_pattern_destroy (p->second);
+ }
+
+ metric_patterns[w.get_name()] = pattern;
+
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
+ g_object_unref (layout);
+
+ return pattern;
+}
+
+gint
+GainMeter::meter_metrics_expose (GdkEventExpose *ev)
+{
+ Glib::RefPtr<Gdk::Window> win (meter_metric_area.get_window());
+ cairo_t* cr;
+
+ cr = gdk_cairo_create (win->gobj());
+
+ /* clip to expose area */
+
+ gdk_cairo_rectangle (cr, &ev->area);
+ cairo_clip (cr);
+
+ cairo_pattern_t* pattern;
+ MetricPatterns::iterator i = metric_patterns.find (meter_metric_area.get_name());
+
+ if (i == metric_patterns.end() || style_changed || dpi_changed) {
+ pattern = render_metrics (meter_metric_area, _types);
+ } else {
+ pattern = i->second;
+ }
+
+ cairo_move_to (cr, 0, 0);
+ cairo_set_source (cr, pattern);
+
+ gint width, height;
+ win->get_size (width, height);
+
+ cairo_rectangle (cr, 0, 0, width, height);
+ cairo_fill (cr);
+
+ style_changed = false;
+ dpi_changed = false;
+
+ cairo_destroy (cr);
+
+ return true;
+}
+
+boost::shared_ptr<PBD::Controllable>
+GainMeterBase::get_controllable()
+{
+ if (_amp) {
+ return _amp->gain_control();
+ } else {
+ return boost::shared_ptr<PBD::Controllable>();
+ }
+}
+
+bool
+GainMeterBase::level_meter_button_press (GdkEventButton* ev)
+{
+ return LevelMeterButtonPress (ev); /* EMIT SIGNAL */
+}
+
+void
+GainMeter::meter_configuration_changed (ChanCount c)
+{
+ _types.clear ();
+
+ for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
+ if (c.get (*i) > 0) {
+ _types.push_back (*i);
+ }