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