Prompter now prevents blank strings or unaltered names & now has a horizontal orienta...
[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.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
749         name_prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
750         name_prompter.show_all ();
751
752         switch (name_prompter.run ()) {
753
754         case Gtk::RESPONSE_ACCEPT:
755         name_prompter.get_result (result);
756         if (result.length()) {
757                         redirect->set_name (result, this);
758                 }       
759                 break;
760         }
761
762         return;
763   
764 }
765
766 void
767 RedirectBox::cut_redirect (Redirect *redirect)
768 {
769         /* this essentially transfers ownership of the redirect
770            of the redirect from the route to the mixer
771            selection.
772         */
773
774         _rr_selection.add (redirect);
775         
776         void* gui = redirect->get_gui ();
777
778         if (gui) {
779                 static_cast<Gtk::Widget*>(gui)->hide ();
780         }
781         
782         if (_route.remove_redirect (redirect, this)) {
783                 _rr_selection.remove (redirect);
784         }
785 }
786
787 void
788 RedirectBox::copy_redirect (Redirect *redirect)
789 {
790         Redirect* copy = Redirect::clone (*redirect);
791         _rr_selection.add (copy);
792 }
793
794 void
795 RedirectBox::paste_redirects ()
796 {
797         if (_rr_selection.redirects.empty()) {
798                 return;
799         }
800
801         paste_redirect_list (_rr_selection.redirects);
802 }
803
804 void
805 RedirectBox::paste_redirect_list (list<Redirect*>& redirects)
806 {
807         list<Redirect*> copies;
808
809         for (list<Redirect*>::iterator i = redirects.begin(); i != redirects.end(); ++i) {
810
811                 Redirect* copy = Redirect::clone (**i);
812
813                 copy->set_placement (_placement, this);
814                 copies.push_back (copy);
815         }
816
817         if (_route.add_redirects (copies, this)) {
818                 for (list<Redirect*>::iterator i = copies.begin(); i != copies.end(); ++i) {
819                         delete *i;
820                 }
821
822                 string msg = _(
823                         "Copying the set of redirects on the clipboard failed,\n\
824 probably because the I/O configuration of the plugins\n\
825 could not match the configuration of this track.");
826                 MessageDialog am (msg);
827                 am.run ();
828         }
829 }
830
831 void
832 RedirectBox::activate_redirect (Redirect *r)
833 {
834         r->set_active (true, 0);
835 }
836
837 void
838 RedirectBox::deactivate_redirect (Redirect *r)
839 {
840         r->set_active (false, 0);
841 }
842
843 void
844 RedirectBox::get_selected_redirects (vector<Redirect*>& redirects)
845 {
846     vector<Gtk::TreeModel::Path> pathlist = redirect_display.get_selection()->get_selected_rows();
847  
848         for (vector<Gtk::TreeModel::Path>::iterator iter = pathlist.begin(); iter != pathlist.end(); ++iter)
849                 redirects.push_back ((*(model->get_iter(*iter)))[columns.redirect]);
850 }
851
852 void
853 RedirectBox::for_selected_redirects (void (RedirectBox::*pmf)(Redirect*))
854 {
855     vector<Gtk::TreeModel::Path> pathlist = redirect_display.get_selection()->get_selected_rows();
856
857         for (vector<Gtk::TreeModel::Path>::iterator iter = pathlist.begin(); iter != pathlist.end(); ++iter) {
858                 Redirect* redirect = (*(model->get_iter(*iter)))[columns.redirect];
859                 (this->*pmf)(redirect);
860         }
861 }
862
863 void
864 RedirectBox::clone_redirects ()
865 {
866         RouteSelection& routes (_rr_selection.routes);
867
868         if (!routes.empty()) {
869                 if (_route.copy_redirects (*routes.front(), _placement)) {
870                         string msg = _(
871 "Copying the set of redirects on the clipboard failed,\n\
872 probably because the I/O configuration of the plugins\n\
873 could not match the configuration of this track.");
874                         MessageDialog am (msg);
875                         am.run ();
876                 }
877         }
878 }
879
880 void
881 RedirectBox::all_redirects_active (bool state)
882 {
883         _route.all_redirects_active (state);
884 }
885
886 void
887 RedirectBox::clear_redirects()
888 {
889         string prompt;
890         vector<string> choices;
891
892         if (dynamic_cast<AudioTrack*>(&_route) != 0) {
893                 prompt = _("Do you really want to remove all redirects from this track?\n"
894                            "(this cannot be undone)");
895         } else {
896                 prompt = _("Do you really want to remove all redirects from this bus?\n"
897                            "(this cannot be undone)");
898         }
899
900         choices.push_back (_("Cancel"));
901         choices.push_back (_("Yes, remove them all"));
902
903         Gtkmm2ext::Choice prompter (prompt, choices);
904
905         if (prompter.run () == 1) {
906                 _route.clear_redirects (this);
907         }
908 }
909
910 void
911 RedirectBox::edit_redirect (Redirect* redirect)
912 {
913         Insert *insert;
914
915         if (dynamic_cast<AudioTrack*>(&_route) != 0) {
916
917                 if (dynamic_cast<AudioTrack*> (&_route)->freeze_state() == AudioTrack::Frozen) {
918                         return;
919                 }
920         }
921         
922         if ((insert = dynamic_cast<Insert *> (redirect)) == 0) {
923                 
924                 /* its a send */
925                 
926                 if (!_session.engine().connected()) {
927                         return;
928                 }
929
930                 Send *send = dynamic_cast<Send*> (redirect);
931                 
932                 SendUIWindow *send_ui;
933                 
934                 if (send->get_gui() == 0) {
935                         
936                         string title;
937                         title = string_compose(_("ardour: %1"), send->name());  
938                         
939                         send_ui = new SendUIWindow (*send, _session);
940                         send_ui->set_title (title);
941                         send->set_gui (send_ui);
942                         
943                 } else {
944                         send_ui = reinterpret_cast<SendUIWindow *> (send->get_gui());
945                 }
946                 
947                 if (send_ui->is_visible()) {
948                         send_ui->get_window()->raise ();
949                 } else {
950                         send_ui->show_all ();
951                 }
952                 
953         } else {
954                 
955                 /* its an insert */
956                 
957                 PluginInsert *plugin_insert;
958                 PortInsert *port_insert;
959                 
960                 if ((plugin_insert = dynamic_cast<PluginInsert *> (insert)) != 0) {
961                         
962                         PluginUIWindow *plugin_ui;
963                         
964                         if (plugin_insert->get_gui() == 0) {
965                                 
966                                 string title;
967                                 string maker = plugin_insert->plugin().maker();
968                                 string::size_type email_pos;
969                                 
970                                 if ((email_pos = maker.find_first_of ('<')) != string::npos) {
971                                         maker = maker.substr (0, email_pos - 1);
972                                 }
973                                 
974                                 if (maker.length() > 32) {
975                                         maker = maker.substr (0, 32);
976                                         maker += " ...";
977                                 }
978
979                                 title = string_compose(_("ardour: %1: %2 (by %3)"), _route.name(), plugin_insert->name(), maker);       
980                                 
981                                 plugin_ui = new PluginUIWindow (_session.engine(), *plugin_insert);
982                                 if (_owner_is_mixer) {
983                                         ARDOUR_UI::instance()->the_mixer()->ensure_float (*plugin_ui);
984                                 } else {
985                                         ARDOUR_UI::instance()->the_editor().ensure_float (*plugin_ui);
986                                 }
987                                 plugin_ui->set_title (title);
988                                 plugin_insert->set_gui (plugin_ui);
989                                 
990                         } else {
991                                 plugin_ui = reinterpret_cast<PluginUIWindow *> (plugin_insert->get_gui());
992                         }
993                         
994                         if (plugin_ui->is_visible()) {
995                                 plugin_ui->get_window()->raise ();
996                         } else {
997                                 plugin_ui->show_all ();
998                         }
999                         
1000                 } else if ((port_insert = dynamic_cast<PortInsert *> (insert)) != 0) {
1001                         
1002                         if (!_session.engine().connected()) {
1003                                 MessageDialog msg ( _("Not connected to JACK - no I/O changes are possible"));
1004                                 msg.run ();
1005                                 return;
1006                         }
1007
1008                         PortInsertWindow *io_selector;
1009
1010                         if (port_insert->get_gui() == 0) {
1011                                 io_selector = new PortInsertWindow (_session, *port_insert);
1012                                 port_insert->set_gui (io_selector);
1013                                 
1014                         } else {
1015                                 io_selector = reinterpret_cast<PortInsertWindow *> (port_insert->get_gui());
1016                         }
1017                         
1018                         if (io_selector->is_visible()) {
1019                                 io_selector->get_window()->raise ();
1020                         } else {
1021                                 io_selector->show_all ();
1022                         }
1023                 }
1024         }
1025 }
1026
1027 bool
1028 RedirectBox::enter_box (GdkEventCrossing *ev, RedirectBox* rb)
1029 {
1030         switch (ev->detail) {
1031         case GDK_NOTIFY_INFERIOR:
1032                 break;
1033
1034         case GDK_NOTIFY_VIRTUAL:
1035                 /* fallthru */
1036
1037         default:
1038                 _current_redirect_box = rb;
1039         }
1040
1041         return false;
1042 }
1043
1044 void
1045 RedirectBox::register_actions ()
1046 {
1047         Glib::RefPtr<Gtk::ActionGroup> popup_act_grp = Gtk::ActionGroup::create(X_("redirectmenu"));
1048         Glib::RefPtr<Action> act;
1049
1050         /* new stuff */
1051         ActionManager::register_action (popup_act_grp, X_("newplugin"), _("New Plugin ..."),  sigc::ptr_fun (RedirectBox::rb_choose_plugin));
1052         ActionManager::register_action (popup_act_grp, X_("newinsert"), _("New Insert"),  sigc::ptr_fun (RedirectBox::rb_choose_insert));
1053         ActionManager::register_action (popup_act_grp, X_("newsend"), _("New Send ..."),  sigc::ptr_fun (RedirectBox::rb_choose_send));
1054         ActionManager::register_action (popup_act_grp, X_("clear"), _("Clear"),  sigc::ptr_fun (RedirectBox::rb_clear));
1055
1056         /* standard editing stuff */
1057         act = ActionManager::register_action (popup_act_grp, X_("cut"), _("Cut"),  sigc::ptr_fun (RedirectBox::rb_cut));
1058         ActionManager::plugin_selection_sensitive_actions.push_back(act);
1059         act = ActionManager::register_action (popup_act_grp, X_("copy"), _("Copy"),  sigc::ptr_fun (RedirectBox::rb_copy));
1060         ActionManager::plugin_selection_sensitive_actions.push_back(act);
1061         paste_action = ActionManager::register_action (popup_act_grp, X_("paste"), _("Paste"),  sigc::ptr_fun (RedirectBox::rb_paste));
1062         act = ActionManager::register_action (popup_act_grp, X_("rename"), _("Rename"),  sigc::ptr_fun (RedirectBox::rb_rename));
1063         ActionManager::plugin_selection_sensitive_actions.push_back(act);
1064         ActionManager::register_action (popup_act_grp, X_("selectall"), _("Select All"),  sigc::ptr_fun (RedirectBox::rb_select_all));
1065         ActionManager::register_action (popup_act_grp, X_("deselectall"), _("Deselect All"),  sigc::ptr_fun (RedirectBox::rb_deselect_all));
1066                 
1067         /* activation */
1068         act = ActionManager::register_action (popup_act_grp, X_("activate"), _("Activate"),  sigc::ptr_fun (RedirectBox::rb_activate));
1069         ActionManager::plugin_selection_sensitive_actions.push_back(act);
1070         act = ActionManager::register_action (popup_act_grp, X_("deactivate"), _("Deactivate"),  sigc::ptr_fun (RedirectBox::rb_deactivate));
1071         ActionManager::plugin_selection_sensitive_actions.push_back(act);
1072         ActionManager::register_action (popup_act_grp, X_("activate_all"), _("Activate all"),  sigc::ptr_fun (RedirectBox::rb_activate_all));
1073         ActionManager::register_action (popup_act_grp, X_("deactivate_all"), _("Deactivate all"),  sigc::ptr_fun (RedirectBox::rb_deactivate_all));
1074
1075         /* show editors */
1076         act = ActionManager::register_action (popup_act_grp, X_("edit"), _("Edit"),  sigc::ptr_fun (RedirectBox::rb_edit));
1077         ActionManager::plugin_selection_sensitive_actions.push_back(act);
1078
1079         ActionManager::add_action_group (popup_act_grp);
1080 }
1081
1082 void
1083 RedirectBox::rb_choose_plugin ()
1084 {
1085         if (_current_redirect_box == 0) {
1086                 return;
1087         }
1088         _current_redirect_box->choose_plugin ();
1089 }
1090
1091 void
1092 RedirectBox::rb_choose_insert ()
1093 {
1094         if (_current_redirect_box == 0) {
1095                 return;
1096         }
1097         _current_redirect_box->choose_insert ();
1098 }
1099
1100 void
1101 RedirectBox::rb_choose_send ()
1102 {
1103         if (_current_redirect_box == 0) {
1104                 return;
1105         }
1106         _current_redirect_box->choose_send ();
1107 }
1108
1109 void
1110 RedirectBox::rb_clear ()
1111 {
1112         if (_current_redirect_box == 0) {
1113                 return;
1114         }
1115
1116         _current_redirect_box->clear_redirects ();
1117 }
1118
1119 void
1120 RedirectBox::rb_cut ()
1121 {
1122         if (_current_redirect_box == 0) {
1123                 return;
1124         }
1125
1126         _current_redirect_box->cut_redirects ();
1127 }
1128
1129 void
1130 RedirectBox::rb_copy ()
1131 {
1132         if (_current_redirect_box == 0) {
1133                 return;
1134         }
1135         _current_redirect_box->copy_redirects ();
1136 }
1137
1138 void
1139 RedirectBox::rb_paste ()
1140 {
1141         if (_current_redirect_box == 0) {
1142                 return;
1143         }
1144
1145         _current_redirect_box->paste_redirects ();
1146 }
1147
1148 void
1149 RedirectBox::rb_rename ()
1150 {
1151         if (_current_redirect_box == 0) {
1152                 return;
1153         }
1154         _current_redirect_box->rename_redirects ();
1155 }
1156
1157 void
1158 RedirectBox::rb_select_all ()
1159 {
1160         if (_current_redirect_box == 0) {
1161                 return;
1162         }
1163
1164         _current_redirect_box->select_all_redirects ();
1165 }
1166
1167 void
1168 RedirectBox::rb_deselect_all ()
1169 {
1170         if (_current_redirect_box == 0) {
1171                 return;
1172         }
1173
1174         _current_redirect_box->deselect_all_redirects ();
1175 }
1176
1177 void
1178 RedirectBox::rb_activate ()
1179 {
1180         if (_current_redirect_box == 0) {
1181                 return;
1182         }
1183
1184         _current_redirect_box->for_selected_redirects (&RedirectBox::activate_redirect);
1185 }
1186
1187 void
1188 RedirectBox::rb_deactivate ()
1189 {
1190         if (_current_redirect_box == 0) {
1191                 return;
1192         }
1193         _current_redirect_box->for_selected_redirects (&RedirectBox::deactivate_redirect);
1194 }
1195
1196 void
1197 RedirectBox::rb_activate_all ()
1198 {
1199         if (_current_redirect_box == 0) {
1200                 return;
1201         }
1202
1203         _current_redirect_box->all_redirects_active (true);
1204 }
1205
1206 void
1207 RedirectBox::rb_deactivate_all ()
1208 {
1209         if (_current_redirect_box == 0) {
1210                 return;
1211         }
1212         _current_redirect_box->all_redirects_active (false);
1213 }
1214
1215 void
1216 RedirectBox::rb_edit ()
1217 {
1218         if (_current_redirect_box == 0) {
1219                 return;
1220         }
1221
1222         _current_redirect_box->for_selected_redirects (&RedirectBox::edit_redirect);
1223 }
1224