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