950071ba1300083d72ec93243a6b0e139adceeca
[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/io.h"
24 #include "ardour/route.h"
25 #include "ardour/route_group.h"
26 #include "ardour/session.h"
27 #include "ardour/session_route.h"
28 #include "ardour/dB.h"
29
30 #include <gtkmm/style.h>
31 #include <gdkmm/color.h>
32 #include <gtkmm2ext/utils.h>
33 #include <gtkmm2ext/fastmeter.h>
34 #include <gtkmm2ext/stop_signal.h>
35 #include <gtkmm2ext/barcontroller.h>
36 #include <gtkmm2ext/gtk_ui.h>
37 #include "midi++/manager.h"
38 #include "pbd/fastlog.h"
39 #include "pbd/stacktrace.h"
40
41 #include "ardour_ui.h"
42 #include "gain_meter.h"
43 #include "utils.h"
44 #include "logmeter.h"
45 #include "gui_thread.h"
46 #include "keyboard.h"
47 #include "public_editor.h"
48
49 #include "ardour/session.h"
50 #include "ardour/route.h"
51 #include "ardour/meter.h"
52
53 #include "i18n.h"
54
55 using namespace ARDOUR;
56 using namespace PBD;
57 using namespace Gtkmm2ext;
58 using namespace Gtk;
59 using namespace std;
60 using Gtkmm2ext::Keyboard;
61
62 sigc::signal<void> GainMeterBase::ResetAllPeakDisplays;
63 sigc::signal<void,RouteGroup*> GainMeterBase::ResetGroupPeakDisplays;
64
65 map<string,Glib::RefPtr<Gdk::Pixmap> > GainMeter::metric_pixmaps;
66 Glib::RefPtr<Gdk::Pixbuf> GainMeter::slider;
67
68
69 void
70 GainMeter::setup_slider_pix ()
71 {
72         if ((slider = ::get_icon ("fader_belt")) == 0) {
73                 throw failed_constructor();
74         }
75 }
76
77 GainMeterBase::GainMeterBase (Session& s,
78                               const Glib::RefPtr<Gdk::Pixbuf>& pix,
79                               bool horizontal,
80                               int fader_length)
81         : _session (s)
82           // 0.781787 is the value needed for gain to be set to 0.
83         , gain_adjustment (0.781787, 0.0, 1.0, 0.01, 0.1)
84         , gain_automation_style_button ("")
85         , gain_automation_state_button ("")
86         , dpi_changed (false)
87         , _is_midi (false)
88
89 {
90         using namespace Menu_Helpers;
91
92         ignore_toggle = false;
93         meter_menu = 0;
94         next_release_selects = false;
95         style_changed = true;
96         _width = Wide;
97
98         if (horizontal) {
99                 gain_slider = manage (new HSliderController (pix,       
100                                                              &gain_adjustment,
101                                                              fader_length,
102                                                              false));
103         } else {
104                 gain_slider = manage (new VSliderController (pix,
105                                                              &gain_adjustment,
106                                                              fader_length,
107                                                              false));
108         }
109
110         level_meter = new LevelMeter(_session);
111
112         gain_slider->signal_button_press_event().connect (sigc::mem_fun(*this, &GainMeter::start_gain_touch));
113         gain_slider->signal_button_release_event().connect (sigc::mem_fun(*this, &GainMeter::end_gain_touch));
114         gain_slider->set_name ("GainFader");
115
116         gain_display.set_name ("MixerStripGainDisplay");
117         gain_display.set_has_frame (false);
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
123         peak_display.set_name ("MixerStripPeakDisplay");
124 //      peak_display.set_has_frame (false);
125 //      peak_display.set_editable (false);
126         set_size_request_to_display_given_text  (peak_display, "-80.g", 2, 6); /* note the descender */
127         max_peak = minus_infinity();
128         peak_display.set_label (_("-inf"));
129         peak_display.unset_flags (Gtk::CAN_FOCUS);
130
131         gain_automation_style_button.set_name ("MixerAutomationModeButton");
132         gain_automation_state_button.set_name ("MixerAutomationPlaybackButton");
133
134         ARDOUR_UI::instance()->tooltips().set_tip (gain_automation_state_button, _("Fader automation mode"));
135         ARDOUR_UI::instance()->tooltips().set_tip (gain_automation_style_button, _("Fader automation type"));
136
137         gain_automation_style_button.unset_flags (Gtk::CAN_FOCUS);
138         gain_automation_state_button.unset_flags (Gtk::CAN_FOCUS);
139
140         gain_automation_state_button.set_size_request(15, 15);
141         gain_automation_style_button.set_size_request(15, 15);
142
143         gain_astyle_menu.items().push_back (MenuElem (_("Trim")));
144         gain_astyle_menu.items().push_back (MenuElem (_("Abs")));
145
146         gain_astate_menu.set_name ("ArdourContextMenu");
147         gain_astyle_menu.set_name ("ArdourContextMenu");
148
149         gain_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &GainMeterBase::gain_adjusted));
150         peak_display.signal_button_release_event().connect (sigc::mem_fun(*this, &GainMeterBase::peak_button_release), false);
151         gain_display.signal_key_press_event().connect (sigc::mem_fun(*this, &GainMeterBase::gain_key_press), false);
152
153         ResetAllPeakDisplays.connect (sigc::mem_fun(*this, &GainMeterBase::reset_peak_display));
154         ResetGroupPeakDisplays.connect (sigc::mem_fun(*this, &GainMeterBase::reset_group_peak_display));
155
156         UI::instance()->theme_changed.connect (sigc::mem_fun(*this, &GainMeterBase::on_theme_changed));
157         ColorsChanged.connect (sigc::bind(sigc::mem_fun (*this, &GainMeterBase::color_handler), false));
158         DPIReset.connect (sigc::bind(sigc::mem_fun (*this, &GainMeterBase::color_handler), true));
159 }
160
161 GainMeterBase::~GainMeterBase ()
162 {
163         delete meter_menu;
164         delete level_meter;
165 }
166
167 void
168 GainMeterBase::set_controls (boost::shared_ptr<Route> r,
169                              boost::shared_ptr<PeakMeter> pm,
170                              boost::shared_ptr<Amp> amp)
171 {
172         connections.clear ();
173
174         if (!pm && !amp) {
175                 level_meter->set_meter (0);
176                 gain_slider->set_controllable (boost::shared_ptr<PBD::Controllable>());
177                 _meter.reset ();
178                 _amp.reset ();
179                 _route.reset ();
180                 return;
181         }
182
183         _meter = pm;
184         _amp = amp;
185         _route = r;
186
187         level_meter->set_meter (pm.get());
188         gain_slider->set_controllable (amp->gain_control());
189
190         if (!_route || _route->output()->n_ports().n_midi() == 0) {
191                 _is_midi = false;
192                 gain_adjustment.set_lower (0.0);
193                 gain_adjustment.set_upper (1.0);
194                 gain_adjustment.set_step_increment (0.01);
195                 gain_adjustment.set_page_increment (0.1);
196         } else {
197                 _is_midi = true;
198                 gain_adjustment.set_lower (0.0);
199                 gain_adjustment.set_upper (2.0);
200                 gain_adjustment.set_step_increment (0.05);
201                 gain_adjustment.set_page_increment (0.1);
202         }
203
204         if (!_route || !_route->is_hidden()) {
205
206                 using namespace Menu_Helpers;
207
208                 gain_astate_menu.items().clear ();
209
210                 gain_astate_menu.items().push_back (MenuElem (_("Manual"),
211                                                               sigc::bind (sigc::mem_fun (*(amp.get()), &Automatable::set_parameter_automation_state),
212                                                                     Evoral::Parameter(GainAutomation), (AutoState) Off)));
213                 gain_astate_menu.items().push_back (MenuElem (_("Play"),
214                                                               sigc::bind (sigc::mem_fun (*(amp.get()), &Automatable::set_parameter_automation_state),
215                                                                     Evoral::Parameter(GainAutomation), (AutoState) Play)));
216                 gain_astate_menu.items().push_back (MenuElem (_("Write"),
217                                                               sigc::bind (sigc::mem_fun (*(amp.get()), &Automatable::set_parameter_automation_state),
218                                                                     Evoral::Parameter(GainAutomation), (AutoState) Write)));
219                 gain_astate_menu.items().push_back (MenuElem (_("Touch"),
220                                                               sigc::bind (sigc::mem_fun (*(amp.get()), &Automatable::set_parameter_automation_state),
221                                                                     Evoral::Parameter(GainAutomation), (AutoState) Touch)));
222
223                 connections.push_back (gain_automation_style_button.signal_button_press_event().connect (sigc::mem_fun(*this, &GainMeterBase::gain_automation_style_button_event), false));
224                 connections.push_back (gain_automation_state_button.signal_button_press_event().connect (sigc::mem_fun(*this, &GainMeterBase::gain_automation_state_button_event), false));
225
226                 boost::shared_ptr<AutomationControl> gc = amp->gain_control();
227
228                 connections.push_back (gc->alist()->automation_state_changed.connect (sigc::mem_fun(*this, &GainMeter::gain_automation_state_changed)));
229                 connections.push_back (gc->alist()->automation_style_changed.connect (sigc::mem_fun(*this, &GainMeter::gain_automation_style_changed)));
230
231                 gain_automation_state_changed ();
232         }
233
234         connections.push_back (amp->gain_control()->Changed.connect (sigc::mem_fun (*this, &GainMeterBase::gain_changed)));
235
236         gain_changed ();
237         show_gain ();
238         update_gain_sensitive ();
239 }
240
241 void
242 GainMeterBase::hide_all_meters ()
243 {
244         level_meter->hide_meters();
245 }
246
247 void
248 GainMeter::hide_all_meters ()
249 {
250         bool remove_metric_area = false;
251
252         GainMeterBase::hide_all_meters ();
253
254         if (remove_metric_area) {
255                 if (meter_metric_area.get_parent()) {
256                         level_meter->remove (meter_metric_area);
257                 }
258         }
259 }
260
261 void
262 GainMeterBase::setup_meters (int len)
263 {
264         level_meter->setup_meters(len, 5);
265 }
266
267 void
268 GainMeter::setup_meters (int len)
269 {
270         if (!meter_metric_area.get_parent()) {
271                 level_meter->pack_end (meter_metric_area, false, false);
272                 meter_metric_area.show_all ();
273         }
274         GainMeterBase::setup_meters (len);
275 }
276
277 bool
278 GainMeterBase::gain_key_press (GdkEventKey* ev)
279 {
280         if (key_is_legal_for_numeric_entry (ev->keyval)) {
281                 /* drop through to normal handling */
282                 return false;
283         }
284         /* illegal key for gain entry */
285         return true;
286 }
287
288 bool
289 GainMeterBase::peak_button_release (GdkEventButton* ev)
290 {
291         /* reset peak label */
292
293         if (ev->button == 1 && Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
294                 ResetAllPeakDisplays ();
295         } else if (ev->button == 1 && Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
296                 if (_route) {
297                         ResetGroupPeakDisplays (_route->route_group());
298                 }
299         } else {
300                 reset_peak_display ();
301         }
302
303         return true;
304 }
305
306 void
307 GainMeterBase::reset_peak_display ()
308 {
309         _meter->reset_max();
310         level_meter->clear_meters();
311         max_peak = -INFINITY;
312         peak_display.set_label (_("-Inf"));
313         peak_display.set_name ("MixerStripPeakDisplay");
314 }
315
316 void
317 GainMeterBase::reset_group_peak_display (RouteGroup* group)
318 {
319         if (_route && group == _route->route_group()) {
320                 reset_peak_display ();
321                 }
322 }
323
324 void
325 GainMeterBase::popup_meter_menu (GdkEventButton *ev)
326 {
327         using namespace Menu_Helpers;
328
329         if (meter_menu == 0) {
330                 meter_menu = new Gtk::Menu;
331                 MenuList& items = meter_menu->items();
332
333                 items.push_back (MenuElem ("-inf .. +0dBFS"));
334                 items.push_back (MenuElem ("-10dB .. +0dBFS"));
335                 items.push_back (MenuElem ("-4 .. +0dBFS"));
336                 items.push_back (SeparatorElem());
337                 items.push_back (MenuElem ("-inf .. -2dBFS"));
338                 items.push_back (MenuElem ("-10dB .. -2dBFS"));
339                 items.push_back (MenuElem ("-4 .. -2dBFS"));
340         }
341
342         meter_menu->popup (1, ev->time);
343 }
344
345 bool
346 GainMeterBase::gain_focused (GdkEventFocus* ev)
347 {
348         if (ev->in) {
349                 gain_display.select_region (0, -1);
350         } else {
351                 gain_display.select_region (0, 0);
352         }
353         return false;
354 }
355
356 void
357 GainMeterBase::gain_activated ()
358 {
359         float f;
360
361         if (sscanf (gain_display.get_text().c_str(), "%f", &f) == 1) {
362
363                 /* clamp to displayable values */
364
365                 f = min (f, 6.0f);
366
367                 _amp->set_gain (dB_to_coefficient(f), this);
368
369                 if (gain_display.has_focus()) {
370                         PublicEditor::instance().reset_focus();
371                 }
372         }
373 }
374
375 void
376 GainMeterBase::show_gain ()
377 {
378         char buf[32];
379
380         float v = gain_adjustment.get_value();
381
382         if (!_is_midi) {
383                 if (v == 0.0) {
384                         strcpy (buf, _("-inf"));
385                 } else {
386                         snprintf (buf, sizeof (buf), "%.1f", accurate_coefficient_to_dB (slider_position_to_gain (v)));
387                 }
388         } else {
389                 snprintf (buf, sizeof (buf), "%.1f", v);
390         }
391
392         gain_display.set_text (buf);
393 }
394
395 void
396 GainMeterBase::gain_adjusted ()
397 {
398         if (!ignore_toggle) {
399                 if (_route) {
400                         if (_route->amp() == _amp) {
401                                 if (_is_midi) {
402                                         _route->set_gain (gain_adjustment.get_value(), this);
403                                 } else {
404                                         _route->set_gain (slider_position_to_gain (gain_adjustment.get_value()), this);
405                                 }
406                         } else {
407                                 _amp->set_gain (slider_position_to_gain (gain_adjustment.get_value()), this);
408                         }
409                 }
410         }
411
412         show_gain ();
413 }
414
415 void
416 GainMeterBase::effective_gain_display ()
417 {
418         gfloat value;
419
420         if (!_route || _route->output()->n_ports().n_midi() == 0) {
421                 value = gain_to_slider_position (_amp->gain());
422         } else {
423                 value = _amp->gain ();
424         }
425
426         //cerr << this << " for " << _io->name() << " EGAIN = " << value
427         //              << " AGAIN = " << gain_adjustment.get_value () << endl;
428         // stacktrace (cerr, 20);
429
430         if (gain_adjustment.get_value() != value) {
431                 ignore_toggle = true;
432                 gain_adjustment.set_value (value);
433                 ignore_toggle = false;
434         }
435 }
436
437 void
438 GainMeterBase::gain_changed ()
439 {
440         Gtkmm2ext::UI::instance()->call_slot (boost::bind (&GainMeterBase::effective_gain_display, this));
441 }
442
443 void
444 GainMeterBase::set_meter_strip_name (const char * name)
445 {
446         meter_metric_area.set_name (name);
447 }
448
449 void
450 GainMeterBase::set_fader_name (const char * name)
451 {
452         gain_slider->set_name (name);
453 }
454
455 void
456 GainMeterBase::update_gain_sensitive ()
457 {
458         bool x = !(_amp->gain_control()->alist()->automation_state() & Play);
459         static_cast<Gtkmm2ext::SliderController*>(gain_slider)->set_sensitive (x);
460 }
461
462 static MeterPoint
463 next_meter_point (MeterPoint mp)
464 {
465         switch (mp) {
466         case MeterInput:
467                 return MeterPreFader;
468                 break;
469
470         case MeterPreFader:
471                 return MeterPostFader;
472                 break;
473
474         case MeterPostFader:
475                 return MeterCustom;
476                 break;
477
478         case MeterCustom:
479                 return MeterInput;              
480                 break;
481         }
482
483         /*NOTREACHED*/
484         return MeterInput;
485 }
486
487 gint
488 GainMeterBase::meter_press(GdkEventButton* ev)
489 {
490         wait_for_release = false;
491
492         if (!_route) {
493                 return FALSE;
494         }
495
496         if (!ignore_toggle) {
497
498                 if (Keyboard::is_context_menu_event (ev)) {
499
500                         // no menu at this time.
501
502                 } else {
503
504                         if (Keyboard::is_button2_event(ev)) {
505
506                                 // Primary-button2 click is the midi binding click
507                                 // button2-click is "momentary"
508
509                                 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier))) {
510                                         wait_for_release = true;
511                                         old_meter_point = _route->meter_point ();
512                                 }
513                         }
514
515                         if (_route && (ev->button == 1 || Keyboard::is_button2_event (ev))) {
516
517                                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
518
519                                         /* Primary+Tertiary-click applies change to all routes */
520
521                                         _session.foreach_route (this, &GainMeterBase::set_meter_point, next_meter_point (_route->meter_point()));
522
523
524                                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
525
526                                         /* Primary-click: solo mix group.
527                                            NOTE: Primary-button2 is MIDI learn.
528                                         */
529
530                                         if (ev->button == 1) {
531                                                 set_mix_group_meter_point (*_route, next_meter_point (_route->meter_point()));
532                                         }
533
534                                 } else {
535
536                                         /* click: change just this route */
537
538                                         // XXX no undo yet
539
540                                         _route->set_meter_point (next_meter_point (_route->meter_point()), this);
541                                 }
542                         }
543                 }
544         }
545
546         return true;
547
548 }
549
550 gint
551 GainMeterBase::meter_release(GdkEventButton*)
552 {
553         if(!ignore_toggle){
554                 if (wait_for_release){
555                         wait_for_release = false;
556
557                         if (_route) {
558                                 set_meter_point (*_route, old_meter_point);
559                         }
560                 }
561         }
562
563         return true;
564 }
565
566 void
567 GainMeterBase::set_meter_point (Route& route, MeterPoint mp)
568 {
569         route.set_meter_point (mp, this);
570 }
571
572 void
573 GainMeterBase::set_mix_group_meter_point (Route& route, MeterPoint mp)
574 {
575         RouteGroup* mix_group;
576
577         if((mix_group = route.route_group()) != 0){
578                 mix_group->apply (&Route::set_meter_point, mp, this);
579         } else {
580                 route.set_meter_point (mp, this);
581         }
582 }
583
584 void
585 GainMeterBase::meter_point_clicked ()
586 {
587         if (_route) {
588                 /* WHAT? */
589         }
590 }
591
592 gint
593 GainMeterBase::start_gain_touch (GdkEventButton*)
594 {
595         _amp->gain_control()->start_touch ();
596         return FALSE;
597 }
598
599 gint
600 GainMeterBase::end_gain_touch (GdkEventButton*)
601 {
602         _amp->gain_control()->stop_touch ();
603         return FALSE;
604 }
605
606 gint
607 GainMeterBase::gain_automation_state_button_event (GdkEventButton *ev)
608 {
609         if (ev->type == GDK_BUTTON_RELEASE) {
610                 return TRUE;
611         }
612
613         switch (ev->button) {
614                 case 1:
615                         gain_astate_menu.popup (1, ev->time);
616                         break;
617                 default:
618                         break;
619         }
620
621         return TRUE;
622 }
623
624 gint
625 GainMeterBase::gain_automation_style_button_event (GdkEventButton *ev)
626 {
627         if (ev->type == GDK_BUTTON_RELEASE) {
628                 return TRUE;
629         }
630
631         switch (ev->button) {
632         case 1:
633                 gain_astyle_menu.popup (1, ev->time);
634                 break;
635         default:
636                 break;
637         }
638         return TRUE;
639 }
640
641 string
642 GainMeterBase::astate_string (AutoState state)
643 {
644         return _astate_string (state, false);
645 }
646
647 string
648 GainMeterBase::short_astate_string (AutoState state)
649 {
650         return _astate_string (state, true);
651 }
652
653 string
654 GainMeterBase::_astate_string (AutoState state, bool shrt)
655 {
656         string sstr;
657
658         switch (state) {
659         case Off:
660                 sstr = (shrt ? "M" : _("M"));
661                 break;
662         case Play:
663                 sstr = (shrt ? "P" : _("P"));
664                 break;
665         case Touch:
666                 sstr = (shrt ? "T" : _("T"));
667                 break;
668         case Write:
669                 sstr = (shrt ? "W" : _("W"));
670                 break;
671         }
672
673         return sstr;
674 }
675
676 string
677 GainMeterBase::astyle_string (AutoStyle style)
678 {
679         return _astyle_string (style, false);
680 }
681
682 string
683 GainMeterBase::short_astyle_string (AutoStyle style)
684 {
685         return _astyle_string (style, true);
686 }
687
688 string
689 GainMeterBase::_astyle_string (AutoStyle style, bool shrt)
690 {
691         if (style & Trim) {
692                 return _("Trim");
693         } else {
694                 /* XXX it might different in different languages */
695
696                 return (shrt ? _("Abs") : _("Abs"));
697         }
698 }
699
700 void
701 GainMeterBase::gain_automation_style_changed ()
702 {
703         switch (_width) {
704         case Wide:
705                 gain_automation_style_button.set_label (astyle_string(_amp->gain_control()->alist()->automation_style()));
706                 break;
707         case Narrow:
708                 gain_automation_style_button.set_label  (short_astyle_string(_amp->gain_control()->alist()->automation_style()));
709                 break;
710         }
711 }
712
713 void
714 GainMeterBase::gain_automation_state_changed ()
715 {
716         ENSURE_GUI_THREAD (*this, &GainMeterBase::gain_automation_state_changed)
717
718         bool x;
719
720         switch (_width) {
721         case Wide:
722                 gain_automation_state_button.set_label (astate_string(_amp->gain_control()->alist()->automation_state()));
723                 break;
724         case Narrow:
725                 gain_automation_state_button.set_label (short_astate_string(_amp->gain_control()->alist()->automation_state()));
726                 break;
727         }
728
729         x = (_amp->gain_control()->alist()->automation_state() != Off);
730
731         if (gain_automation_state_button.get_active() != x) {
732                 ignore_toggle = true;
733                 gain_automation_state_button.set_active (x);
734                 ignore_toggle = false;
735         }
736
737         update_gain_sensitive ();
738
739         /* start watching automation so that things move */
740
741         gain_watching.disconnect();
742
743         if (x) {
744                 gain_watching = ARDOUR_UI::RapidScreenUpdate.connect (sigc::mem_fun (*this, &GainMeterBase::effective_gain_display));
745         }
746 }
747
748 void
749 GainMeterBase::update_meters()
750 {
751         char buf[32];
752         float mpeak = level_meter->update_meters();
753
754         if (mpeak > max_peak) {
755                 max_peak = mpeak;
756                 if (mpeak <= -200.0f) {
757                         peak_display.set_label (_("-inf"));
758                 } else {
759                         snprintf (buf, sizeof(buf), "%.1f", mpeak);
760                         peak_display.set_label (buf);
761                 }
762
763                 if (mpeak >= 0.0f) {
764                         peak_display.set_name ("MixerStripPeakDisplayPeak");
765                 }
766         }
767 }
768
769 void GainMeterBase::color_handler(bool dpi)
770 {
771         color_changed = true;
772         dpi_changed = (dpi) ? true : false;
773         setup_meters();
774 }
775
776 void
777 GainMeterBase::set_width (Width w, int len)
778 {
779         _width = w;
780         level_meter->setup_meters (len);
781 }
782
783
784 void
785 GainMeterBase::on_theme_changed()
786 {
787         style_changed = true;
788 }
789
790 GainMeter::GainMeter (Session& s, int fader_length)
791         : GainMeterBase (s, slider, false, fader_length)
792 {
793         gain_display_box.set_homogeneous (true);
794         gain_display_box.set_spacing (2);
795         gain_display_box.pack_start (gain_display, true, true);
796
797         meter_metric_area.set_name ("AudioTrackMetrics");
798         set_size_request_to_display_given_text (meter_metric_area, "-50", 0, 0);
799
800         gain_automation_style_button.set_name ("MixerAutomationModeButton");
801         gain_automation_state_button.set_name ("MixerAutomationPlaybackButton");
802
803         ARDOUR_UI::instance()->tooltips().set_tip (gain_automation_state_button, _("Fader automation mode"));
804         ARDOUR_UI::instance()->tooltips().set_tip (gain_automation_style_button, _("Fader automation type"));
805
806         gain_automation_style_button.unset_flags (Gtk::CAN_FOCUS);
807         gain_automation_state_button.unset_flags (Gtk::CAN_FOCUS);
808
809         gain_automation_state_button.set_size_request(15, 15);
810         gain_automation_style_button.set_size_request(15, 15);
811
812         HBox* fader_centering_box = manage (new HBox);
813         fader_centering_box->pack_start (*gain_slider, true, false);
814
815         fader_vbox = manage (new Gtk::VBox());
816         fader_vbox->set_spacing (0);
817         fader_vbox->pack_start (*fader_centering_box, false, false, 0);
818
819         hbox.set_spacing (2);
820         hbox.pack_start (*fader_vbox, true, true);
821
822         set_spacing (2);
823
824         pack_start (gain_display_box, Gtk::PACK_SHRINK);
825         pack_start (hbox, Gtk::PACK_SHRINK);
826
827         meter_metric_area.signal_expose_event().connect (sigc::mem_fun(*this, &GainMeter::meter_metrics_expose));
828 }
829
830 void
831 GainMeter::set_controls (boost::shared_ptr<Route> r,
832                          boost::shared_ptr<PeakMeter> meter,
833                          boost::shared_ptr<Amp> amp)
834 {
835         if (level_meter->get_parent()) {
836                 hbox.remove (*level_meter);
837         }
838
839         if (peak_display.get_parent()) {
840                 gain_display_box.remove (peak_display);
841         }
842
843         if (gain_automation_state_button.get_parent()) {
844                 fader_vbox->remove (gain_automation_state_button);
845         }
846
847         GainMeterBase::set_controls (r, meter, amp);
848
849         /*
850            if we have a non-hidden route (ie. we're not the click or the auditioner),
851            pack some route-dependent stuff.
852         */
853
854         gain_display_box.pack_end (peak_display, true, true);
855         hbox.pack_end (*level_meter, true, true);
856
857         if (r && !r->is_hidden()) {
858                 fader_vbox->pack_start (gain_automation_state_button, false, false, 0);
859         }
860
861         setup_meters ();
862         hbox.show_all ();
863 }
864
865 int
866 GainMeter::get_gm_width ()
867 {
868         Gtk::Requisition sz;
869         hbox.size_request (sz);
870         return sz.width;
871 }
872
873 Glib::RefPtr<Gdk::Pixmap>
874 GainMeter::render_metrics (Gtk::Widget& w)
875 {
876         Glib::RefPtr<Gdk::Window> win (w.get_window());
877         Glib::RefPtr<Gdk::GC> fg_gc (w.get_style()->get_fg_gc (Gtk::STATE_NORMAL));
878         Glib::RefPtr<Gdk::GC> bg_gc (w.get_style()->get_bg_gc (Gtk::STATE_NORMAL));
879         gint width, height;
880         int  db_points[] = { -50, -40, -20, -30, -10, -3, 0, 4 };
881         char buf[32];
882
883         win->get_size (width, height);
884
885         Glib::RefPtr<Gdk::Pixmap> pixmap = Gdk::Pixmap::create (win, width, height);
886
887         metric_pixmaps[w.get_name()] = pixmap;
888
889         pixmap->draw_rectangle (bg_gc, true, 0, 0, width, height);
890
891         Glib::RefPtr<Pango::Layout> layout = w.create_pango_layout("");
892
893         for (uint32_t i = 0; i < sizeof (db_points)/sizeof (db_points[0]); ++i) {
894
895                 float fraction = log_meter (db_points[i]);
896                 gint pos = height - (gint) floor (height * fraction);
897
898                 snprintf (buf, sizeof (buf), "%d", abs (db_points[i]));
899
900                 layout->set_text (buf);
901
902                 /* we want logical extents, not ink extents here */
903
904                 int width, height;
905                 layout->get_pixel_size (width, height);
906
907                 pixmap->draw_line (fg_gc, 0, pos, 4, pos);
908                 pixmap->draw_layout (fg_gc, 6, pos - (height/2), layout);
909         }
910
911         return pixmap;
912 }
913
914 gint
915 GainMeter::meter_metrics_expose (GdkEventExpose *ev)
916 {
917         static Glib::RefPtr<Gtk::Style> meter_style;
918         if (style_changed) {
919                 meter_style = meter_metric_area.get_style();
920         }
921         Glib::RefPtr<Gdk::Window> win (meter_metric_area.get_window());
922         Glib::RefPtr<Gdk::GC> bg_gc (meter_style->get_bg_gc (Gtk::STATE_INSENSITIVE));
923         GdkRectangle base_rect;
924         GdkRectangle draw_rect;
925         gint width, height;
926
927         win->get_size (width, height);
928
929         base_rect.width = width;
930         base_rect.height = height;
931         base_rect.x = 0;
932         base_rect.y = 0;
933
934         Glib::RefPtr<Gdk::Pixmap> pixmap;
935         std::map<string,Glib::RefPtr<Gdk::Pixmap> >::iterator i = metric_pixmaps.find (meter_metric_area.get_name());
936
937         if (i == metric_pixmaps.end() || style_changed || dpi_changed) {
938                 pixmap = render_metrics (meter_metric_area);
939         } else {
940                 pixmap = i->second;
941         }
942
943         gdk_rectangle_intersect (&ev->area, &base_rect, &draw_rect);
944         win->draw_drawable (bg_gc, pixmap, draw_rect.x, draw_rect.y, draw_rect.x, draw_rect.y, draw_rect.width, draw_rect.height);
945         style_changed = false;
946         return true;
947 }
948
949 boost::shared_ptr<PBD::Controllable>
950 GainMeterBase::get_controllable()
951 {
952         if (_amp) {
953                 return _amp->gain_control();
954         } else {
955                 return boost::shared_ptr<PBD::Controllable>();
956         }
957 }
958
959