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