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