Mixer strip uses button 1 where possible, make mouse click behaviour more consistent...
[ardour.git] / gtk2_ardour / redirect_box.cc
1 /*
2     Copyright (C) 2000-2004 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 <cmath>
22
23 #include <sigc++/bind.h>
24
25 #include <pbd/convert.h>
26
27 #include <gtkmm/messagedialog.h>
28
29 #include <gtkmm2ext/gtk_ui.h>
30 #include <gtkmm2ext/utils.h>
31 #include <gtkmm2ext/choice.h>
32 #include <gtkmm2ext/utils.h>
33 #include <gtkmm2ext/stop_signal.h>
34 #include <gtkmm2ext/doi.h>
35
36 #include <ardour/ardour.h>
37 #include <ardour/session.h>
38 #include <ardour/audioengine.h>
39 #include <ardour/route.h>
40 #include <ardour/audio_track.h>
41 #include <ardour/diskstream.h>
42 #include <ardour/send.h>
43 #include <ardour/insert.h>
44 #include <ardour/ladspa_plugin.h>
45 #include <ardour/connection.h>
46 #include <ardour/session_connection.h>
47
48 #include "ardour_ui.h"
49 #include "ardour_dialog.h"
50 #include "public_editor.h"
51 #include "redirect_box.h"
52 #include "keyboard.h"
53 #include "plugin_selector.h"
54 #include "route_redirect_selection.h"
55 #include "mixer_ui.h"
56 #include "actions.h"
57
58 #include "plugin_ui.h"
59 #include "send_ui.h"
60 #include "io_selector.h"
61 #include "utils.h"
62 #include "gui_thread.h"
63
64 #include "i18n.h"
65
66 using namespace sigc;
67 using namespace ARDOUR;
68 using namespace Gtk;
69 using namespace Glib;
70 using namespace Gtkmm2ext;
71
72 RedirectBox* RedirectBox::_current_redirect_box = 0;
73 RefPtr<Action> RedirectBox::paste_action;
74 bool RedirectBox::get_colors = true;
75 Gdk::Color* RedirectBox::active_redirect_color;
76 Gdk::Color* RedirectBox::inactive_redirect_color;
77
78 RedirectBox::RedirectBox (Placement pcmnt, Session& sess, Route& rt, PluginSelector &plugsel, 
79                           RouteRedirectSelection & rsel, bool owner_is_mixer)
80         : _route(rt), 
81           _session(sess), 
82           _owner_is_mixer (owner_is_mixer), 
83           _placement(pcmnt), 
84           _plugin_selector(plugsel),
85           _rr_selection(rsel)
86 {
87         if (get_colors) {
88                 active_redirect_color = new Gdk::Color;
89                 inactive_redirect_color = new Gdk::Color;
90                 set_color (*active_redirect_color, rgba_from_style ("RedirectSelector", 0xff, 0, 0, 0, "fg", Gtk::STATE_ACTIVE, false ));
91                 set_color (*inactive_redirect_color, rgba_from_style ("RedirectSelector", 0xff, 0, 0, 0, "fg", Gtk::STATE_NORMAL, false ));
92                 get_colors = false;
93         }
94
95         _width = Wide;
96         redirect_menu = 0;
97         send_action_menu = 0;
98         redirect_drag_in_progress = false;
99         no_redirect_redisplay = false;
100         ignore_delete = false;
101
102         model = ListStore::create(columns);
103
104         RefPtr<TreeSelection> selection = redirect_display.get_selection();
105         selection->set_mode (Gtk::SELECTION_MULTIPLE);
106         selection->signal_changed().connect (mem_fun (*this, &RedirectBox::selection_changed));
107
108         redirect_display.set_model (model);
109         redirect_display.append_column (X_("notshown"), columns.text);
110         redirect_display.set_name ("RedirectSelector");
111         redirect_display.set_headers_visible (false);
112         redirect_display.set_reorderable (true);
113         redirect_display.set_size_request (-1, 40);
114         redirect_display.get_column(0)->set_sizing(TREE_VIEW_COLUMN_FIXED);
115         redirect_display.get_column(0)->set_fixed_width(48);
116         redirect_display.add_object_drag (columns.redirect.index(), "redirects");
117         redirect_display.signal_object_drop.connect (mem_fun (*this, &RedirectBox::object_drop));
118
119         TreeViewColumn* name_col = redirect_display.get_column(0);
120         CellRendererText* renderer = dynamic_cast<CellRendererText*>(redirect_display.get_column_cell_renderer (0));
121         name_col->add_attribute(renderer->property_foreground_gdk(), columns.color);
122
123         redirect_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
124         
125         model->signal_row_deleted().connect (mem_fun (*this, &RedirectBox::row_deleted));
126
127         redirect_scroller.add (redirect_display);
128         redirect_eventbox.add (redirect_scroller);
129         
130         redirect_scroller.set_size_request (-1, 40);
131
132         pack_start (redirect_eventbox, true, true);
133
134         _route.redirects_changed.connect (mem_fun(*this, &RedirectBox::redisplay_redirects));
135
136         redirect_eventbox.signal_enter_notify_event().connect (bind (sigc::ptr_fun (RedirectBox::enter_box), this));
137
138         redirect_display.signal_button_press_event().connect (mem_fun(*this, &RedirectBox::redirect_button_press_event), false);
139         redirect_display.signal_button_release_event().connect (mem_fun(*this, &RedirectBox::redirect_button_press_event));
140
141         /* start off as a passthru strip. we'll correct this, if necessary,
142            in update_diskstream_display().
143         */
144
145         /* now force an update of all the various elements */
146
147         redisplay_redirects (0);
148 }
149
150 RedirectBox::~RedirectBox ()
151 {
152 }
153
154 void
155 RedirectBox::object_drop (string type, uint32_t cnt, void** ptr)
156 {
157         if (type != "redirects" || cnt == 0 || ptr == 0) {
158                 return;
159         }
160
161         /* do something with the dropped redirects */
162
163         list<Redirect*> redirects;
164
165         for (uint32_t n = 0; n < cnt; ++n) {
166                 redirects.push_back ((Redirect*) ptr[n]);
167         }
168         
169         paste_redirect_list (redirects);
170 }
171
172 void
173 RedirectBox::update()
174 {
175         redisplay_redirects (0);
176 }
177
178
179 void
180 RedirectBox::set_width (Width w)
181 {
182         if (_width == w) {
183                 return;
184         }
185         _width = w;
186
187         redisplay_redirects (0);
188 }
189
190 void
191 RedirectBox::remove_redirect_gui (Redirect *redirect)
192 {
193         Insert *insert = 0;
194         Send *send = 0;
195         PortInsert *port_insert = 0;
196
197         if ((insert = dynamic_cast<Insert *> (redirect)) != 0) {
198
199                 if ((port_insert = dynamic_cast<PortInsert *> (insert)) != 0) {
200                         PortInsertUI *io_selector = reinterpret_cast<PortInsertUI *> (port_insert->get_gui());
201                         port_insert->set_gui (0);
202                         delete io_selector;
203                 } 
204
205         } else if ((send = dynamic_cast<Send *> (insert)) != 0) {
206                 SendUIWindow *sui = reinterpret_cast<SendUIWindow*> (send->get_gui());
207                 send->set_gui (0);
208                 delete sui;
209         }
210 }
211
212 void 
213 RedirectBox::build_send_action_menu ()
214
215 {
216         using namespace Menu_Helpers;
217
218         send_action_menu = new Menu;
219         send_action_menu->set_name ("ArdourContextMenu");
220         MenuList& items = send_action_menu->items();
221
222         items.push_back (MenuElem (_("New send"), mem_fun(*this, &RedirectBox::new_send)));
223         items.push_back (MenuElem (_("Show send controls"), mem_fun(*this, &RedirectBox::show_send_controls)));
224 }
225
226 void
227 RedirectBox::show_send_controls ()
228
229 {
230 }
231
232 void
233 RedirectBox::new_send ()
234
235 {
236 }
237
238 void
239 RedirectBox::show_redirect_menu (gint arg)
240 {
241         if (redirect_menu == 0) {
242                 redirect_menu = build_redirect_menu ();
243         }
244
245         paste_action->set_sensitive (!_rr_selection.redirects.empty());
246
247         redirect_menu->popup (1, arg);
248 }
249
250 void
251 RedirectBox::redirect_drag_begin (GdkDragContext *context)
252 {
253         redirect_drag_in_progress = true;
254 }
255
256 void
257 RedirectBox::redirect_drag_end (GdkDragContext *context)
258 {
259         redirect_drag_in_progress = false;
260 }
261
262 bool
263 RedirectBox::redirect_button_press_event (GdkEventButton *ev)
264 {
265         TreeIter iter;
266         TreeModel::Path path;
267         TreeViewColumn* column;
268         int cellx;
269         int celly;
270         Redirect* redirect = 0;
271         int ret = false;
272         bool selected = false;
273
274         if (redirect_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
275                 if ((iter = model->get_iter (path))) {
276                         redirect = (*iter)[columns.redirect];
277                         selected = redirect_display.get_selection()->is_selected (iter);
278                 }
279                 
280         }
281         
282         if (redirect && Keyboard::is_delete_event (ev)) {
283                 
284                 Glib::signal_idle().connect (bind (mem_fun(*this, &RedirectBox::idle_delete_redirect), redirect));
285                 ret = true;
286                 
287         } else if (redirect && (Keyboard::is_edit_event (ev) || (ev->button == 1 && ev->type == GDK_2BUTTON_PRESS && ev->state == 0))) {
288                 
289                 if (_session.engine().connected()) {
290                         /* XXX giving an error message here is hard, because we may be in the midst of a button press */
291                         edit_redirect (redirect);
292                 }
293                 ret = true;
294                 
295         } else if (Keyboard::is_context_menu_event (ev)) {
296
297                 show_redirect_menu(ev->time);
298                 ret = true;
299
300         } else if (redirect && ev->button == 2 && ev->state == 0) {
301                 
302                 redirect->set_active (!redirect->active(), this);
303                 ret = true;
304
305         } 
306         else if (redirect && ev->button == 1 && selected) {
307
308                 // this is purely informational but necessary
309                 RedirectSelected (redirect); // emit
310         }
311         
312         return ret;
313 }
314
315 Menu *
316 RedirectBox::build_redirect_menu ()
317 {
318         redirect_menu = dynamic_cast<Gtk::Menu*>(ActionManager::get_widget("/redirectmenu") );
319         redirect_menu->set_name ("ArdourContextMenu");
320
321         show_all_children();
322
323         return redirect_menu;
324 }
325
326 void
327 RedirectBox::selection_changed ()
328 {
329         bool sensitive = (redirect_display.get_selection()->count_selected_rows()) ? true : false;
330         ActionManager::set_sensitive (ActionManager::plugin_selection_sensitive_actions, sensitive);
331 }
332
333 void
334 RedirectBox::select_all_redirects ()
335 {
336         redirect_display.get_selection()->select_all();
337 }
338
339 void
340 RedirectBox::deselect_all_redirects ()
341 {
342         redirect_display.get_selection()->unselect_all();
343 }
344
345 void
346 RedirectBox::choose_plugin ()
347 {
348         sigc::connection newplug_connection = _plugin_selector.PluginCreated.connect (mem_fun(*this,&RedirectBox::insert_plugin_chosen));
349         _plugin_selector.show_all();
350         _plugin_selector.run ();
351         newplug_connection.disconnect();
352 }
353
354 void
355 RedirectBox::insert_plugin_chosen (Plugin *plugin)
356 {
357         if (plugin) {
358
359                 Redirect *redirect = new PluginInsert (_session, *plugin, _placement);
360                 
361                 redirect->active_changed.connect (mem_fun(*this, &RedirectBox::show_redirect_active));
362
363                 uint32_t err_streams;
364
365                 if (_route.add_redirect (redirect, this, &err_streams)) {
366                         wierd_plugin_dialog (*plugin, err_streams, _route);
367                         delete redirect;
368                 }
369         }
370 }
371
372 void
373 RedirectBox::wierd_plugin_dialog (Plugin& p, uint32_t streams, IO& io)
374 {
375         ArdourDialog dialog ("wierd plugin dialog");
376         Label label;
377
378         /* i hate this kind of code */
379
380         if (streams > p.get_info().n_inputs) {
381                 label.set_text (string_compose (_(
382 "You attempted to add a plugin (%1).\n"
383 "The plugin has %2 inputs\n"
384 "but at the insertion point, there are\n"
385 "%3 active signal streams.\n"
386 "\n"
387 "This makes no sense - you are throwing away\n"
388 "part of the signal."),
389                                          p.name(),
390                                          p.get_info().n_inputs,
391                                          streams));
392         } else if (streams < p.get_info().n_inputs) {
393                 label.set_text (string_compose (_(
394 "You attempted to add a plugin (%1).\n"
395 "The plugin has %2 inputs\n"
396 "but at the insertion point there are\n"
397 "only %3 active signal streams.\n"
398 "\n"
399 "This makes no sense - unless the plugin supports\n"
400 "side-chain inputs. A future version of Ardour will\n"
401 "support this type of configuration."),
402                                          p.name(),
403                                          p.get_info().n_inputs,
404                                          streams));
405         } else {
406                 label.set_text (string_compose (_(
407 "You attempted to add a plugin (%1).\n"
408 "\n"
409 "The I/O configuration doesn't make sense:\n"
410 "\n" 
411 "The plugin has %2 inputs and %3 outputs.\n"
412 "The track/bus has %4 inputs and %5 outputs.\n"
413 "The insertion point, has %6 active signals.\n"
414 "\n"
415 "Ardour does not understand what to do in such situations.\n"),
416                                          p.name(),
417                                          p.get_info().n_inputs,
418                                          p.get_info().n_outputs,
419                                          io.n_inputs(),
420                                          io.n_outputs(),
421                                          streams));
422         }
423
424         dialog.get_vbox()->pack_start (label);
425         dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
426
427         dialog.set_name (X_("PluginIODialog"));
428         dialog.set_position (Gtk::WIN_POS_MOUSE);
429         dialog.set_modal (true);
430         dialog.show_all ();
431
432         dialog.run ();
433 }
434
435 void
436 RedirectBox::choose_insert ()
437 {
438         Redirect *redirect = new PortInsert (_session, _placement);
439         redirect->active_changed.connect (mem_fun(*this, &RedirectBox::show_redirect_active));
440         _route.add_redirect (redirect, this);
441 }
442
443 void
444 RedirectBox::choose_send ()
445 {
446         Send *send = new Send (_session, _placement);
447
448         /* XXX need redirect lock on route */
449
450         send->ensure_io (0, _route.max_redirect_outs(), false, this);
451         
452         IOSelectorWindow *ios = new IOSelectorWindow (_session, *send, false, true);
453         
454         ios->show_all ();
455         ios->selector().Finished.connect (bind (mem_fun(*this, &RedirectBox::send_io_finished), static_cast<Redirect*>(send), ios));
456 }
457
458 void
459 RedirectBox::send_io_finished (IOSelector::Result r, Redirect* redirect, IOSelectorWindow* ios)
460 {
461         switch (r) {
462         case IOSelector::Cancelled:
463                 delete redirect;
464                 break;
465
466         case IOSelector::Accepted:
467                 _route.add_redirect (redirect, this);
468                 break;
469         }
470
471         delete_when_idle (ios);
472 }
473
474 void
475 RedirectBox::redisplay_redirects (void *src)
476 {
477         ENSURE_GUI_THREAD(bind (mem_fun(*this, &RedirectBox::redisplay_redirects), src));
478
479         if (no_redirect_redisplay) {
480                 return;
481         }
482
483         ignore_delete = true;
484         model->clear ();
485         ignore_delete = false;
486
487         redirect_active_connections.clear ();
488         redirect_name_connections.clear ();
489
490         _route.foreach_redirect (this, &RedirectBox::add_redirect_to_display);
491
492         switch (_placement) {
493         case PreFader:
494                 build_redirect_tooltip(redirect_eventbox, _("Pre-fader inserts, sends & plugins:"));
495                 break;
496         case PostFader:
497                 build_redirect_tooltip(redirect_eventbox, _("Post-fader inserts, sends & plugins:"));
498                 break;
499         }
500 }
501
502 void
503 RedirectBox::add_redirect_to_display (Redirect *redirect)
504 {
505         if (redirect->placement() != _placement) {
506                 return;
507         }
508         
509         Gtk::TreeModel::Row row = *(model->append());
510         row[columns.text] = redirect_name (*redirect);
511         row[columns.redirect] = redirect;
512         
513         show_redirect_active (redirect, this);
514
515         redirect_active_connections.push_back (redirect->active_changed.connect (mem_fun(*this, &RedirectBox::show_redirect_active)));
516         redirect_name_connections.push_back (redirect->name_changed.connect (bind (mem_fun(*this, &RedirectBox::show_redirect_name), redirect)));
517 }
518
519 string
520 RedirectBox::redirect_name (Redirect& redirect)
521 {
522         Send *send;
523         string name_display;
524
525         if (!redirect.active()) {
526                 name_display = " (";
527         }
528
529         if ((send = dynamic_cast<Send *> (&redirect)) != 0) {
530
531                 name_display += '>';
532
533                 /* grab the send name out of its overall name */
534
535                 string::size_type lbracket, rbracket;
536                 lbracket = send->name().find ('[');
537                 rbracket = send->name().find (']');
538
539                 switch (_width) {
540                 case Wide:
541                         name_display += send->name().substr (lbracket+1, lbracket-rbracket-1);
542                         break;
543                 case Narrow:
544                         name_display += PBD::short_version (send->name().substr (lbracket+1, lbracket-rbracket-1), 4);
545                         break;
546                 }
547
548         } else {
549
550                 switch (_width) {
551                 case Wide:
552                         name_display += redirect.name();
553                         break;
554                 case Narrow:
555                         name_display += PBD::short_version (redirect.name(), 5);
556                         break;
557                 }
558
559         }
560
561         if (!redirect.active()) {
562                 name_display += ')';
563         }
564
565         return name_display;
566 }
567
568 void
569 RedirectBox::build_redirect_tooltip (EventBox& box, string start)
570 {
571         string tip(start);
572
573         Gtk::TreeModel::Children children = model->children();
574         for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
575                 Gtk::TreeModel::Row row = *iter;
576                 tip += '\n';
577                 tip += row[columns.text];
578         }
579         ARDOUR_UI::instance()->tooltips().set_tip (box, tip);
580 }
581
582 void
583 RedirectBox::show_redirect_name (void* src, Redirect *redirect)
584 {
585         ENSURE_GUI_THREAD(bind (mem_fun(*this, &RedirectBox::show_redirect_name), src, redirect));
586         
587         show_redirect_active (redirect, src);
588 }
589
590 void
591 RedirectBox::show_redirect_active (Redirect *redirect, void *src)
592 {
593         ENSURE_GUI_THREAD(bind (mem_fun(*this, &RedirectBox::show_redirect_active), redirect, src));
594
595         Gtk::TreeModel::Children children = model->children();
596         Gtk::TreeModel::Children::iterator iter = children.begin();
597
598         while( iter != children.end())
599         {
600                 if ((*iter)[columns.redirect] == redirect)
601                         break;
602                 iter++;
603         }
604
605         (*iter)[columns.text] = redirect_name (*redirect);
606
607         if (redirect->active()) {
608                 (*iter)[columns.color] = *active_redirect_color;
609         } else {
610                 (*iter)[columns.color] = *inactive_redirect_color;
611         }
612 }
613
614 void
615 RedirectBox::row_deleted (const Gtk::TreeModel::Path& path)
616 {
617         if (!ignore_delete) {
618                 compute_redirect_sort_keys ();
619         }
620 }
621
622 void
623 RedirectBox::compute_redirect_sort_keys ()
624 {
625         uint32_t sort_key = 0;
626         Gtk::TreeModel::Children children = model->children();
627
628         for (Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
629                 Redirect *redirect = (*iter)[columns.redirect];
630                 redirect->set_sort_key (sort_key);
631                 sort_key++;
632         }
633
634         if (_route.sort_redirects ()) {
635
636                 redisplay_redirects (0);
637
638                 /* now tell them about the problem */
639
640                 ArdourDialog dialog ("wierd plugin dialog");
641                 Label label;
642
643                 label.set_text (_("\
644 You cannot reorder this set of redirects\n\
645 in that way because the inputs and\n\
646 outputs do not work correctly."));
647
648                 dialog.get_vbox()->pack_start (label);
649                 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
650
651                 dialog.set_name (X_("PluginIODialog"));
652                 dialog.set_position (Gtk::WIN_POS_MOUSE);
653                 dialog.set_modal (true);
654                 dialog.show_all ();
655
656                 dialog.run ();
657         }
658 }
659
660 void
661 RedirectBox::rename_redirects ()
662 {
663         vector<Redirect*> to_be_renamed;
664         
665         get_selected_redirects (to_be_renamed);
666
667         if (to_be_renamed.empty()) {
668                 return;
669         }
670
671         for (vector<Redirect*>::iterator i = to_be_renamed.begin(); i != to_be_renamed.end(); ++i) {
672                 rename_redirect (*i);
673         }
674 }
675
676 void
677 RedirectBox::cut_redirects ()
678 {
679         vector<Redirect*> to_be_removed;
680         
681         get_selected_redirects (to_be_removed);
682
683         if (to_be_removed.empty()) {
684                 return;
685         }
686
687         /* this essentially transfers ownership of the redirect
688            of the redirect from the route to the mixer
689            selection.
690         */
691         
692         _rr_selection.set (to_be_removed);
693
694         for (vector<Redirect*>::iterator i = to_be_removed.begin(); i != to_be_removed.end(); ++i) {
695                 
696                 void* gui = (*i)->get_gui ();
697                 
698                 if (gui) {
699                         static_cast<Gtk::Widget*>(gui)->hide ();
700                 }
701                 
702                 if (_route.remove_redirect (*i, this)) {
703                         /* removal failed */
704                         _rr_selection.remove (*i);
705                 }
706
707         }
708 }
709
710 void
711 RedirectBox::copy_redirects ()
712 {
713         vector<Redirect*> to_be_copied;
714         vector<Redirect*> copies;
715
716         get_selected_redirects (to_be_copied);
717
718         if (to_be_copied.empty()) {
719                 return;
720         }
721
722         for (vector<Redirect*>::iterator i = to_be_copied.begin(); i != to_be_copied.end(); ++i) {
723                 copies.push_back (Redirect::clone (**i));
724         }
725
726         _rr_selection.set (copies);
727 }
728
729 gint
730 RedirectBox::idle_delete_redirect (Redirect *redirect)
731 {
732         /* NOT copied to _mixer.selection() */
733
734         if (_route.remove_redirect (redirect, this)) {
735                 /* removal failed */
736                 return FALSE;
737         }
738
739         delete redirect;
740         return FALSE;
741 }
742
743 void
744 RedirectBox::rename_redirect (Redirect* redirect)
745 {
746         ArdourPrompter name_prompter (true);
747         string result;
748         name_prompter.set_prompt (_("rename redirect"));
749         name_prompter.set_initial_text (redirect->name());
750         name_prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
751         name_prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
752         name_prompter.show_all ();
753
754         switch (name_prompter.run ()) {
755
756         case Gtk::RESPONSE_ACCEPT:
757         name_prompter.get_result (result);
758         if (result.length()) {
759                         redirect->set_name (result, this);
760                 }       
761                 break;
762         }
763
764         return;
765   
766 }
767
768 void
769 RedirectBox::cut_redirect (Redirect *redirect)
770 {
771         /* this essentially transfers ownership of the redirect
772            of the redirect from the route to the mixer
773            selection.
774         */
775
776         _rr_selection.add (redirect);
777         
778         void* gui = redirect->get_gui ();
779
780         if (gui) {
781                 static_cast<Gtk::Widget*>(gui)->hide ();
782         }
783         
784         if (_route.remove_redirect (redirect, this)) {
785                 _rr_selection.remove (redirect);
786         }
787 }
788
789 void
790 RedirectBox::copy_redirect (Redirect *redirect)
791 {
792         Redirect* copy = Redirect::clone (*redirect);
793         _rr_selection.add (copy);
794 }
795
796 void
797 RedirectBox::paste_redirects ()
798 {
799         if (_rr_selection.redirects.empty()) {
800                 return;
801         }
802
803         paste_redirect_list (_rr_selection.redirects);
804 }
805
806 void
807 RedirectBox::paste_redirect_list (list<Redirect*>& redirects)
808 {
809         list<Redirect*> copies;
810
811         for (list<Redirect*>::iterator i = redirects.begin(); i != redirects.end(); ++i) {
812
813                 Redirect* copy = Redirect::clone (**i);
814
815                 copy->set_placement (_placement, this);
816                 copies.push_back (copy);
817         }
818
819         if (_route.add_redirects (copies, this)) {
820                 for (list<Redirect*>::iterator i = copies.begin(); i != copies.end(); ++i) {
821                         delete *i;
822                 }
823
824                 string msg = _(
825                         "Copying the set of redirects on the clipboard failed,\n\
826 probably because the I/O configuration of the plugins\n\
827 could not match the configuration of this track.");
828                 MessageDialog am (msg);
829                 am.run ();
830         }
831 }
832
833 void
834 RedirectBox::activate_redirect (Redirect *r)
835 {
836         r->set_active (true, 0);
837 }
838
839 void
840 RedirectBox::deactivate_redirect (Redirect *r)
841 {
842         r->set_active (false, 0);
843 }
844
845 void
846 RedirectBox::get_selected_redirects (vector<Redirect*>& redirects)
847 {
848     vector<Gtk::TreeModel::Path> pathlist = redirect_display.get_selection()->get_selected_rows();
849  
850         for (vector<Gtk::TreeModel::Path>::iterator iter = pathlist.begin(); iter != pathlist.end(); ++iter)
851                 redirects.push_back ((*(model->get_iter(*iter)))[columns.redirect]);
852 }
853
854 void
855 RedirectBox::for_selected_redirects (void (RedirectBox::*pmf)(Redirect*))
856 {
857     vector<Gtk::TreeModel::Path> pathlist = redirect_display.get_selection()->get_selected_rows();
858
859         for (vector<Gtk::TreeModel::Path>::iterator iter = pathlist.begin(); iter != pathlist.end(); ++iter) {
860                 Redirect* redirect = (*(model->get_iter(*iter)))[columns.redirect];
861                 (this->*pmf)(redirect);
862         }
863 }
864
865 void
866 RedirectBox::clone_redirects ()
867 {
868         RouteSelection& routes (_rr_selection.routes);
869
870         if (!routes.empty()) {
871                 if (_route.copy_redirects (*routes.front(), _placement)) {
872                         string msg = _(
873 "Copying the set of redirects on the clipboard failed,\n\
874 probably because the I/O configuration of the plugins\n\
875 could not match the configuration of this track.");
876                         MessageDialog am (msg);
877                         am.run ();
878                 }
879         }
880 }
881
882 void
883 RedirectBox::all_redirects_active (bool state)
884 {
885         _route.all_redirects_active (state);
886 }
887
888 void
889 RedirectBox::clear_redirects()
890 {
891         string prompt;
892         vector<string> choices;
893
894         if (dynamic_cast<AudioTrack*>(&_route) != 0) {
895                 prompt = _("Do you really want to remove all redirects from this track?\n"
896                            "(this cannot be undone)");
897         } else {
898                 prompt = _("Do you really want to remove all redirects from this bus?\n"
899                            "(this cannot be undone)");
900         }
901
902         choices.push_back (_("Cancel"));
903         choices.push_back (_("Yes, remove them all"));
904
905         Gtkmm2ext::Choice prompter (prompt, choices);
906
907         if (prompter.run () == 1) {
908                 _route.clear_redirects (this);
909         }
910 }
911
912 void
913 RedirectBox::edit_redirect (Redirect* redirect)
914 {
915         Insert *insert;
916
917         if (dynamic_cast<AudioTrack*>(&_route) != 0) {
918
919                 if (dynamic_cast<AudioTrack*> (&_route)->freeze_state() == AudioTrack::Frozen) {
920                         return;
921                 }
922         }
923         
924         if ((insert = dynamic_cast<Insert *> (redirect)) == 0) {
925                 
926                 /* its a send */
927                 
928                 if (!_session.engine().connected()) {
929                         return;
930                 }
931
932                 Send *send = dynamic_cast<Send*> (redirect);
933                 
934                 SendUIWindow *send_ui;
935                 
936                 if (send->get_gui() == 0) {
937                         
938                         string title;
939                         title = string_compose(_("ardour: %1"), send->name());  
940                         
941                         send_ui = new SendUIWindow (*send, _session);
942                         send_ui->set_title (title);
943                         send->set_gui (send_ui);
944                         
945                 } else {
946                         send_ui = reinterpret_cast<SendUIWindow *> (send->get_gui());
947                 }
948                 
949                 if (send_ui->is_visible()) {
950                         send_ui->get_window()->raise ();
951                 } else {
952                         send_ui->show_all ();
953                 }
954                 
955         } else {
956                 
957                 /* its an insert */
958                 
959                 PluginInsert *plugin_insert;
960                 PortInsert *port_insert;
961                 
962                 if ((plugin_insert = dynamic_cast<PluginInsert *> (insert)) != 0) {
963                         
964                         PluginUIWindow *plugin_ui;
965                         
966                         if (plugin_insert->get_gui() == 0) {
967                                 
968                                 string title;
969                                 string maker = plugin_insert->plugin().maker();
970                                 string::size_type email_pos;
971                                 
972                                 if ((email_pos = maker.find_first_of ('<')) != string::npos) {
973                                         maker = maker.substr (0, email_pos - 1);
974                                 }
975                                 
976                                 if (maker.length() > 32) {
977                                         maker = maker.substr (0, 32);
978                                         maker += " ...";
979                                 }
980
981                                 title = string_compose(_("ardour: %1: %2 (by %3)"), _route.name(), plugin_insert->name(), maker);       
982                                 
983                                 plugin_ui = new PluginUIWindow (_session.engine(), *plugin_insert);
984                                 if (_owner_is_mixer) {
985                                         ARDOUR_UI::instance()->the_mixer()->ensure_float (*plugin_ui);
986                                 } else {
987                                         ARDOUR_UI::instance()->the_editor().ensure_float (*plugin_ui);
988                                 }
989                                 plugin_ui->set_title (title);
990                                 plugin_insert->set_gui (plugin_ui);
991                                 
992                         } else {
993                                 plugin_ui = reinterpret_cast<PluginUIWindow *> (plugin_insert->get_gui());
994                         }
995                         
996                         if (plugin_ui->is_visible()) {
997                                 plugin_ui->get_window()->raise ();
998                         } else {
999                                 plugin_ui->show_all ();
1000                         }
1001                         
1002                 } else if ((port_insert = dynamic_cast<PortInsert *> (insert)) != 0) {
1003                         
1004                         if (!_session.engine().connected()) {
1005                                 MessageDialog msg ( _("Not connected to JACK - no I/O changes are possible"));
1006                                 msg.run ();
1007                                 return;
1008                         }
1009
1010                         PortInsertWindow *io_selector;
1011
1012                         if (port_insert->get_gui() == 0) {
1013                                 io_selector = new PortInsertWindow (_session, *port_insert);
1014                                 port_insert->set_gui (io_selector);
1015                                 
1016                         } else {
1017                                 io_selector = reinterpret_cast<PortInsertWindow *> (port_insert->get_gui());
1018                         }
1019                         
1020                         if (io_selector->is_visible()) {
1021                                 io_selector->get_window()->raise ();
1022                         } else {
1023                                 io_selector->show_all ();
1024                         }
1025                 }
1026         }
1027 }
1028
1029 bool
1030 RedirectBox::enter_box (GdkEventCrossing *ev, RedirectBox* rb)
1031 {
1032         switch (ev->detail) {
1033         case GDK_NOTIFY_INFERIOR:
1034                 break;
1035
1036         case GDK_NOTIFY_VIRTUAL:
1037                 /* fallthru */
1038
1039         default:
1040                 _current_redirect_box = rb;
1041         }
1042
1043         return false;
1044 }
1045
1046 void
1047 RedirectBox::register_actions ()
1048 {
1049         Glib::RefPtr<Gtk::ActionGroup> popup_act_grp = Gtk::ActionGroup::create(X_("redirectmenu"));
1050         Glib::RefPtr<Action> act;
1051
1052         /* new stuff */
1053         ActionManager::register_action (popup_act_grp, X_("newplugin"), _("New Plugin ..."),  sigc::ptr_fun (RedirectBox::rb_choose_plugin));
1054         ActionManager::register_action (popup_act_grp, X_("newinsert"), _("New Insert"),  sigc::ptr_fun (RedirectBox::rb_choose_insert));
1055         ActionManager::register_action (popup_act_grp, X_("newsend"), _("New Send ..."),  sigc::ptr_fun (RedirectBox::rb_choose_send));
1056         ActionManager::register_action (popup_act_grp, X_("clear"), _("Clear"),  sigc::ptr_fun (RedirectBox::rb_clear));
1057
1058         /* standard editing stuff */
1059         act = ActionManager::register_action (popup_act_grp, X_("cut"), _("Cut"),  sigc::ptr_fun (RedirectBox::rb_cut));
1060         ActionManager::plugin_selection_sensitive_actions.push_back(act);
1061         act = ActionManager::register_action (popup_act_grp, X_("copy"), _("Copy"),  sigc::ptr_fun (RedirectBox::rb_copy));
1062         ActionManager::plugin_selection_sensitive_actions.push_back(act);
1063         paste_action = ActionManager::register_action (popup_act_grp, X_("paste"), _("Paste"),  sigc::ptr_fun (RedirectBox::rb_paste));
1064         act = ActionManager::register_action (popup_act_grp, X_("rename"), _("Rename"),  sigc::ptr_fun (RedirectBox::rb_rename));
1065         ActionManager::plugin_selection_sensitive_actions.push_back(act);
1066         ActionManager::register_action (popup_act_grp, X_("selectall"), _("Select All"),  sigc::ptr_fun (RedirectBox::rb_select_all));
1067         ActionManager::register_action (popup_act_grp, X_("deselectall"), _("Deselect All"),  sigc::ptr_fun (RedirectBox::rb_deselect_all));
1068                 
1069         /* activation */
1070         act = ActionManager::register_action (popup_act_grp, X_("activate"), _("Activate"),  sigc::ptr_fun (RedirectBox::rb_activate));
1071         ActionManager::plugin_selection_sensitive_actions.push_back(act);
1072         act = ActionManager::register_action (popup_act_grp, X_("deactivate"), _("Deactivate"),  sigc::ptr_fun (RedirectBox::rb_deactivate));
1073         ActionManager::plugin_selection_sensitive_actions.push_back(act);
1074         ActionManager::register_action (popup_act_grp, X_("activate_all"), _("Activate all"),  sigc::ptr_fun (RedirectBox::rb_activate_all));
1075         ActionManager::register_action (popup_act_grp, X_("deactivate_all"), _("Deactivate all"),  sigc::ptr_fun (RedirectBox::rb_deactivate_all));
1076
1077         /* show editors */
1078         act = ActionManager::register_action (popup_act_grp, X_("edit"), _("Edit"),  sigc::ptr_fun (RedirectBox::rb_edit));
1079         ActionManager::plugin_selection_sensitive_actions.push_back(act);
1080
1081         ActionManager::add_action_group (popup_act_grp);
1082 }
1083
1084 void
1085 RedirectBox::rb_choose_plugin ()
1086 {
1087         if (_current_redirect_box == 0) {
1088                 return;
1089         }
1090         _current_redirect_box->choose_plugin ();
1091 }
1092
1093 void
1094 RedirectBox::rb_choose_insert ()
1095 {
1096         if (_current_redirect_box == 0) {
1097                 return;
1098         }
1099         _current_redirect_box->choose_insert ();
1100 }
1101
1102 void
1103 RedirectBox::rb_choose_send ()
1104 {
1105         if (_current_redirect_box == 0) {
1106                 return;
1107         }
1108         _current_redirect_box->choose_send ();
1109 }
1110
1111 void
1112 RedirectBox::rb_clear ()
1113 {
1114         if (_current_redirect_box == 0) {
1115                 return;
1116         }
1117
1118         _current_redirect_box->clear_redirects ();
1119 }
1120
1121 void
1122 RedirectBox::rb_cut ()
1123 {
1124         if (_current_redirect_box == 0) {
1125                 return;
1126         }
1127
1128         _current_redirect_box->cut_redirects ();
1129 }
1130
1131 void
1132 RedirectBox::rb_copy ()
1133 {
1134         if (_current_redirect_box == 0) {
1135                 return;
1136         }
1137         _current_redirect_box->copy_redirects ();
1138 }
1139
1140 void
1141 RedirectBox::rb_paste ()
1142 {
1143         if (_current_redirect_box == 0) {
1144                 return;
1145         }
1146
1147         _current_redirect_box->paste_redirects ();
1148 }
1149
1150 void
1151 RedirectBox::rb_rename ()
1152 {
1153         if (_current_redirect_box == 0) {
1154                 return;
1155         }
1156         _current_redirect_box->rename_redirects ();
1157 }
1158
1159 void
1160 RedirectBox::rb_select_all ()
1161 {
1162         if (_current_redirect_box == 0) {
1163                 return;
1164         }
1165
1166         _current_redirect_box->select_all_redirects ();
1167 }
1168
1169 void
1170 RedirectBox::rb_deselect_all ()
1171 {
1172         if (_current_redirect_box == 0) {
1173                 return;
1174         }
1175
1176         _current_redirect_box->deselect_all_redirects ();
1177 }
1178
1179 void
1180 RedirectBox::rb_activate ()
1181 {
1182         if (_current_redirect_box == 0) {
1183                 return;
1184         }
1185
1186         _current_redirect_box->for_selected_redirects (&RedirectBox::activate_redirect);
1187 }
1188
1189 void
1190 RedirectBox::rb_deactivate ()
1191 {
1192         if (_current_redirect_box == 0) {
1193                 return;
1194         }
1195         _current_redirect_box->for_selected_redirects (&RedirectBox::deactivate_redirect);
1196 }
1197
1198 void
1199 RedirectBox::rb_activate_all ()
1200 {
1201         if (_current_redirect_box == 0) {
1202                 return;
1203         }
1204
1205         _current_redirect_box->all_redirects_active (true);
1206 }
1207
1208 void
1209 RedirectBox::rb_deactivate_all ()
1210 {
1211         if (_current_redirect_box == 0) {
1212                 return;
1213         }
1214         _current_redirect_box->all_redirects_active (false);
1215 }
1216
1217 void
1218 RedirectBox::rb_edit ()
1219 {
1220         if (_current_redirect_box == 0) {
1221                 return;
1222         }
1223
1224         _current_redirect_box->for_selected_redirects (&RedirectBox::edit_redirect);
1225 }
1226