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