Fix the User button for Mixbus; we use it as a button, not a modifier.
[ardour.git] / libs / surfaces / faderport8 / actions.cc
1 /* Faderport 8 Control Surface
2  * This is the button "Controller" of the MVC surface inteface,
3  * see callbacks.cc for the "View".
4  *
5  * Copyright (C) 2017 Robin Gareus <robin@gareus.org>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20  */
21
22 #include "ardour/dB.h"
23 #include "ardour/plugin_insert.h"
24 #include "ardour/session.h"
25 #include "ardour/session_configuration.h"
26 #include "ardour/track.h"
27 #include "ardour/types.h"
28
29 #include "gtkmm2ext/actions.h"
30
31 #include "faderport8.h"
32
33 #include "pbd/i18n.h"
34
35 using namespace std;
36 using namespace ARDOUR;
37 using namespace ArdourSurface::FP_NAMESPACE;
38 using namespace ArdourSurface::FP_NAMESPACE::FP8Types;
39
40 #define BindMethod(ID, CB) \
41         _ctrls.button (FP8Controls::ID).released.connect_same_thread (button_connections, boost::bind (&FaderPort8:: CB, this));
42
43 #define BindMethod2(ID, ACT, CB) \
44         _ctrls.button (FP8Controls::ID). ACT .connect_same_thread (button_connections, boost::bind (&FaderPort8:: CB, this));
45
46 #define BindFunction(ID, ACT, CB, ...) \
47         _ctrls.button (FP8Controls::ID). ACT .connect_same_thread (button_connections, boost::bind (&FaderPort8:: CB, this, __VA_ARGS__));
48
49 #define BindAction(ID, GRP, ITEM) \
50         _ctrls.button (FP8Controls::ID).released.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_action, this, GRP, ITEM));
51
52 #define BindUserAction(ID) \
53         _ctrls.button (ID).pressed.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_user, this, true, ID)); \
54 _ctrls.button (ID).released.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_user, this, false, ID));
55
56
57 /* Bind button signals (press, release) to callback methods
58  * (called once after constructing buttons).
59  * Bound actions are handled the the ctrl-surface thread.
60  */
61 void
62 FaderPort8::setup_actions ()
63 {
64         BindMethod2 (BtnPlay, pressed, button_play);
65         BindMethod2 (BtnStop, pressed, button_stop);
66         BindMethod2 (BtnLoop, pressed, button_loop);
67         BindMethod2 (BtnRecord, pressed, button_record);
68         BindMethod2 (BtnClick, pressed, button_metronom);
69         BindAction (BtnRedo, "Editor", "redo");
70
71         BindAction (BtnSave, "Common", "Save");
72         BindAction (BtnUndo, "Editor", "undo");
73         BindAction (BtnRedo, "Editor", "redo");
74
75 #ifdef FP8_MUTESOLO_UNDO
76         BindMethod (BtnSoloClear, button_solo_clear);
77 #else
78         BindAction (BtnSoloClear, "Main", "cancel-solo");
79 #endif
80         BindMethod (BtnMuteClear, button_mute_clear);
81
82         BindMethod (FP8Controls::BtnArmAll, button_arm_all);
83
84         BindFunction (BtnRewind, pressed, button_varispeed, false);
85         BindFunction (BtnFastForward, pressed, button_varispeed, true);
86
87         BindFunction (BtnPrev, released, button_prev_next, false);
88         BindFunction (BtnNext, released, button_prev_next, true);
89
90         BindFunction (BtnArm, pressed, button_arm, true);
91         BindFunction (BtnArm, released, button_arm, false);
92
93         BindFunction (BtnAOff, released, button_automation, ARDOUR::Off);
94         BindFunction (BtnATouch, released, button_automation, ARDOUR::Touch);
95         BindFunction (BtnARead, released, button_automation, ARDOUR::Play);
96         BindFunction (BtnAWrite, released, button_automation, ARDOUR::Write);
97         BindFunction (BtnALatch, released, button_automation, ARDOUR::Latch);
98
99         _ctrls.button (FP8Controls::BtnEncoder).pressed.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_encoder, this));
100 #ifdef FADERPORT2
101         _ctrls.button (FP8Controls::BtnParam).pressed.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_encoder, this));
102 #else
103         _ctrls.button (FP8Controls::BtnParam).pressed.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_parameter, this));
104 #endif
105
106
107         BindMethod (BtnBypass, button_bypass);
108         BindAction (BtnBypassAll, "Mixer", "ab-plugins");
109
110         BindAction (BtnMacro, "Common", "toggle-editor-and-mixer");
111         BindMethod (BtnOpen, button_open);
112
113         BindMethod (BtnLink, button_link);
114         BindMethod (BtnLock, button_lock);
115
116 #ifdef FADERPORT2
117         BindMethod (BtnChanLock, button_chanlock);
118         BindMethod (BtnFlip, button_flip);
119 #endif
120
121         // user-specific
122         for (FP8Controls::UserButtonMap::const_iterator i = _ctrls.user_buttons ().begin ();
123                         i != _ctrls.user_buttons ().end (); ++i) {
124                 BindUserAction ((*i).first);
125         }
126 }
127
128 /* ****************************************************************************
129  * Direct control callback Actions
130  */
131
132 void
133 FaderPort8::button_play ()
134 {
135         if (session->transport_rolling ()) {
136                 if (session->transport_speed () != 1.0) {
137                         session->request_transport_speed (1.0);
138                 } else {
139                         transport_stop ();
140                 }
141         } else {
142                 transport_play ();
143         }
144 }
145
146 void
147 FaderPort8::button_stop ()
148 {
149         if (session->transport_rolling ()) {
150                 transport_stop ();
151         } else {
152                 AccessAction ("Transport", "GotoStart");
153         }
154 }
155
156 void
157 FaderPort8::button_record ()
158 {
159         set_record_enable (!get_record_enabled ());
160 }
161
162 void
163 FaderPort8::button_loop ()
164 {
165         loop_toggle ();
166 }
167
168 void
169 FaderPort8::button_metronom ()
170 {
171         Config->set_clicking (!Config->get_clicking ());
172 }
173
174 void
175 FaderPort8::button_bypass ()
176 {
177         boost::shared_ptr<PluginInsert> pi = _plugin_insert.lock();
178         if (pi) {
179                 pi->enable (! pi->enabled ());
180         } else {
181                 AccessAction ("Mixer", "ab-plugins");
182         }
183 }
184
185 void
186 FaderPort8::button_open ()
187 {
188         boost::shared_ptr<PluginInsert> pi = _plugin_insert.lock();
189         if (pi) {
190                 pi->ToggleUI (); /* EMIT SIGNAL */
191         } else {
192                 AccessAction ("Common", "addExistingAudioFiles");
193         }
194 }
195
196 void
197 FaderPort8::button_chanlock ()
198 {
199         _chan_locked = !_chan_locked;
200
201         _ctrls.button (FP8Controls::BtnChannel).set_blinking (_chan_locked);
202 }
203
204 void
205 FaderPort8::button_flip ()
206 {
207 }
208
209 void
210 FaderPort8::button_lock ()
211 {
212         if (!_link_enabled) {
213                 AccessAction ("Editor", "lock");
214                 return;
215         }
216         if (_link_locked) {
217                 unlock_link ();
218         } else if (!_link_control.expired ()) {
219                 lock_link ();
220         }
221 }
222
223 void
224 FaderPort8::button_link ()
225 {
226         switch (_ctrls.fader_mode()) {
227                 case ModeTrack:
228                 case ModePan:
229                         if (_link_enabled) {
230                                 stop_link ();
231                         } else {
232                                 start_link ();
233                         }
234                         break;
235                 default:
236                         //AccessAction ("Window", "show-mixer");
237                         break;
238         }
239 }
240
241 void
242 FaderPort8::button_automation (ARDOUR::AutoState as)
243 {
244         FaderMode fadermode = _ctrls.fader_mode ();
245         switch (fadermode) {
246                 case ModePlugins:
247 #if 0 // Plugin Control Automation Mode
248                         for (std::list <ProcessorCtrl>::iterator i = _proc_params.begin(); i != _proc_params.end(); ++i) {
249                                 ((*i).ac)->set_automation_state (as);
250                         }
251 #endif
252                         return;
253                 case ModeSend:
254                         if (first_selected_stripable()) {
255 #if 0 // Send Level Automation
256                                 boost::shared_ptr<Stripable> s = first_selected_stripable();
257                                 boost::shared_ptr<AutomationControl> send;
258                                 uint32_t i = 0;
259                                 while (0 != (send = s->send_level_controllable (i))) {
260                                         send->set_automation_state (as);
261                                         ++i;
262                                 }
263 #endif
264                         }
265                         return;
266                 default:
267                         break;
268         }
269
270         // TODO link/lock control automation?
271
272         // apply to all selected tracks
273         StripableList all;
274         session->get_stripables (all);
275         for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) {
276                 if ((*i)->is_master() || (*i)->is_monitor()) {
277                         continue;
278                 }
279                 if (!(*i)->is_selected()) {
280                         continue;
281                 }
282                 boost::shared_ptr<AutomationControl> ac;
283                 switch (fadermode) {
284                         case ModeTrack:
285                                 ac = (*i)->gain_control ();
286                                 break;
287                         case ModePan:
288                                 ac = (*i)->pan_azimuth_control ();
289                                 break;
290                         default:
291                                 break;
292                 }
293                 if (ac) {
294                         ac->set_automation_state (as);
295                 }
296         }
297 }
298
299 void
300 FaderPort8::button_varispeed (bool ffw)
301 {
302         /* pressing both rew + ffwd -> return to zero */
303         FP8ButtonInterface& b_rew = _ctrls.button (FP8Controls::BtnRewind);
304         FP8ButtonInterface& b_ffw = _ctrls.button (FP8Controls::BtnFastForward);
305         if (b_rew.is_pressed () && b_ffw.is_pressed ()){
306                 // stop key-repeat
307                 dynamic_cast<FP8RepeatButton*>(&b_ffw)->stop_repeat();
308                 dynamic_cast<FP8RepeatButton*>(&b_rew)->stop_repeat();
309                 session->request_locate (0, false);
310                 return;
311         }
312
313         // switch play direction, if needed
314         if (ffw) {
315                 if (session->transport_speed () <= 0) {
316                         session->request_transport_speed (1.0);
317                         return ;
318                 }
319         } else {
320                 if (session->transport_speed () >= 0) {
321                         session->request_transport_speed (-1.0);
322                         return ;
323                 }
324         }
325         // incremetally increase speed. double speed every 10 clicks
326         // (keypress auto-repeat is 100ms)
327         float maxspeed = Config->get_shuttle_max_speed();
328         float speed = exp2f(0.1f) * session->transport_speed ();
329         speed = std::max (-maxspeed, std::min (maxspeed, speed));
330         session->request_transport_speed (speed, false);
331 }
332
333 #ifdef FP8_MUTESOLO_UNDO
334 void
335 FaderPort8::button_solo_clear ()
336 {
337         bool soloing = session->soloing() || session->listening();
338 #ifdef MIXBUS
339         soloing |= session->mixbus_soloed();
340 #endif
341         if (soloing) {
342                 StripableList all;
343                 session->get_stripables (all);
344                 for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) {
345                         if ((*i)->is_master() || (*i)->is_auditioner() || (*i)->is_monitor()) {
346                                 continue;
347                         }
348                         boost::shared_ptr<SoloControl> sc = (*i)->solo_control();
349                         if (sc && sc->self_soloed ()) {
350                                 _solo_state.push_back (boost::weak_ptr<AutomationControl>(sc));
351                         }
352                 }
353                 cancel_all_solo (); // AccessAction ("Main", "cancel-solo");
354         } else {
355                 /* restore solo */
356                 boost::shared_ptr<ControlList> cl (new ControlList);
357                 for (std::vector <boost::weak_ptr<AutomationControl> >::const_iterator i = _solo_state.begin(); i != _solo_state.end(); ++i) {
358                         boost::shared_ptr<AutomationControl> ac = (*i).lock();
359                         if (!ac) {
360                                 continue;
361                         }
362                         ac->start_touch (ac->session().transport_sample());
363                         cl->push_back (ac);
364                 }
365                 if (!cl->empty()) {
366                         session->set_controls (cl, 1.0, PBD::Controllable::NoGroup);
367                 }
368         }
369 }
370 #endif
371
372 void
373 FaderPort8::button_mute_clear ()
374 {
375 #ifdef FP8_MUTESOLO_UNDO
376         if (session->muted ()) {
377                 _mute_state = session->cancel_all_mute ();
378         } else {
379                 /* restore mute */
380                 boost::shared_ptr<ControlList> cl (new ControlList);
381                 for (std::vector <boost::weak_ptr<AutomationControl> >::const_iterator i = _mute_state.begin(); i != _mute_state.end(); ++i) {
382                         boost::shared_ptr<AutomationControl> ac = (*i).lock();
383                         if (!ac) {
384                                 continue;
385                         }
386                         cl->push_back (ac);
387                         ac->start_touch (ac->session().transport_sample());
388                 }
389                 if (!cl->empty()) {
390                         session->set_controls (cl, 1.0, PBD::Controllable::NoGroup);
391                 }
392         }
393 #else
394         session->cancel_all_mute ();
395 #endif
396 }
397
398 void
399 FaderPort8::button_arm_all ()
400 {
401         BasicUI::all_tracks_rec_in ();
402 }
403
404 /* access generic action */
405 void
406 FaderPort8::button_action (const std::string& group, const std::string& item)
407 {
408         AccessAction (group, item);
409 }
410
411 /* ****************************************************************************
412  * Control Interaction (encoder)
413  */
414
415 void
416 FaderPort8::handle_encoder_pan (int steps)
417 {
418         boost::shared_ptr<Stripable> s = first_selected_stripable();
419         if (s) {
420                 boost::shared_ptr<AutomationControl> ac;
421                 if (shift_mod () || _ctrls.fader_mode() == ModePan) {
422                         ac = s->pan_width_control ();
423                 } else {
424                         ac = s->pan_azimuth_control ();
425                 }
426                 if (ac) {
427                         ac->start_touch (ac->session().transport_sample());
428                         if (steps == 0) {
429                                 ac->set_value (ac->normal(), PBD::Controllable::UseGroup);
430                         } else {
431                                 double v = ac->internal_to_interface (ac->get_value());
432                                 v = std::max (0.0, std::min (1.0, v + steps * .01));
433                                 ac->set_value (ac->interface_to_internal(v), PBD::Controllable::UseGroup);
434                         }
435                 }
436         }
437 }
438
439 void
440 FaderPort8::handle_encoder_link (int steps)
441 {
442         if (_link_control.expired ()) {
443                 return;
444         }
445         boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (_link_control.lock ());
446         if (!ac) {
447                 return;
448         }
449
450         double v = ac->internal_to_interface (ac->get_value());
451         ac->start_touch (ac->session().transport_sample());
452
453         if (steps == 0) {
454                 ac->set_value (ac->normal(), PBD::Controllable::UseGroup);
455                 return;
456         }
457
458         if (ac->desc().toggled) {
459                 v = v > 0 ? 0. : 1.;
460         } else if (ac->desc().integer_step) {
461                 v += steps / (1.f + ac->desc().upper - ac->desc().lower);
462         } else if (ac->desc().enumeration) {
463                 ac->set_value (ac->desc().step_enum (ac->get_value(), steps < 0), PBD::Controllable::UseGroup);
464                 return;
465         } else {
466                 v = std::max (0.0, std::min (1.0, v + steps * .01));
467         }
468         ac->set_value (ac->interface_to_internal(v), PBD::Controllable::UseGroup);
469 }
470
471
472 /* ****************************************************************************
473  * Mode specific and internal callbacks
474  */
475
476 /* handle "ARM" press -- act like shift, change "Select" button mode */
477 void
478 FaderPort8::button_arm (bool press)
479 {
480 #ifdef FADERPORT2
481         boost::shared_ptr<Stripable> s = first_selected_stripable();
482         if (press && s) {
483                 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(s);
484                 if (t) {
485                         t->rec_enable_control()->set_value (!t->rec_enable_control()->get_value(), PBD::Controllable::UseGroup);
486                 }
487         }
488 #else
489         FaderMode fadermode = _ctrls.fader_mode ();
490         if (fadermode == ModeTrack || fadermode == ModePan) {
491                 _ctrls.button (FP8Controls::BtnArm).set_active (press);
492                 ARMButtonChange (press); /* EMIT SIGNAL */
493         }
494 #endif
495 }
496
497 void
498 FaderPort8::button_prev_next (bool next)
499 {
500         switch (_ctrls.nav_mode()) {
501                 case NavChannel:
502 #ifndef FADERPORT2
503                         select_prev_next (next);
504                         break;
505 #endif
506                 case NavMaster:
507                 case NavScroll:
508                 case NavPan:
509                         bank (!next, false);
510                         break;
511                 case NavBank:
512                         bank (!next, true);
513                         break;
514                 case NavZoom:
515                         if (next) {
516                                 VerticalZoomInSelected ();
517                         } else {
518                                 VerticalZoomOutSelected ();
519                         }
520                         break;
521                 case NavSection:
522                         if (next) {
523                                 AccessAction ("Region", "nudge-forward");
524                         } else {
525                                 AccessAction ("Region", "nudge-backward");
526                         }
527                         break;
528                 case NavMarker:
529                         if (next) {
530                                 next_marker ();
531                         } else {
532                                 prev_marker ();
533                         }
534                         break;
535         }
536 }
537
538 /* handle navigation encoder press */
539 void
540 FaderPort8::button_encoder ()
541 {
542         /* special-case metronome level */
543         if (_ctrls.button (FP8Controls::BtnClick).is_pressed ()) {
544                 Config->set_click_gain (1.0);
545                 _ctrls.button (FP8Controls::BtnClick).ignore_release();
546                 return;
547         }
548         switch (_ctrls.nav_mode()) {
549                 case NavZoom:
550                         ZoomToSession (); // XXX undo zoom
551                         break;
552                 case NavScroll:
553                         ZoomToSession ();
554                         break;
555                 case NavChannel:
556                         AccessAction ("Editor", "select-topmost");
557                         break;
558                 case NavBank:
559                         move_selected_into_view ();
560                         break;
561                 case NavMaster:
562                         {
563                                 /* master || monitor level -- reset to 0dB */
564                                 boost::shared_ptr<AutomationControl> ac;
565                                 if (session->monitor_active() && !_ctrls.button (FP8Controls::BtnMaster).is_pressed ()) {
566                                         ac = session->monitor_out()->gain_control ();
567                                 } else if (session->master_out()) {
568                                         ac = session->master_out()->gain_control ();
569                                 }
570                                 if (ac) {
571                                         ac->start_touch (ac->session().transport_sample());
572                                         ac->set_value (ac->normal(), PBD::Controllable::NoGroup);
573                                 }
574                         }
575                         break;
576                 case NavPan:
577                         break;
578                 case NavSection:
579                         // TODO nudge
580                         break;
581                 case NavMarker:
582                         {
583                                 string markername;
584                                 /* Don't add another mark if one exists within 1/100th of a second of
585                                  * the current position and we're not rolling.
586                                  */
587                                 samplepos_t where = session->audible_sample();
588                                 if (session->transport_stopped() && session->locations()->mark_at (where, session->sample_rate() / 100.0)) {
589                                         return;
590                                 }
591
592                                 session->locations()->next_available_name (markername,"mark");
593                                 add_marker (markername);
594                         }
595                         break;
596         }
597 }
598
599 /* handle navigation encoder turn */
600 void
601 FaderPort8::encoder_navigate (bool neg, int steps)
602 {
603         /* special-case metronome level */
604         if (_ctrls.button (FP8Controls::BtnClick).is_pressed ()) {
605                 // compare to ARDOUR_UI::click_button_scroll()
606                 gain_t gain = Config->get_click_gain();
607                 float gain_db = accurate_coefficient_to_dB (gain);
608                 gain_db += (neg ? -1.f : 1.f) * steps;
609                 gain_db = std::max (-60.f, gain_db);
610                 gain = dB_to_coefficient (gain_db);
611                 gain = std::min (gain, Config->get_max_gain());
612                 Config->set_click_gain (gain);
613                 _ctrls.button (FP8Controls::BtnClick).ignore_release();
614                 return;
615         }
616
617         switch (_ctrls.nav_mode()) {
618                 case NavChannel:
619                         if (neg) {
620                                 AccessAction ("Mixer", "scroll-left");
621                                 AccessAction ("Editor", "step-tracks-up");
622                         } else {
623                                 AccessAction ("Mixer", "scroll-right");
624                                 AccessAction ("Editor", "step-tracks-down");
625                         }
626                         break;
627                 case NavZoom:
628                         if (neg) {
629                                 ZoomOut ();
630                         } else {
631                                 ZoomIn ();
632                         }
633                         break;
634                 case NavMarker:
635                 case NavScroll:
636                         ScrollTimeline ((neg ? -1.f : 1.f) * steps / (shift_mod() ? 1024.f : 256.f));
637                         break;
638                 case NavBank:
639                         bank (neg, false);
640                         break;
641                 case NavMaster:
642                         {
643                                 /* master || monitor level */
644                                 boost::shared_ptr<AutomationControl> ac;
645                                 if (session->monitor_active() && !_ctrls.button (FP8Controls::BtnMaster).is_pressed ()) {
646                                         ac = session->monitor_out()->gain_control ();
647                                 } else if (session->master_out()) {
648                                         ac = session->master_out()->gain_control ();
649                                 }
650                                 if (ac) {
651                                         double v = ac->internal_to_interface (ac->get_value());
652                                         v = std::max (0.0, std::min (1.0, v + steps * (neg ? -.01 : .01)));
653                                         ac->start_touch (ac->session().transport_sample());
654                                         ac->set_value (ac->interface_to_internal(v), PBD::Controllable::NoGroup);
655                                 }
656                         }
657                         break;
658                 case NavSection:
659                         if (neg) {
660                                 AccessAction ("Common", "nudge-playhead-backward");
661                         } else {
662                                 AccessAction ("Common", "nudge-playhead-forward");
663                         }
664                         break;
665                 case NavPan:
666                         abort(); /*NOTREACHED*/
667                         break;
668         }
669 }
670
671 /* handle pan/param encoder press */
672 void
673 FaderPort8::button_parameter ()
674 {
675         switch (_ctrls.fader_mode()) {
676                 case ModeTrack:
677                 case ModePan:
678                         if (_link_enabled || _link_locked) {
679                                 handle_encoder_link (0);
680                         } else {
681                                 handle_encoder_pan (0);
682                         }
683                         break;
684                 case ModePlugins:
685                         toggle_preset_param_mode ();
686                         break;
687                 case ModeSend:
688                         break;
689         }
690 }
691
692 /* handle pan/param encoder turn */
693 void
694 FaderPort8::encoder_parameter (bool neg, int steps)
695 {
696         switch (_ctrls.fader_mode()) {
697                 case ModeTrack:
698                 case ModePan:
699                         if (steps != 0) {
700                                 if (_link_enabled || _link_locked) {
701                                         handle_encoder_link (neg ? -steps : steps);
702                                 } else {
703                                         handle_encoder_pan (neg ? -steps : steps);
704                                 }
705                         }
706                         break;
707                 case ModePlugins:
708                 case ModeSend:
709                         while (steps > 0) {
710                                 bank_param (neg, shift_mod());
711                                 --steps;
712                         }
713                         break;
714         }
715 }
716
717 /* handle user-specific actions */
718 void
719 FaderPort8::button_user (bool press, FP8Controls::ButtonId btn)
720 {
721         _user_action_map[btn].call (*this, press);
722 }