Towards MIDI:
[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         _session.MeterHoldChanged.connect (mem_fun(*this, &GainMeter::meter_hold_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::meter_hold_changed()
354 {
355         ENSURE_GUI_THREAD(mem_fun(*this, &GainMeter::meter_hold_changed));
356         
357         vector<MeterInfo>::iterator i;
358         uint32_t n;
359         
360         for (n = 0, i = meters.begin(); i != meters.end(); ++i, ++n) {
361                 
362                 (*i).meter->set_hold_count ((uint32_t) floor(_session.meter_hold()));
363         }
364 }
365
366 void
367 GainMeter::hide_all_meters ()
368 {
369         bool remove_metric_area = false;
370
371         for (vector<MeterInfo>::iterator i = meters.begin(); i != meters.end(); ++i) {
372                 if ((*i).packed) {
373                         remove_metric_area = true;
374                         meter_packer.remove (*((*i).meter));
375                         (*i).packed = false;
376                 }
377         }
378
379         if (remove_metric_area) {
380                 if (meter_metric_area.get_parent()) {
381                         meter_packer.remove (meter_metric_area);
382                 }
383         }
384 }
385
386 void
387 GainMeter::setup_meters ()
388 {
389         uint32_t nmeters = _io->n_outputs().get(DataType::AUDIO);
390         guint16 width;
391
392         hide_all_meters ();
393
394         Route* r;
395
396         if ((r = dynamic_cast<Route*> (_io.get())) != 0) {
397
398                 switch (r->meter_point()) {
399                 case MeterPreFader:
400                 case MeterInput:
401                         nmeters = r->n_inputs().get(DataType::AUDIO);
402                         break;
403                 case MeterPostFader:
404                         nmeters = r->n_outputs().get(DataType::AUDIO);
405                         break;
406                 }
407
408         } else {
409
410                 nmeters = _io->n_outputs().get(DataType::AUDIO);
411
412         }
413
414         if (nmeters == 0) {
415                 return;
416         }
417
418         if (nmeters <= 2) {
419                 width = regular_meter_width;
420         } else {
421                 width = thin_meter_width;
422         }
423
424         while (meters.size() < nmeters) {
425                 meters.push_back (MeterInfo());
426         }
427
428         for (uint32_t n = 0; n < nmeters; ++n) {
429                 if (meters[n].width != width) {
430                         delete meters[n].meter;
431                         meters[n].meter = new FastMeter ((uint32_t) floor (_session.meter_hold()), width, FastMeter::Vertical);
432                         meters[n].width = width;
433
434                         meters[n].meter->add_events (Gdk::BUTTON_RELEASE_MASK);
435                         meters[n].meter->signal_button_release_event().connect (bind (mem_fun(*this, &GainMeter::meter_button_release), n));
436                 }
437
438                 meter_packer.pack_start (*meters[n].meter, Gtk::PACK_SHRINK);
439                 meters[n].meter->show_all ();
440                 meters[n].packed = true;
441         }
442
443         if (_width == Wide) {
444                 meter_packer.pack_start (meter_metric_area, Gtk::PACK_SHRINK);
445                 meter_metric_area.show_all ();
446         }
447 }       
448
449 gint
450 GainMeter::peak_button_release (GdkEventButton* ev)
451 {
452         /* reset peak label */
453
454         if (ev->button == 1 && Keyboard::modifier_state_equals (ev->state, Keyboard::Control|Keyboard::Shift)) {
455                 ResetAllPeakDisplays ();
456         } else if (ev->button == 1 && Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
457                 Route* r;
458                 if ((r = dynamic_cast<Route*> (_io.get())) != 0) {
459                         ResetGroupPeakDisplays (r->mix_group());
460                 }
461         } else {
462                 reset_peak_display ();
463         }
464         return TRUE;
465 }
466
467 void
468 GainMeter::reset_peak_display ()
469 {
470         max_peak = minus_infinity();
471         peak_display_label.set_text (_("-Inf"));
472         peak_display.set_name ("Mixerstrippeakdisplay");
473 }
474
475 void
476 GainMeter::reset_group_peak_display (RouteGroup* group)
477 {
478         Route* r;
479         if ((r = dynamic_cast<Route*> (_io.get())) != 0) {
480                 if (group == r->mix_group()) {
481                         reset_peak_display ();
482                 }
483         }
484 }
485
486 gint
487 GainMeter::meter_button_release (GdkEventButton* ev, uint32_t which)
488 {
489         switch (ev->button) {
490         case 1:
491                 meters[which].meter->clear();
492                 max_peak = minus_infinity();
493                 peak_display_label.set_text (_("-inf"));
494                 peak_display.set_name ("MixerStripPeakDisplay");
495                 break;
496
497         case 3:
498                 // popup_meter_menu (ev);
499                 break;
500         };
501
502         return TRUE;
503 }
504
505 void
506 GainMeter::popup_meter_menu (GdkEventButton *ev)
507 {
508         using namespace Menu_Helpers;
509
510         if (meter_menu == 0) {
511                 meter_menu = new Gtk::Menu;
512                 MenuList& items = meter_menu->items();
513
514                 items.push_back (MenuElem ("-inf .. +0dBFS"));
515                 items.push_back (MenuElem ("-10dB .. +0dBFS"));
516                 items.push_back (MenuElem ("-4 .. +0dBFS"));
517                 items.push_back (SeparatorElem());
518                 items.push_back (MenuElem ("-inf .. -2dBFS"));
519                 items.push_back (MenuElem ("-10dB .. -2dBFS"));
520                 items.push_back (MenuElem ("-4 .. -2dBFS"));
521         }
522
523         meter_menu->popup (1, ev->time);
524 }
525
526 void
527 GainMeter::_gain_printer (char buf[32], Gtk::Adjustment& adj, void *arg)
528 {
529         static_cast<GainMeter *>(arg)->gain_printer (buf, adj);
530 }
531
532 void
533 GainMeter::gain_printer (char buf[32], Gtk::Adjustment& adj)
534 {
535         float v = adj.get_value();
536
537         if (v == 0.0) {
538                 strcpy (buf, _("-inf"));
539         } else {
540                 snprintf (buf, 32, "%.1f", coefficient_to_dB (slider_position_to_gain (v)));
541         }
542 }
543
544 void
545 GainMeter::gain_adjusted ()
546 {
547         if (!ignore_toggle) {
548                 _io->set_gain (slider_position_to_gain (gain_adjustment.get_value()), this);
549         }
550 }
551
552 void
553 GainMeter::effective_gain_display ()
554 {
555         gfloat value = gain_to_slider_position (_io->effective_gain());
556         
557         if (gain_adjustment.get_value() != value) {
558                 ignore_toggle = true;
559                 gain_adjustment.set_value (value);
560                 ignore_toggle = false;
561         }
562 }
563
564 void
565 GainMeter::gain_changed (void *src)
566 {
567         Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &GainMeter::effective_gain_display));
568 }
569
570 void
571 GainMeter::set_meter_strip_name (const char * name)
572 {
573         meter_metric_area.set_name (name);
574 }
575
576 void
577 GainMeter::set_fader_name (const char * name)
578 {
579         gain_slider->set_name (name);
580 }
581
582 void
583 GainMeter::update_gain_sensitive ()
584 {
585         static_cast<Gtkmm2ext::SliderController*>(gain_slider)->set_sensitive (!(_io->gain_automation_state() & Play));
586 }
587
588
589 static MeterPoint
590 next_meter_point (MeterPoint mp)
591 {
592         switch (mp) {
593         case MeterInput:
594                 return MeterPreFader;
595                 break;
596                 
597         case MeterPreFader:
598                 return MeterPostFader;
599                 break;
600                 
601         case MeterPostFader:
602                 return MeterInput;
603                 break;
604         }
605         /*NOTREACHED*/
606         return MeterInput;
607 }
608
609 gint
610 GainMeter::meter_press(GdkEventButton* ev)
611 {
612         Route* _route;
613
614         wait_for_release = false;
615
616         if ((_route = dynamic_cast<Route*>(_io.get())) == 0) {
617                 return FALSE;
618         }
619
620         if (!ignore_toggle) {
621
622                 if (Keyboard::is_context_menu_event (ev)) {
623                         
624                         // no menu at this time.
625
626                 } else {
627
628                         if (ev->button == 2) {
629
630                                 // ctrl-button2 click is the midi binding click
631                                 // button2-click is "momentary"
632                                 
633                                 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control))) {
634                                         wait_for_release = true;
635                                         old_meter_point = _route->meter_point ();
636                                 }
637                         }
638
639                         if (ev->button == 1 || ev->button == 2) {
640
641                                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
642
643                                         /* ctrl-shift-click applies change to all routes */
644
645                                         _session.foreach_route (this, &GainMeter::set_meter_point, next_meter_point (_route->meter_point()));
646                                         
647                                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
648
649                                         /* ctrl-click: solo mix group.
650                                            ctrl-button2 is MIDI learn.
651                                         */
652                                         
653                                         if (ev->button == 1) {
654                                                 set_mix_group_meter_point (*_route, next_meter_point (_route->meter_point()));
655                                         }
656                                         
657                                 } else {
658                                         
659                                         /* click: solo this route */
660                                         
661                                         _route->set_meter_point (next_meter_point (_route->meter_point()), this);
662                                 }
663                         }
664                 }
665         }
666
667         return true;
668
669 }
670
671 gint
672 GainMeter::meter_release(GdkEventButton* ev)
673 {
674
675         if(!ignore_toggle){
676                 if (wait_for_release){
677                         wait_for_release = false;
678                         set_meter_point (*(dynamic_cast<Route*>(_io.get())), old_meter_point);
679                 }
680         }
681         return true;
682 }
683
684 void
685 GainMeter::set_meter_point (Route& route, MeterPoint mp)
686 {
687         route.set_meter_point (mp, this);
688 }
689
690 void
691 GainMeter::set_mix_group_meter_point (Route& route, MeterPoint mp)
692 {
693         RouteGroup* mix_group;
694
695         if((mix_group = route.mix_group()) != 0){
696                 mix_group->apply (&Route::set_meter_point, mp, this);
697         } else {
698                 route.set_meter_point (mp, this);
699         }
700 }
701
702 void
703 GainMeter::meter_point_clicked ()
704 {
705         Route* r;
706
707         if ((r = dynamic_cast<Route*> (_io.get())) != 0) {
708
709         }
710 }
711
712 gint
713 GainMeter::start_gain_touch (GdkEventButton* ev)
714 {
715         _io->start_gain_touch ();
716         return FALSE;
717 }
718
719 gint
720 GainMeter::end_gain_touch (GdkEventButton* ev)
721 {
722         _io->end_gain_touch ();
723         return FALSE;
724 }
725
726 gint
727 GainMeter::gain_automation_state_button_event (GdkEventButton *ev)
728 {
729         if (ev->type == GDK_BUTTON_RELEASE) {
730                 return TRUE;
731         }
732         
733         switch (ev->button) {
734                 case 1:
735                         gain_astate_menu.popup (1, ev->time);
736                         break;
737                 default:
738                         break;
739         }
740
741         return TRUE;
742 }
743
744 gint
745 GainMeter::gain_automation_style_button_event (GdkEventButton *ev)
746 {
747         if (ev->type == GDK_BUTTON_RELEASE) {
748                 return TRUE;
749         }
750
751         switch (ev->button) {
752         case 1:
753                 gain_astyle_menu.popup (1, ev->time);
754                 break;
755         default:
756                 break;
757         }
758         return TRUE;
759 }
760
761 string
762 GainMeter::astate_string (AutoState state)
763 {
764         return _astate_string (state, false);
765 }
766
767 string
768 GainMeter::short_astate_string (AutoState state)
769 {
770         return _astate_string (state, true);
771 }
772
773 string
774 GainMeter::_astate_string (AutoState state, bool shrt)
775 {
776         string sstr;
777
778         switch (state) {
779         case Off:
780                 sstr = (shrt ? "O" : _("O"));
781                 break;
782         case Play:
783                 sstr = (shrt ? "P" : _("P"));
784                 break;
785         case Touch:
786                 sstr = (shrt ? "T" : _("T"));
787                 break;
788         case Write:
789                 sstr = (shrt ? "W" : _("W"));
790                 break;
791         }
792
793         return sstr;
794 }
795
796 string
797 GainMeter::astyle_string (AutoStyle style)
798 {
799         return _astyle_string (style, false);
800 }
801
802 string
803 GainMeter::short_astyle_string (AutoStyle style)
804 {
805         return _astyle_string (style, true);
806 }
807
808 string
809 GainMeter::_astyle_string (AutoStyle style, bool shrt)
810 {
811         if (style & Trim) {
812                 return _("Trim");
813         } else {
814                 /* XXX it might different in different languages */
815
816                 return (shrt ? _("Abs") : _("Abs"));
817         }
818 }
819
820 void
821 GainMeter::gain_automation_style_changed ()
822 {
823   // Route* _route = dynamic_cast<Route*>(&_io);
824         switch (_width) {
825         case Wide:
826                 gain_automation_style_button.set_label (astyle_string(_io->gain_automation_curve().automation_style()));
827                 break;
828         case Narrow:
829                 gain_automation_style_button.set_label  (short_astyle_string(_io->gain_automation_curve().automation_style()));
830                 break;
831         }
832 }
833
834 void
835 GainMeter::gain_automation_state_changed ()
836 {
837         ENSURE_GUI_THREAD(mem_fun(*this, &GainMeter::gain_automation_state_changed));
838         //Route* _route = dynamic_cast<Route*>(&_io);
839         
840         bool x;
841
842         switch (_width) {
843         case Wide:
844                 gain_automation_state_button.set_label (astate_string(_io->gain_automation_curve().automation_state()));
845                 break;
846         case Narrow:
847                 gain_automation_state_button.set_label (short_astate_string(_io->gain_automation_curve().automation_state()));
848                 break;
849         }
850
851         x = (_io->gain_automation_state() != Off);
852         
853         if (gain_automation_state_button.get_active() != x) {
854                 ignore_toggle = true;
855                 gain_automation_state_button.set_active (x);
856                 ignore_toggle = false;
857         }
858
859         update_gain_sensitive ();
860         
861         /* start watching automation so that things move */
862         
863         gain_watching.disconnect();
864
865         if (x) {
866                 gain_watching = ARDOUR_UI::RapidScreenUpdate.connect (mem_fun (*this, &GainMeter::effective_gain_display));
867         }
868 }