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