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