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