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