Move Gtkmm2ext widgets into libwidget
[ardour.git] / gtk2_ardour / gain_meter.cc
1 /*
2   Copyright (C) 2002 Paul Davis
3
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License as published by
6   the Free Software Foundation; either version 2 of the License, or
7   (at your option) any later version.
8
9   This program is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   GNU General Public License for more details.
13
14   You should have received a copy of the GNU General Public License
15   along with this program; if not, write to the Free Software
16   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <limits.h>
21
22 #include "ardour/amp.h"
23 #include "ardour/logmeter.h"
24 #include "ardour/route_group.h"
25 #include "ardour/session_route.h"
26 #include "ardour/dB.h"
27 #include "ardour/utils.h"
28
29 #include <pangomm.h>
30
31 #include "gtkmm/style.h"
32 #include "gdkmm/color.h"
33 #include "gtkmm2ext/utils.h"
34 #include "gtkmm2ext/gtk_ui.h"
35
36 #include "widgets/tooltips.h"
37
38 #include "pbd/fastlog.h"
39 #include "pbd/stacktrace.h"
40
41 #include "gain_meter.h"
42 #include "gui_thread.h"
43 #include "keyboard.h"
44 #include "public_editor.h"
45 #include "utils.h"
46 #include "meter_patterns.h"
47 #include "timers.h"
48 #include "ui_config.h"
49
50 #include "ardour/session.h"
51 #include "ardour/route.h"
52 #include "ardour/meter.h"
53 #include "ardour/audio_track.h"
54 #include "ardour/midi_track.h"
55 #include "ardour/dB.h"
56
57 #include "pbd/i18n.h"
58
59 using namespace ARDOUR;
60 using namespace ArdourWidgets;
61 using namespace PBD;
62 using namespace Gtkmm2ext;
63 using namespace Gtk;
64 using namespace std;
65 using Gtkmm2ext::Keyboard;
66 using namespace ArdourMeter;
67
68 static void
69 reset_cursor_to_default (Gtk::Entry* widget)
70 {
71         Glib::RefPtr<Gdk::Window> win = widget->get_text_window ();
72         if (win) {
73                 /* C++ doesn't provide a pointer argument version of this
74                    (i.e. you cannot set to NULL to get the default/parent
75                    cursor)
76                 */
77                 gdk_window_set_cursor (win->gobj(), 0);
78         }
79 }
80
81 static void
82 reset_cursor_to_default_state (Gtk::StateType, Gtk::Entry* widget)
83 {
84         reset_cursor_to_default (widget);
85 }
86
87 sigc::signal<void, ARDOUR::AutoState> GainMeterBase::ChangeGainAutomationState;
88
89 GainMeterBase::GainMeterBase (Session* s, bool horizontal, int fader_length, int fader_girth)
90         : gain_adjustment (gain_to_slider_position_with_max (1.0, Config->get_max_gain()),  // value
91                            0.0,  // lower
92                            1.0,  // upper
93                            dB_coeff_step(Config->get_max_gain()) / 10.0,  // step increment
94                            dB_coeff_step(Config->get_max_gain()))  // page increment
95         , gain_automation_state_button ("")
96         , meter_point_button (_("pre"))
97         , gain_astate_propagate (false)
98         , _data_type (DataType::AUDIO)
99 {
100         using namespace Menu_Helpers;
101
102         set_session (s);
103
104         ignore_toggle = false;
105         meter_menu = 0;
106         next_release_selects = false;
107         _width = Wide;
108
109         fader_length = rint (fader_length * UIConfiguration::instance().get_ui_scale());
110         fader_girth = rint (fader_girth * UIConfiguration::instance().get_ui_scale());
111
112         if (horizontal) {
113                 gain_slider = manage (new HSliderController (&gain_adjustment, boost::shared_ptr<PBD::Controllable>(), fader_length, fader_girth));
114         } else {
115                 gain_slider = manage (new VSliderController (&gain_adjustment, boost::shared_ptr<PBD::Controllable>(), fader_length, fader_girth));
116         }
117
118         level_meter = new LevelMeterHBox(_session);
119
120         level_meter->ButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&GainMeterBase::level_meter_button_press, this, _1));
121         meter_metric_area.signal_button_press_event().connect (sigc::mem_fun (*this, &GainMeterBase::level_meter_button_press));
122         meter_metric_area.add_events (Gdk::BUTTON_PRESS_MASK);
123
124         gain_slider->set_tweaks (ArdourFader::Tweaks(ArdourFader::NoButtonForward | ArdourFader::NoVerticalScroll));
125         gain_slider->StartGesture.connect (sigc::mem_fun (*this, &GainMeter::amp_start_touch));
126         gain_slider->StopGesture.connect (sigc::mem_fun (*this, &GainMeter::amp_stop_touch));
127         gain_slider->set_name ("GainFader");
128
129         gain_display.set_name ("MixerStripGainDisplay");
130         set_size_request_to_display_given_text (gain_display, "-80.g", 2, 6); /* note the descender */
131         gain_display.signal_activate().connect (sigc::mem_fun (*this, &GainMeter::gain_activated));
132         gain_display.signal_focus_in_event().connect (sigc::mem_fun (*this, &GainMeter::gain_focused), false);
133         gain_display.signal_focus_out_event().connect (sigc::mem_fun (*this, &GainMeter::gain_focused), false);
134         gain_display.set_alignment (0.5);
135
136         peak_display.set_name ("MixerStripPeakDisplay");
137         set_size_request_to_display_given_text (peak_display, "-80.g", 2, 6); /* note the descender */
138         max_peak = minus_infinity();
139         peak_display.set_text (_("-inf"));
140         peak_display.set_alignment (0.5);
141
142         /* stuff related to the fact that the peak display is not, in
143            fact, supposed to be a text entry.
144         */
145         peak_display.set_events (peak_display.get_events() & ~(Gdk::EventMask (Gdk::LEAVE_NOTIFY_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::POINTER_MOTION_MASK)));
146         peak_display.signal_map().connect (sigc::bind (sigc::ptr_fun (reset_cursor_to_default), &peak_display));
147         peak_display.signal_state_changed().connect (sigc::bind (sigc::ptr_fun (reset_cursor_to_default_state), &peak_display));
148         peak_display.unset_flags (Gtk::CAN_FOCUS);
149         peak_display.set_editable (false);
150
151         gain_automation_state_button.set_name ("mixer strip button");
152
153         set_tooltip (gain_automation_state_button, _("Fader automation mode"));
154
155         gain_automation_state_button.unset_flags (Gtk::CAN_FOCUS);
156
157         gain_automation_state_button.set_size_request(15, 15);
158
159         gain_astate_menu.set_name ("ArdourContextMenu");
160         gain_astate_menu.set_reserve_toggle_size(false);
161
162         meter_point_button.set_name ("mixer strip button");
163
164         set_tooltip (&meter_point_button, _("Metering point"));
165
166         meter_point_button.unset_flags (Gtk::CAN_FOCUS);
167
168         meter_point_button.set_size_request(15, 15);
169
170         meter_point_menu.set_name ("ArdourContextMenu");
171         meter_point_menu.set_reserve_toggle_size(false);
172
173         meter_point_menu.items().clear ();
174         meter_point_menu.items().push_back (MenuElem(_("Input"),
175          sigc::bind (sigc::mem_fun (*this,
176          &GainMeterBase::meter_point_clicked), (MeterPoint) MeterInput)));
177         meter_point_menu.items().push_back (MenuElem(_("Pre Fader"),
178          sigc::bind (sigc::mem_fun (*this,
179          &GainMeterBase::meter_point_clicked), (MeterPoint) MeterPreFader)));
180         meter_point_menu.items().push_back (MenuElem(_("Post Fader"),
181          sigc::bind (sigc::mem_fun (*this,
182          &GainMeterBase::meter_point_clicked), (MeterPoint) MeterPostFader)));
183         meter_point_menu.items().push_back (MenuElem(_("Output"),
184          sigc::bind (sigc::mem_fun (*this,
185          &GainMeterBase::meter_point_clicked), (MeterPoint) MeterOutput)));
186         meter_point_menu.items().push_back (MenuElem(_("Custom"),
187          sigc::bind (sigc::mem_fun (*this,
188          &GainMeterBase::meter_point_clicked), (MeterPoint) MeterCustom)));
189         meter_point_button.signal_button_press_event().connect (sigc::mem_fun (*this, &GainMeter::meter_press), false);
190
191         gain_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &GainMeterBase::fader_moved));
192         peak_display.signal_button_press_event().connect (sigc::mem_fun(*this, &GainMeterBase::peak_button_press), false);
193         peak_display.signal_button_release_event().connect (sigc::mem_fun(*this, &GainMeterBase::peak_button_release), false);
194         gain_display.signal_key_press_event().connect (sigc::mem_fun(*this, &GainMeterBase::gain_key_press), false);
195
196         ResetAllPeakDisplays.connect (sigc::mem_fun(*this, &GainMeterBase::reset_peak_display));
197         ResetRoutePeakDisplays.connect (sigc::mem_fun(*this, &GainMeterBase::reset_route_peak_display));
198         ResetGroupPeakDisplays.connect (sigc::mem_fun(*this, &GainMeterBase::reset_group_peak_display));
199         RedrawMetrics.connect (sigc::mem_fun(*this, &GainMeterBase::redraw_metrics));
200
201         UI::instance()->theme_changed.connect (sigc::mem_fun(*this, &GainMeterBase::on_theme_changed));
202         UIConfiguration::instance().ColorsChanged.connect (sigc::bind(sigc::mem_fun (*this, &GainMeterBase::color_handler), false));
203         UIConfiguration::instance().DPIReset.connect (sigc::bind(sigc::mem_fun (*this, &GainMeterBase::color_handler), true));
204 }
205
206 GainMeterBase::~GainMeterBase ()
207 {
208         delete meter_menu;
209         delete level_meter;
210 }
211
212 void
213 GainMeterBase::set_controls (boost::shared_ptr<Route> r,
214                              boost::shared_ptr<PeakMeter> pm,
215                              boost::shared_ptr<Amp> amp,
216                              boost::shared_ptr<GainControl> control)
217 {
218         connections.clear ();
219         model_connections.drop_connections ();
220
221         /* no meter and no control? nothing to do ... */
222
223         if (!pm && !control) {
224                 level_meter->set_meter (0);
225                 gain_slider->set_controllable (boost::shared_ptr<PBD::Controllable>());
226                 _meter.reset ();
227                 _amp.reset ();
228                 _route.reset ();
229                 _control.reset ();
230                 return;
231         }
232
233         _meter = pm;
234         _amp = amp;
235         _route = r;
236         _control = control;
237
238         level_meter->set_meter (pm.get());
239         gain_slider->set_controllable (_control);
240
241         if (amp) {
242                 amp->ConfigurationChanged.connect (
243                         model_connections, invalidator (*this), boost::bind (&GainMeterBase::setup_gain_adjustment, this), gui_context ()
244                         );
245         }
246
247         setup_gain_adjustment ();
248
249         if (!_route || !_route->is_auditioner()) {
250
251                 using namespace Menu_Helpers;
252
253                 gain_astate_menu.items().clear ();
254
255                 gain_astate_menu.items().push_back (MenuElem (S_("Automation|Manual"),
256                                                               sigc::bind (sigc::mem_fun (*this, &GainMeterBase::set_gain_astate), (AutoState) ARDOUR::Off)));
257                 gain_astate_menu.items().push_back (MenuElem (_("Play"),
258                                                               sigc::bind (sigc::mem_fun (*this, &GainMeterBase::set_gain_astate), (AutoState) ARDOUR::Play)));
259                 gain_astate_menu.items().push_back (MenuElem (_("Write"),
260                                                               sigc::bind (sigc::mem_fun (*this, &GainMeterBase::set_gain_astate), (AutoState) ARDOUR::Write)));
261                 gain_astate_menu.items().push_back (MenuElem (_("Touch"),
262                                                               sigc::bind (sigc::mem_fun (*this, &GainMeterBase::set_gain_astate), (AutoState) ARDOUR::Touch)));
263
264                 connections.push_back (gain_automation_state_button.signal_button_press_event().connect (sigc::mem_fun(*this, &GainMeterBase::gain_automation_state_button_event), false));
265                 connections.push_back (ChangeGainAutomationState.connect (sigc::mem_fun(*this, &GainMeterBase::set_gain_astate)));
266
267                 _control->alist()->automation_state_changed.connect (model_connections, invalidator (*this), boost::bind (&GainMeter::gain_automation_state_changed, this), gui_context());
268
269                 gain_automation_state_changed ();
270         }
271
272         _control->Changed.connect (model_connections, invalidator (*this), boost::bind (&GainMeterBase::gain_changed, this), gui_context());
273
274         gain_changed ();
275         show_gain ();
276         update_gain_sensitive ();
277
278         if (!_meter) {
279                 peak_display.hide ();
280         } else {
281                 peak_display.show ();
282         }
283 }
284
285 void
286 GainMeterBase::set_gain_astate (AutoState as)
287 {
288         if (gain_astate_propagate) {
289                 gain_astate_propagate = false;
290                 ChangeGainAutomationState (as);
291                 return;
292         }
293         _amp->set_parameter_automation_state (Evoral::Parameter (GainAutomation), as);
294 }
295
296 void
297 GainMeterBase::setup_gain_adjustment ()
298 {
299         if (!_amp) {
300                 return;
301         }
302
303         if (_previous_amp_output_streams == _amp->output_streams ()) {
304                 return;
305         }
306
307         ignore_toggle = true;
308
309         if (_amp->output_streams().n_midi() <=  _amp->output_streams().n_audio()) {
310                 _data_type = DataType::AUDIO;
311                 gain_adjustment.set_lower (GAIN_COEFF_ZERO);
312                 gain_adjustment.set_upper (GAIN_COEFF_UNITY);
313                 gain_adjustment.set_step_increment (dB_coeff_step(Config->get_max_gain()) / 10.0);
314                 gain_adjustment.set_page_increment (dB_coeff_step(Config->get_max_gain()));
315                 gain_slider->set_default_value (gain_to_slider_position_with_max (GAIN_COEFF_UNITY, Config->get_max_gain()));
316         } else {
317                 _data_type = DataType::MIDI;
318                 gain_adjustment.set_lower (0.0);
319                 gain_adjustment.set_upper (2.0);
320                 gain_adjustment.set_step_increment (1.0/128.0);
321                 gain_adjustment.set_page_increment (10.0/128.0);
322                 gain_slider->set_default_value (1.0);
323         }
324
325         ignore_toggle = false;
326
327         effective_gain_display ();
328
329         _previous_amp_output_streams = _amp->output_streams ();
330 }
331
332 void
333 GainMeterBase::hide_all_meters ()
334 {
335         level_meter->hide_meters();
336 }
337
338 void
339 GainMeter::hide_all_meters ()
340 {
341         GainMeterBase::hide_all_meters ();
342 }
343
344 void
345 GainMeterBase::setup_meters (int len)
346 {
347         int meter_width = 5;
348         uint32_t meter_channels = 0;
349         if (_meter) {
350                 meter_channels = _meter->input_streams().n_total();
351         } else if (_route) {
352                 meter_channels = _route->shared_peak_meter()->input_streams().n_total();
353         }
354
355         switch (_width) {
356                 case Wide:
357                         //meter_ticks1_area.show();
358                         //meter_ticks2_area.show();
359                         meter_metric_area.show();
360                         if (meter_channels == 1) {
361                                 meter_width = 10;
362                         }
363                         break;
364                 case Narrow:
365                         if (meter_channels > 1) {
366                                 meter_width = 4;
367                         }
368                         //meter_ticks1_area.hide();
369                         //meter_ticks2_area.hide();
370                         meter_metric_area.hide();
371                         break;
372         }
373         level_meter->setup_meters(len, meter_width);
374 }
375
376 void
377 GainMeterBase::set_type (MeterType t)
378 {
379         level_meter->set_meter_type(t);
380 }
381
382 void
383 GainMeter::setup_meters (int len)
384 {
385         switch (_width) {
386                 case Wide:
387                         {
388                                 uint32_t meter_channels = 0;
389                                 if (_meter) {
390                                         meter_channels = _meter->input_streams().n_total();
391                                 } else if (_route) {
392                                         meter_channels = _route->shared_peak_meter()->input_streams().n_total();
393                                 }
394                                 hbox.set_homogeneous(meter_channels < 7 ? true : false);
395                         }
396                         break;
397                 case Narrow:
398                         hbox.set_homogeneous(false);
399                         break;
400         }
401         GainMeterBase::setup_meters (len);
402 }
403
404 void
405 GainMeter::set_type (MeterType t)
406 {
407         GainMeterBase::set_type (t);
408 }
409
410 bool
411 GainMeterBase::gain_key_press (GdkEventKey* ev)
412 {
413         if (ARDOUR_UI_UTILS::key_is_legal_for_numeric_entry (ev->keyval)) {
414                 /* drop through to normal handling */
415                 return false;
416         }
417         /* illegal key for gain entry */
418         return true;
419 }
420
421 bool
422 GainMeterBase::peak_button_press (GdkEventButton* ev)
423 {
424         return true;
425 }
426
427 bool
428 GainMeterBase::peak_button_release (GdkEventButton* ev)
429 {
430         /* reset peak label */
431
432         if (ev->button == 1 && Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
433                 ResetAllPeakDisplays ();
434         } else if (ev->button == 1 && Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
435                 if (_route) {
436                         ResetGroupPeakDisplays (_route->route_group());
437                 }
438         } else {
439                 ResetRoutePeakDisplays (_route.get());
440         }
441
442         return true;
443 }
444
445 void
446 GainMeterBase::reset_peak_display ()
447 {
448         if (!_route) {
449                 // catch "reset all" for VCAs
450                 return;
451         }
452         _meter->reset_max();
453         level_meter->clear_meters();
454         max_peak = minus_infinity ();
455         peak_display.set_text (_("-inf"));
456         peak_display.set_name ("MixerStripPeakDisplay");
457 }
458
459 void
460 GainMeterBase::reset_route_peak_display (Route* route)
461 {
462         if (_route && _route.get() == route) {
463                 reset_peak_display ();
464         }
465 }
466
467 void
468 GainMeterBase::reset_group_peak_display (RouteGroup* group)
469 {
470         if (_route && group == _route->route_group()) {
471                 reset_peak_display ();
472         }
473 }
474
475 void
476 GainMeterBase::popup_meter_menu (GdkEventButton *ev)
477 {
478         using namespace Menu_Helpers;
479
480         if (meter_menu == 0) {
481                 meter_menu = new Gtk::Menu;
482                 MenuList& items = meter_menu->items();
483
484                 items.push_back (MenuElem ("-inf .. +0dBFS"));
485                 items.push_back (MenuElem ("-10dB .. +0dBFS"));
486                 items.push_back (MenuElem ("-4 .. +0dBFS"));
487                 items.push_back (SeparatorElem());
488                 items.push_back (MenuElem ("-inf .. -2dBFS"));
489                 items.push_back (MenuElem ("-10dB .. -2dBFS"));
490                 items.push_back (MenuElem ("-4 .. -2dBFS"));
491         }
492
493         meter_menu->popup (1, ev->time);
494 }
495
496 bool
497 GainMeterBase::gain_focused (GdkEventFocus* ev)
498 {
499         if (ev->in) {
500                 gain_display.select_region (0, -1);
501         } else {
502                 gain_display.select_region (0, 0);
503         }
504         return false;
505 }
506
507 void
508 GainMeterBase::gain_activated ()
509 {
510         float f;
511
512         // Use the user's preferred locale/LC_NUMERIC setting
513         if (sscanf (gain_display.get_text().c_str(), "%f", &f) != 1) {
514                 return;
515         }
516
517         /* clamp to displayable values */
518         if (_data_type == DataType::AUDIO) {
519                 f = min (f, 6.0f);
520                 _control->set_value (dB_to_coefficient(f), Controllable::UseGroup);
521         } else {
522                 f = min (fabs (f), 2.0f);
523                 _control->set_value (f, Controllable::UseGroup);
524         }
525
526         if (gain_display.has_focus()) {
527                 Gtk::Widget* w = gain_display.get_toplevel();
528                 if (w) {
529                         Gtk::Window* win = dynamic_cast<Gtk::Window*> (w);
530
531                         /* sigh. gtkmm doesn't wrap get_default_widget() */
532
533                         if (win) {
534                                 GtkWidget* f = gtk_window_get_default_widget (win->gobj());
535                                 if (f) {
536                                         gtk_widget_grab_focus (f);
537                                         return;
538                                 }
539                         }
540                 }
541         }
542 }
543
544 void
545 GainMeterBase::show_gain ()
546 {
547         char buf[32];
548
549         float v = gain_adjustment.get_value();
550
551         switch (_data_type) {
552         case DataType::AUDIO:
553                 if (v == 0.0) {
554                         strcpy (buf, _("-inf"));
555                 } else {
556                         snprintf (buf, sizeof (buf), "%.1f", accurate_coefficient_to_dB (slider_position_to_gain_with_max (v, Config->get_max_gain())));
557                 }
558                 break;
559         case DataType::MIDI:
560                 snprintf (buf, sizeof (buf), "%.1f", v);
561                 break;
562         }
563
564         gain_display.set_text (buf);
565 }
566
567 void
568 GainMeterBase::fader_moved ()
569 {
570         if (!ignore_toggle) {
571
572                 gain_t value;
573
574                 /* convert from adjustment range (0..1) to gain coefficient */
575
576                 if (_data_type == DataType::AUDIO) {
577                         value = slider_position_to_gain_with_max (gain_adjustment.get_value(), Config->get_max_gain());
578                 } else {
579                         value = gain_adjustment.get_value();
580                 }
581
582                 // XXX hack allow to override group
583                 // (this breaks group'ed  shift+click reset)
584                 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_R)
585                                 || Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
586                         _control->set_value (value, Controllable::InverseGroup);
587                 } else {
588                         _control->set_value (value, Controllable::UseGroup);
589                 }
590         }
591
592         show_gain ();
593 }
594
595 void
596 GainMeterBase::effective_gain_display ()
597 {
598         gain_t fader_position = 0;
599
600         switch (_data_type) {
601         case DataType::AUDIO:
602                 /* the position of the fader should reflect any master gain,
603                  * not just the control's own inherent value
604                  */
605                 fader_position = gain_to_slider_position_with_max (_control->get_value(), Config->get_max_gain());
606                 break;
607         case DataType::MIDI:
608                 fader_position = _control->get_value ();
609                 break;
610         }
611
612         if (gain_adjustment.get_value() != fader_position) {
613                 ignore_toggle = true;
614                 gain_adjustment.set_value (fader_position);
615                 ignore_toggle = false;
616         }
617 }
618
619 void
620 GainMeterBase::gain_changed ()
621 {
622         ENSURE_GUI_THREAD (*this, &GainMeterBase::gain_automation_state_changed);
623         effective_gain_display ();
624 }
625
626 void
627 GainMeterBase::set_meter_strip_name (const char * name)
628 {
629         char tmp[256];
630         meter_metric_area.set_name (name);
631         sprintf(tmp, "Mark%sLeft", name);
632         meter_ticks1_area.set_name (tmp);
633         sprintf(tmp, "Mark%sRight", name);
634         meter_ticks2_area.set_name (tmp);
635 }
636
637 void
638 GainMeterBase::set_fader_name (const char * name)
639 {
640         gain_slider->set_name (name);
641 }
642
643 void
644 GainMeterBase::update_gain_sensitive ()
645 {
646         bool x = !(_control->alist()->automation_state() & Play);
647         static_cast<ArdourWidgets::SliderController*>(gain_slider)->set_sensitive (x);
648 }
649
650 gint
651 GainMeterBase::meter_press(GdkEventButton* ev)
652 {
653         if (!_route) {
654                 return false;
655         }
656         if (!ignore_toggle) {
657                 switch (ev->button) {
658                         case 1:
659                                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
660
661                                         /* Primary+Tertiary-click applies change to all routes */
662
663                                         meter_point_change_target = MeterPointChangeAll;
664
665                                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
666
667                                         /* Primary-click: apply change to all routes in group */
668
669                                         meter_point_change_target = MeterPointChangeGroup;
670
671                                 } else {
672
673                                         /* click: change just this route */
674
675                                         meter_point_change_target = MeterPointChangeSingle;
676                                 }
677                                 Gtkmm2ext::anchored_menu_popup(&meter_point_menu,
678                                                              &meter_point_button,
679                                                            "", 1, ev->time);
680                                 break;
681                         default:
682                                 break;
683                 }
684         }
685         return true;
686 }
687
688 void
689 GainMeterBase::set_meter_point (Route& route, MeterPoint mp)
690 {
691         route.set_meter_point (mp);
692 }
693
694 void
695 GainMeterBase::set_route_group_meter_point (Route& route, MeterPoint mp)
696 {
697         RouteGroup* route_group;
698
699         if ((route_group = route.route_group ()) != 0) {
700                 route_group->foreach_route (boost::bind (&Route::set_meter_point, _1, mp, false));
701         } else {
702                 route.set_meter_point (mp);
703         }
704 }
705
706 void
707 GainMeterBase::meter_point_clicked (MeterPoint mp)
708 {
709         if (_route) {
710                 switch (meter_point_change_target) {
711                         case MeterPointChangeAll:
712                                 _session->foreach_route (this, &GainMeterBase::set_meter_point, mp);
713                                 break;
714                         case MeterPointChangeGroup:
715                                 set_route_group_meter_point (*_route, mp);
716                                 break;
717                         case MeterPointChangeSingle:
718                                 _route->set_meter_point (mp);
719                                 break;
720                 }
721         }
722 }
723
724 void
725 GainMeterBase::amp_start_touch ()
726 {
727         _control->start_touch (_control->session().transport_frame());
728 }
729
730 void
731 GainMeterBase::amp_stop_touch ()
732 {
733         _control->stop_touch (false, _control->session().transport_frame());
734         effective_gain_display ();
735 }
736
737 gint
738 GainMeterBase::gain_automation_state_button_event (GdkEventButton *ev)
739 {
740         if (ev->type == GDK_BUTTON_RELEASE) {
741                 return TRUE;
742         }
743
744         switch (ev->button) {
745                 case 1:
746                         gain_astate_propagate = Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier));
747                         Gtkmm2ext::anchored_menu_popup(&gain_astate_menu,
748                                                        &gain_automation_state_button,
749                                                        "", 1, ev->time);
750                         break;
751                 default:
752                         break;
753         }
754
755         return TRUE;
756 }
757
758
759 string
760 GainMeterBase::astate_string (AutoState state)
761 {
762         return _astate_string (state, false);
763 }
764
765 string
766 GainMeterBase::short_astate_string (AutoState state)
767 {
768         return _astate_string (state, true);
769 }
770
771 string
772 GainMeterBase::_astate_string (AutoState state, bool shrt)
773 {
774         string sstr;
775
776         switch (state) {
777         case ARDOUR::Off:
778                 sstr = (shrt ? "M" : _("M"));
779                 break;
780         case Play:
781                 sstr = (shrt ? "P" : _("P"));
782                 break;
783         case Touch:
784                 sstr = (shrt ? "T" : _("T"));
785                 break;
786         case Write:
787                 sstr = (shrt ? "W" : _("W"));
788                 break;
789         }
790
791         return sstr;
792 }
793
794 void
795 GainMeterBase::gain_automation_state_changed ()
796 {
797         ENSURE_GUI_THREAD (*this, &GainMeterBase::gain_automation_state_changed);
798
799         switch (_width) {
800         case Wide:
801                 gain_automation_state_button.set_text (astate_string(_control->alist()->automation_state()));
802                 break;
803         case Narrow:
804                 gain_automation_state_button.set_text (short_astate_string(_control->alist()->automation_state()));
805                 break;
806         }
807
808         const bool automation_watch_required = (_control->alist()->automation_state() != ARDOUR::Off);
809
810         if (gain_automation_state_button.get_active() != automation_watch_required) {
811                 ignore_toggle = true;
812                 gain_automation_state_button.set_active (automation_watch_required);
813                 ignore_toggle = false;
814         }
815
816         update_gain_sensitive ();
817
818         gain_watching.disconnect();
819 }
820
821 const ChanCount
822 GainMeterBase::meter_channels() const
823 {
824                 if (_meter) { return _meter->input_streams(); }
825                 else { return ChanCount(); }
826 }
827 void
828 GainMeterBase::update_meters()
829 {
830         char buf[32];
831         float mpeak = level_meter->update_meters();
832
833         if (mpeak > max_peak) {
834                 max_peak = mpeak;
835                 if (mpeak <= -200.0f) {
836                         peak_display.set_text (_("-inf"));
837                 } else {
838                         snprintf (buf, sizeof(buf), "%.1f", mpeak);
839                         peak_display.set_text (buf);
840                 }
841         }
842         if (mpeak >= UIConfiguration::instance().get_meter_peak()) {
843                 peak_display.set_name ("MixerStripPeakDisplayPeak");
844         }
845 }
846
847 void GainMeterBase::color_handler(bool /*dpi*/)
848 {
849         setup_meters();
850 }
851
852 void
853 GainMeterBase::set_width (Width w, int len)
854 {
855         _width = w;
856         int meter_width = 5;
857         if (_width == Wide && _route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
858                 meter_width = 10;
859         }
860         level_meter->setup_meters(len, meter_width);
861 }
862
863
864 void
865 GainMeterBase::on_theme_changed()
866 {
867 }
868
869 void
870 GainMeterBase::redraw_metrics()
871 {
872         meter_metric_area.queue_draw ();
873         meter_ticks1_area.queue_draw ();
874         meter_ticks2_area.queue_draw ();
875 }
876
877 #define PX_SCALE(pxmin, dflt) rint(std::max((double)pxmin, (double)dflt * UIConfiguration::instance().get_ui_scale()))
878
879 GainMeter::GainMeter (Session* s, int fader_length)
880         : GainMeterBase (s, false, fader_length, 24)
881         , gain_display_box(true, 0)
882         , hbox(true, 2)
883 {
884         if (gain_display.get_parent()) {
885                 gain_display.get_parent()->remove (gain_display);
886         }
887         gain_display_box.pack_start (gain_display, true, true);
888
889         if (peak_display.get_parent()) {
890                 peak_display.get_parent()->remove (gain_display);
891         }
892         gain_display_box.pack_start (peak_display, true, true);
893
894         meter_metric_area.set_name ("AudioTrackMetrics");
895         meter_metric_area.set_size_request(PX_SCALE(24, 24), -1);
896
897         gain_automation_state_button.set_name ("mixer strip button");
898
899         set_tooltip (gain_automation_state_button, _("Fader automation mode"));
900
901         gain_automation_state_button.unset_flags (Gtk::CAN_FOCUS);
902
903         gain_automation_state_button.set_size_request (PX_SCALE(12, 15), PX_SCALE(12, 15));
904
905         fader_vbox.set_spacing (0);
906         fader_vbox.pack_start (*gain_slider, true, true);
907
908         fader_alignment.set (0.5, 0.5, 0.0, 1.0);
909         fader_alignment.add (fader_vbox);
910
911         hbox.pack_start (fader_alignment, true, true);
912
913         set_spacing (PX_SCALE(2, 2));
914
915         pack_start (gain_display_box, Gtk::PACK_SHRINK);
916         pack_start (hbox, true, true);
917
918         meter_alignment.set (0.5, 0.5, 0.0, 1.0);
919         meter_alignment.add (*level_meter);
920
921         meter_metric_area.signal_expose_event().connect (
922                 sigc::mem_fun(*this, &GainMeter::meter_metrics_expose));
923
924         meter_ticks1_area.set_size_request (PX_SCALE(3, 3), -1);
925         meter_ticks2_area.set_size_request (PX_SCALE(3, 3), -1);
926
927         meter_ticks1_area.signal_expose_event().connect (
928                         sigc::mem_fun(*this, &GainMeter::meter_ticks1_expose));
929         meter_ticks2_area.signal_expose_event().connect (
930                         sigc::mem_fun(*this, &GainMeter::meter_ticks2_expose));
931
932         meter_hbox.pack_start (meter_ticks1_area, false, false);
933         meter_hbox.pack_start (meter_alignment, false, false);
934         meter_hbox.pack_start (meter_ticks2_area, false, false);
935         meter_hbox.pack_start (meter_metric_area, false, false);
936 }
937 #undef PX_SCALE
938
939 GainMeter::~GainMeter () { }
940
941 void
942 GainMeter::set_controls (boost::shared_ptr<Route> r,
943                          boost::shared_ptr<PeakMeter> meter,
944                          boost::shared_ptr<Amp> amp,
945                          boost::shared_ptr<GainControl> control)
946 {
947         if (meter_hbox.get_parent()) {
948                 hbox.remove (meter_hbox);
949         }
950
951 //      if (gain_automation_state_button.get_parent()) {
952 //              fader_vbox->remove (gain_automation_state_button);
953 //      }
954
955         GainMeterBase::set_controls (r, meter, amp, control);
956
957         if (_meter) {
958                 _meter->ConfigurationChanged.connect (
959                         model_connections, invalidator (*this), boost::bind (&GainMeter::meter_configuration_changed, this, _1), gui_context()
960                         );
961                 _meter->TypeChanged.connect (
962                         model_connections, invalidator (*this), boost::bind (&GainMeter::meter_type_changed, this, _1), gui_context()
963                         );
964
965                 meter_configuration_changed (_meter->input_streams ());
966         }
967
968
969         if (_route) {
970                 _route->active_changed.connect (model_connections, invalidator (*this), boost::bind (&GainMeter::route_active_changed, this), gui_context ());
971                 hbox.pack_start (meter_hbox, true, true);
972                 meter_hbox.show ();
973         }
974
975 //      if (r && !r->is_auditioner()) {
976 //              fader_vbox->pack_start (gain_automation_state_button, false, false, 0);
977 //      }
978
979         gain_display_box.show ();
980         gain_display.show ();
981         gain_slider->show ();
982         fader_vbox.show ();
983         fader_alignment.show ();
984         hbox.show ();
985         setup_meters ();
986 }
987
988 int
989 GainMeter::get_gm_width ()
990 {
991         Gtk::Requisition sz;
992         int min_w = 0;
993         sz.width = 0;
994         meter_metric_area.size_request (sz);
995         min_w += sz.width;
996         level_meter->size_request (sz);
997         min_w += sz.width;
998
999         fader_alignment.size_request (sz);
1000         if (_width == Wide)
1001                 return max(sz.width * 2, min_w * 2) + 6;
1002         else
1003                 return sz.width + min_w + 6;
1004
1005 }
1006
1007 gint
1008 GainMeter::meter_metrics_expose (GdkEventExpose *ev)
1009 {
1010         if (!_route) {
1011                 if (_types.empty()) { _types.push_back(DataType::AUDIO); }
1012                 return meter_expose_metrics(ev, MeterPeak, _types, &meter_metric_area);
1013         }
1014         return meter_expose_metrics(ev, _route->meter_type(), _types, &meter_metric_area);
1015 }
1016
1017 gint
1018 GainMeter::meter_ticks1_expose (GdkEventExpose *ev)
1019 {
1020         if (!_route) {
1021                 if (_types.empty()) { _types.push_back(DataType::AUDIO); }
1022                 return meter_expose_ticks(ev, MeterPeak, _types, &meter_ticks1_area);
1023         }
1024         return meter_expose_ticks(ev, _route->meter_type(), _types, &meter_ticks1_area);
1025 }
1026
1027 gint
1028 GainMeter::meter_ticks2_expose (GdkEventExpose *ev)
1029 {
1030         if (!_route) {
1031                 if (_types.empty()) { _types.push_back(DataType::AUDIO); }
1032                 return meter_expose_ticks(ev, MeterPeak, _types, &meter_ticks2_area);
1033         }
1034         return meter_expose_ticks(ev, _route->meter_type(), _types, &meter_ticks2_area);
1035 }
1036
1037 void
1038 GainMeter::on_style_changed (const Glib::RefPtr<Gtk::Style>&)
1039 {
1040         gain_display.queue_draw();
1041         peak_display.queue_draw();
1042 }
1043
1044 boost::shared_ptr<PBD::Controllable>
1045 GainMeterBase::get_controllable()
1046 {
1047         if (_amp) {
1048                 return _control;
1049         } else {
1050                 return boost::shared_ptr<PBD::Controllable>();
1051         }
1052 }
1053
1054 bool
1055 GainMeterBase::level_meter_button_press (GdkEventButton* ev)
1056 {
1057         return static_cast<bool>(LevelMeterButtonPress (ev)); /* EMIT SIGNAL */
1058 }
1059
1060 void
1061 GainMeter::meter_configuration_changed (ChanCount c)
1062 {
1063         int type = 0;
1064         _types.clear ();
1065
1066         for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
1067                 if (c.get (*i) > 0) {
1068                         _types.push_back (*i);
1069                         type |= 1 << (*i);
1070                 }
1071         }
1072
1073         if (_route
1074                         && boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
1075                         && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0
1076                         ) {
1077                 if (_route->active()) {
1078                         set_meter_strip_name ("AudioBusMetrics");
1079                 } else {
1080                         set_meter_strip_name ("AudioBusMetricsInactive");
1081                 }
1082         }
1083         else if (
1084                            (type == (1 << DataType::MIDI))
1085                         || (_route && boost::dynamic_pointer_cast<MidiTrack>(_route))
1086                         ) {
1087                 if (!_route || _route->active()) {
1088                         set_meter_strip_name ("MidiTrackMetrics");
1089                 } else {
1090                         set_meter_strip_name ("MidiTrackMetricsInactive");
1091                 }
1092         }
1093         else if (type == (1 << DataType::AUDIO)) {
1094                 if (!_route || _route->active()) {
1095                         set_meter_strip_name ("AudioTrackMetrics");
1096                 } else {
1097                         set_meter_strip_name ("AudioTrackMetricsInactive");
1098                 }
1099         } else {
1100                 if (!_route || _route->active()) {
1101                         set_meter_strip_name ("AudioMidiTrackMetrics");
1102                 } else {
1103                         set_meter_strip_name ("AudioMidiTrackMetricsInactive");
1104                 }
1105         }
1106
1107         setup_meters();
1108         meter_clear_pattern_cache(4);
1109         on_style_changed(Glib::RefPtr<Gtk::Style>());
1110 }
1111
1112 void
1113 GainMeter::route_active_changed ()
1114 {
1115         if (_meter) {
1116                 meter_configuration_changed (_meter->input_streams ());
1117         }
1118 }
1119
1120 void
1121 GainMeter::meter_type_changed (MeterType t)
1122 {
1123         _route->set_meter_type(t);
1124         RedrawMetrics();
1125 }