Logo Search packages:      
Sourcecode: ardour version File versions  Download package

audio_time_axis.cc

/*
    Copyright (C) 2000 Paul Davis 

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    $Id: audio_time_axis.cc,v 1.102 2005/11/17 21:57:02 sampo Exp $
*/

#include <cstdlib>
#include <cmath>

#include <algorithm>
#include <string>
#include <vector>

#include <sigc++/bind.h>

#include <pbd/error.h>
#include <pbd/stl_delete.h>

#include <gtkmmext/utils.h>
#include <gtkmmext/selector.h>
#include <gtkmmext/gtk_ui.h>
#include <gtkmmext/stop_signal.h>
#include <gtkmmext/bindable_button.h>

#include <ardour/session.h>
#include <ardour/session_playlist.h>
#include <ardour/audioplaylist.h>
#include <ardour/diskstream.h>
#include <ardour/utils.h>
#include <ardour/playlist.h>
#include <ardour/ladspa_plugin.h>
#include <ardour/insert.h>
#include <ardour/location.h>
#include <ardour/panner.h>

#include "ardour_ui.h"
#include "public_editor.h"
#include "audio_time_axis.h"
#include "streamview.h"
#include "canvas-simplerect.h"
#include "playlist_selector.h"
#include "plugin_selector.h"
#include "plugin_ui.h"
#include "regionview.h"
#include "automation_gain_line.h"
#include "automation_pan_line.h"
#include "automation_time_axis.h"
#include "redirect_automation_time_axis.h"
#include "gain_automation_time_axis.h"
#include "pan_automation_time_axis.h"
#include "redirect_automation_line.h"
#include "selection.h"
#include "point_selection.h"
#include "enums.h"
#include "utils.h"
#include "keyboard.h"
#include "rgb_macros.h"
#include "prompter.h"
#include "crossfade_view.h"
#include "gui_thread.h"

#include <ardour/audio_track.h>

#include "i18n.h"

using namespace ARDOUR;
using namespace SigC;
using namespace LADSPA;
using namespace Gtk;
using namespace Editing;

static const gchar * small_x_xpm[] = {
"11 11 2 1",
"     c None",
".    c #000000",
"           ",
"           ",
"  .     .  ",
"   .   .   ",
"    . .    ",
"     .     ",
"    . .    ",
"   .   .   ",
"  .     .  ",
"           ",
"           "};

AudioTimeAxisView::AudioTimeAxisView (PublicEditor& ed, Session& sess, Route& rt, Widget *canvas)
      : AxisView(sess),
        RouteUI(rt, sess, _("m"), _("s"), _("r")), // mute, solo, and record
        TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas),
        parent_canvas (canvas),
        button_table (3, 3),
        edit_group_button (_("g")), // group
        playlist_button (_("p")), 
        size_button (_("h")), // height
        automation_button (_("a")),
        visual_button (_("v")),
        redirect_window ("redirect window"),
        redirect_display (1)
{
      _has_state = true;
      subplugin_menu.set_name ("ArdourContextMenu");
      playlist_menu = 0;
      playlist_action_menu = 0;
      automation_action_menu = 0;
      gain_track = 0;
      pan_track = 0;
      view = 0;
      timestretch_rect = 0;
      waveform_item = 0;
      pan_automation_item = 0;
      gain_automation_item = 0;
      no_redraw = false;

      view = new StreamView (*this);

      add_gain_automation_child ();
      add_pan_automation_child ();

      ignore_toggle = false;

      rec_enable_button->set_active (false);
      mute_button->set_active (false);
      solo_button->set_active (false);
      
      rec_enable_button->set_name ("TrackRecordEnableButton");
      mute_button->set_name ("TrackMuteButton");
      solo_button->set_name ("SoloButton");
      edit_group_button.set_name ("TrackGroupButton");
      playlist_button.set_name ("TrackPlaylistButton");
      automation_button.set_name ("TrackAutomationButton");
      size_button.set_name ("TrackSizeButton");
      visual_button.set_name ("TrackVisualButton");
      hide_button.set_name ("TrackRemoveButton");

      hide_button.add (*(manage (new Pixmap (small_x_xpm))));
      
      _route.mute_changed.connect (slot (*this, &RouteUI::mute_changed));
      _route.solo_changed.connect (slot (*this, &RouteUI::solo_changed));
      _route.solo_safe_changed.connect (slot (*this, &RouteUI::solo_changed));

      _route.panner().Changed.connect (slot (*this, &AudioTimeAxisView::update_pans));

      solo_button->button_press_event.connect (slot (*this, &RouteUI::solo_press));
      solo_button->button_release_event.connect (slot (*this, &RouteUI::solo_release));
      mute_button->button_press_event.connect (slot (*this, &RouteUI::mute_press));
      mute_button->button_release_event.connect (slot (*this, &RouteUI::mute_release));
      rec_enable_button->button_press_event.connect (slot (*this, &RouteUI::rec_enable_press));
      edit_group_button.button_release_event.connect (slot (*this, &AudioTimeAxisView::edit_click));
      playlist_button.clicked.connect (slot (*this, &AudioTimeAxisView::playlist_click));
      automation_button.clicked.connect (slot (*this, &AudioTimeAxisView::automation_click));
      size_button.button_release_event.connect (slot (*this, &AudioTimeAxisView::size_click));
      visual_button.clicked.connect (slot (*this, &AudioTimeAxisView::visual_click));
      hide_button.clicked.connect (slot (*this, &AudioTimeAxisView::hide_click));

      name_entry.activate.connect (slot (*this, &AudioTimeAxisView::name_entry_activated));
      name_entry.focus_out_event.connect (slot (*this, &AudioTimeAxisView::name_entry_focus_out_handler));
      name_entry.button_press_event.connect (slot (*this, &AudioTimeAxisView::name_entry_button_press_handler));
      name_entry.button_release_event.connect (slot (*this, &AudioTimeAxisView::name_entry_button_release_handler));
      name_entry.key_release_event.connect (slot (*this, &AudioTimeAxisView::name_entry_key_release_handler));
      
      if (is_audio_track()) {
            controls_table.attach (*rec_enable_button, 6, 7, 0, 1, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
      }
      controls_table.attach (*mute_button, 7, 8, 0, 1, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
      controls_table.attach (*solo_button, 8, 9, 0, 1, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_FILL|GTK_EXPAND, 0, 0);

      controls_table.attach (edit_group_button, 7, 8, 1, 2, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);

      ARDOUR_UI::instance()->tooltips().set_tip(*rec_enable_button, _("Record"));
      ARDOUR_UI::instance()->tooltips().set_tip(*solo_button,_("Solo"));
      ARDOUR_UI::instance()->tooltips().set_tip(*mute_button,_("Mute"));
      ARDOUR_UI::instance()->tooltips().set_tip(edit_group_button,_("Edit Group"));
      ARDOUR_UI::instance()->tooltips().set_tip(size_button,_("Display Height"));
      ARDOUR_UI::instance()->tooltips().set_tip(playlist_button,_("Playlist"));
      ARDOUR_UI::instance()->tooltips().set_tip(automation_button, _("Automation"));
      ARDOUR_UI::instance()->tooltips().set_tip(visual_button, _("Visual options"));
      ARDOUR_UI::instance()->tooltips().set_tip(hide_button, _("Hide this track"));
      
      label_view ();

      controls_table.attach (hide_button, 0, 1, 1, 2, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
      controls_table.attach (visual_button, 1, 2, 1, 2, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
      controls_table.attach (size_button, 2, 3, 1, 2, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);
      controls_table.attach (automation_button, 3, 4, 1, 2, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);

      if (is_audio_track()) {
            controls_table.attach (playlist_button, 6, 7, 1, 2, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND);

      }

      /* remove focus from the buttons */
      
      automation_button.unset_flags (GTK_CAN_FOCUS);
      solo_button->unset_flags (GTK_CAN_FOCUS);
      mute_button->unset_flags (GTK_CAN_FOCUS);
      edit_group_button.unset_flags (GTK_CAN_FOCUS);
      size_button.unset_flags (GTK_CAN_FOCUS);
      playlist_button.unset_flags (GTK_CAN_FOCUS);
      hide_button.unset_flags (GTK_CAN_FOCUS);
      visual_button.unset_flags (GTK_CAN_FOCUS);

      /* map current state of the route */

      update_diskstream_display ();
      solo_changed(0);
      mute_changed(0);
      redirects_changed (0);
      reset_redirect_automation_curves ();
      edit_group_menu_radio_group = 0;
      y_position = -1;

      ensure_xml_node ();

      set_state (*xml_node);
      
      _route.mute_changed.connect (slot (*this, &RouteUI::mute_changed));
      _route.solo_changed.connect (slot (*this, &RouteUI::solo_changed));
      _route.redirects_changed.connect (slot (*this, &AudioTimeAxisView::redirects_changed));

      _route.name_changed.connect (slot (*this, &AudioTimeAxisView::route_name_changed));

      if (is_audio_track()) {

            /* track */

            audio_track()->FreezeChange.connect (slot (*this, &AudioTimeAxisView::map_frozen));

            audio_track()->diskstream_changed.connect (slot (*this, &AudioTimeAxisView::diskstream_changed));
            get_diskstream()->speed_changed.connect (slot (*this, &AudioTimeAxisView::speed_changed));

            controls_ebox.set_name ("AudioTrackControlsBaseUnselected");
            controls_base_selected_name = "AudioTrackControlsBaseSelected";
            controls_base_unselected_name = "AudioTrackControlsBaseUnselected";

            /* ask for notifications of any new RegionViews */

            view->AudioRegionViewAdded.connect (slot (*this, &AudioTimeAxisView::region_view_added));

            view->attach ();

            /* pick up the correct freeze state */

            map_frozen ();

      } else {

            /* bus */

            controls_ebox.set_name ("BusControlsBaseUnselected");
            controls_base_selected_name = "BusControlsBaseSelected";
            controls_base_unselected_name = "BusControlsBaseUnselected";
      }

      editor.ZoomChanged.connect (slot (*this, &AudioTimeAxisView::reset_samples_per_unit));
}

AudioTimeAxisView::~AudioTimeAxisView ()
{
      GoingAway (); /* EMIT_SIGNAL */

      if (playlist_menu) {
            delete playlist_menu;
            playlist_menu = 0;
      }
  
      if (playlist_action_menu) {
            delete playlist_action_menu;
            playlist_action_menu = 0;
      }

      vector_delete (&redirect_automation_curves);

      for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) {
            delete *i;
      }

      if (view) {
            delete view;
            view = 0;
      }
}

guint32
AudioTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
{
      ensure_xml_node ();
      xml_node->add_property ("shown_editor", "yes");
            
      return TimeAxisView::show_at (y, nth, parent);
}

void
AudioTimeAxisView::hide ()
{
      ensure_xml_node ();
      xml_node->add_property ("shown_editor", "no");

      TimeAxisView::hide ();
}

void
AudioTimeAxisView::set_playlist (AudioPlaylist *newplaylist)
{
      AudioPlaylist *pl;

      modified_connection.disconnect ();
      state_changed_connection.disconnect ();
      
      if ((pl = dynamic_cast<AudioPlaylist*> (playlist())) != 0) {
            state_changed_connection = pl->StateChanged.connect (slot (*this, &AudioTimeAxisView::playlist_state_changed));
            modified_connection = pl->Modified.connect (slot (*this, &AudioTimeAxisView::playlist_modified));
      }
}

void
AudioTimeAxisView::playlist_modified ()
{
}

gint
AudioTimeAxisView::edit_click (GdkEventButton *ev)
{
      if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
              _route.set_edit_group (0, this);
            return FALSE;
      } 

      using namespace Menu_Helpers;

      MenuList& items = edit_group_menu.items ();

      items.clear ();
      items.push_back (RadioMenuElem (edit_group_menu_radio_group, _("No group"), 
                           bind (slot (*this, &AudioTimeAxisView::set_edit_group_from_menu), (RouteGroup *) 0)));
      
      if (_route.edit_group() == 0) {
            static_cast<RadioMenuItem*>(items.back())->set_active ();
      }

      _session.foreach_edit_group (this, &AudioTimeAxisView::add_edit_group_menu_item);
      edit_group_menu.popup (ev->button, ev->time);

      return FALSE;
}

void
AudioTimeAxisView::add_edit_group_menu_item (RouteGroup *eg)
{
      using namespace Menu_Helpers;

      MenuList &items = edit_group_menu.items();
      items.push_back (RadioMenuElem (edit_group_menu_radio_group,
                              eg->name(), bind (slot (*this, &AudioTimeAxisView::set_edit_group_from_menu), eg)));
      if (_route.edit_group() == eg) {
            static_cast<RadioMenuItem*>(items.back())->set_active ();
      }
}

void
AudioTimeAxisView::set_edit_group_from_menu (RouteGroup *eg)

{
      _route.set_edit_group (eg, this);
}

void
AudioTimeAxisView::playlist_state_changed (Change ignored)
{
      // ENSURE_GUI_THREAD (bind (slot (*this, &AudioTimeAxisView::playlist_state_changed), ignored));
      // why are we here ?
}

void
AudioTimeAxisView::playlist_changed ()

{
      label_view ();

      if (is_audio_track()) {
            set_playlist (get_diskstream()->playlist());
      }
}

void
AudioTimeAxisView::label_view ()
{
      string x = _route.name();

      if (x != name_entry.get_text()) {
            name_entry.set_text (x);
      }

      ARDOUR_UI::instance()->tooltips().set_tip (name_entry, x);
}

void
AudioTimeAxisView::route_name_changed (void *src)
{
      editor.route_name_changed (this);
      label_view ();
}

void
AudioTimeAxisView::take_name_changed (void *src)

{
      if (src != this) {
            label_view ();
      }
}

void
AudioTimeAxisView::playlist_click ()
{
      // always build a new action menu
      
      if (playlist_action_menu == 0) {
            playlist_action_menu = new Menu;
            playlist_action_menu->set_name ("ArdourContextMenu");
      }
      
      build_playlist_menu(playlist_action_menu);

      playlist_action_menu->popup (1, 0);
}

void
AudioTimeAxisView::automation_click ()
{
      if (automation_action_menu == 0) {
            /* this seems odd, but the automation action
               menu is built as part of the display menu.
            */
            build_display_menu ();
      }
      automation_action_menu->popup (1, 0);
}

void
AudioTimeAxisView::show_timestretch (jack_nframes_t start, jack_nframes_t end)
{
      double x1;
      double x2;
      double y2;
      
      TimeAxisView::show_timestretch (start, end);

      hide_timestretch ();

#if 0 
      if (ts.empty()) {
            return;
      }


      /* check that the time selection was made in our route, or our edit group.
         remember that edit_group() == 0 implies the route is *not* in a edit group.
      */

      if (!(ts.track == this || (ts.group != 0 && ts.group == _route.edit_group()))) {
            /* this doesn't apply to us */
            return;
      }

      /* ignore it if our edit group is not active */
      
      if ((ts.track != this) && _route.edit_group() && !_route.edit_group()->is_active()) {
            return;
      }
#endif

      if (timestretch_rect == 0) {
            timestretch_rect = gtk_canvas_item_new (GTK_CANVAS_GROUP(canvas_display),
                                          gtk_canvas_simplerect_get_type(),
                                          "x1", 0.0,
                                          "y1", 0.0,
                                          "x2", 0.0,
                                          "y2", 0.0,
                                          "fill_color_rgba", color_map[cTimeStretchFill],
                                          "outline_color_rgba" , color_map[cTimeStretchOutline],
                                          NULL);
      }

      gtk_canvas_item_show (timestretch_rect);
      gtk_canvas_item_raise_to_top (timestretch_rect);
      
      x1 = start / editor.get_current_zoom();
      x2 = (end - 1) / editor.get_current_zoom();
      y2 = height - 2;
      
      gtk_object_set (GTK_OBJECT(timestretch_rect), 
                  "x1", x1,
                  "y1", 1.0,
                  "x2", x2,
                  "y2", y2,
                  NULL);
}

void
AudioTimeAxisView::hide_timestretch ()
{
      TimeAxisView::hide_timestretch ();

      if (timestretch_rect) {
            gtk_canvas_item_hide (timestretch_rect);
      }
}

void
AudioTimeAxisView::show_selection (TimeSelection& ts)
{

#if 0
      /* ignore it if our edit group is not active or if the selection was started
         in some other track or edit group (remember that edit_group() == 0 means
         that the track is not in an edit group).
      */

      if (((ts.track != this && !is_child (ts.track)) && _route.edit_group() && !_route.edit_group()->is_active()) ||
          (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route.edit_group())))) {
            hide_selection ();
            return;
      }
#endif

      TimeAxisView::show_selection (ts);
}

void
AudioTimeAxisView::set_state (const XMLNode& node)
{
      const XMLProperty *prop;
      
      TimeAxisView::set_state (node);
      
      if ((prop = node.property ("shown_editor")) != 0) {
            if (prop->value() == "no") {
                  _marked_for_display = false;
            } else {
                  _marked_for_display = true;
            }
      } else {
            _marked_for_display = true;
      }
      
      XMLNodeList nlist = node.children();
      XMLNodeConstIterator niter;
      XMLNode *child_node;
      
      
      show_gain_automation = false;
      show_pan_automation  = false;
      
      for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
            child_node = *niter;

            if (child_node->name() == "gain") {
                  XMLProperty *prop=child_node->property ("shown");
                  
                  if (prop != 0) {
                        if (prop->value() == "yes") {
                              show_gain_automation = true;
                        }
                  }
                  continue;
            }
            
            if (child_node->name() == "pan") {
                  XMLProperty *prop=child_node->property ("shown");
                  
                  if (prop != 0) {
                        if (prop->value() == "yes") {
                              show_pan_automation = true;
                        }                 
                  }
                  continue;
            }
      }
}

void
AudioTimeAxisView::set_height (TrackHeight h)
{
      bool height_changed = (h != (TrackHeight)height);

      TimeAxisView::set_height (h);

      ensure_xml_node ();

      view->set_height ((double) height);

      switch (height) {
      case Largest:
            xml_node->add_property ("track_height", "largest");
            controls_table.show_all ();
            name_label.hide ();
            break;
      case Large:
            xml_node->add_property ("track_height", "large");
            controls_table.show_all ();
            name_label.hide ();
            break;
      case Larger:
            xml_node->add_property ("track_height", "larger");
            controls_table.show_all ();
            name_label.hide ();
            break;
      case Normal:
            xml_node->add_property ("track_height", "normal");
            controls_table.show_all ();
            name_label.hide ();
            break;
      case Smaller:
            xml_node->add_property ("track_height", "smaller");
            controls_table.show_all ();
            name_label.hide ();
            edit_group_button.hide ();
            hide_button.hide ();
            visual_button.hide ();
            size_button.hide ();
            automation_button.hide ();
            playlist_button.hide ();
            break;
      case Small:
            xml_node->add_property ("track_height", "small");
            controls_table.hide_all ();
            controls_table.show ();
            name_label.set_text (_route.name());
            name_label.show ();
            name_hbox.show ();
            break;
      }

      if (height_changed) {
            /* only emit the signal if the height really changed */
             _route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
      }
}

void
AudioTimeAxisView::select_track_color ()
{
      if (RouteUI::choose_color ()) {

            if (view) {
                  view->apply_color (_color, StreamView::RegionColor);
            }
      }
}

void
AudioTimeAxisView::reset_redirect_automation_curves ()
{
      for (vector<RedirectAutomationLine*>::iterator i = redirect_automation_curves.begin(); i != redirect_automation_curves.end(); ++i) {
            (*i)->reset();
      }
}

void
AudioTimeAxisView::reset_samples_per_unit ()
{
      set_samples_per_unit (editor.get_current_zoom());
}

void
AudioTimeAxisView::set_samples_per_unit (double spu)
{
      double speed = 1.0;

      if (get_diskstream() != 0) {
            speed = get_diskstream()->speed();
      }
      
      if (view) {
            view->set_samples_per_unit (spu * speed);
      }

      TimeAxisView::set_samples_per_unit (spu * speed);
}

void
AudioTimeAxisView::build_display_menu ()
{
      using namespace Menu_Helpers;

      /* get the size menu ready */

      build_size_menu ();

      /* prepare it */

      TimeAxisView::build_display_menu ();

      /* now fill it with our stuff */

      MenuList& items = display_menu->items();
      display_menu->set_name ("ArdourContextMenu");
      
      items.push_back (MenuElem (_("Height"), *size_menu));
      items.push_back (MenuElem (_("Color"), slot (*this, &AudioTimeAxisView::select_track_color)));


      items.push_back (SeparatorElem());
      items.push_back (MenuElem (_("Hide all crossfades"), slot (*this, &AudioTimeAxisView::hide_all_xfades)));
      items.push_back (MenuElem (_("Show all crossfades"), slot (*this, &AudioTimeAxisView::show_all_xfades)));
      items.push_back (SeparatorElem());


      automation_action_menu = manage (new Menu);
      MenuList& automation_items = automation_action_menu->items();
      automation_action_menu->set_name ("ArdourContextMenu");
      
      automation_items.push_back (MenuElem (_("show all automation"),
                                    slot (*this, &AudioTimeAxisView::show_all_automation)));

      automation_items.push_back (MenuElem (_("show existing automation"),
                                    slot (*this, &AudioTimeAxisView::show_existing_automation)));

      automation_items.push_back (MenuElem (_("hide all automation"),
                                    slot (*this, &AudioTimeAxisView::hide_all_automation)));

      automation_items.push_back (SeparatorElem());

      automation_items.push_back (CheckMenuElem (_("gain"), 
                                       slot (*this, &AudioTimeAxisView::toggle_gain_track)));
      gain_automation_item = static_cast<CheckMenuItem*> (automation_items.back());
      gain_automation_item->set_active(show_gain_automation);

      automation_items.push_back (CheckMenuElem (_("pan"),
                                       slot (*this, &AudioTimeAxisView::toggle_pan_track)));
      pan_automation_item = static_cast<CheckMenuItem*> (automation_items.back());
      pan_automation_item->set_active(show_pan_automation);

      automation_items.push_back (MenuElem (_("Plugins"), subplugin_menu));

      items.push_back (MenuElem (_("Automation"), *automation_action_menu));

      Menu *waveform_menu = manage(new Menu);
      MenuList& waveform_items = waveform_menu->items();
      waveform_menu->set_name ("ArdourContextMenu");
      
      waveform_items.push_back (CheckMenuElem (_("Show waveforms"), slot (*this, &AudioTimeAxisView::toggle_waveforms)));
      waveform_item = static_cast<CheckMenuItem *> (waveform_items.back());
      ignore_toggle = true;
      waveform_item->set_active (editor.show_waveforms());
      ignore_toggle = false;

      RadioMenuItem::Group group;

      waveform_items.push_back (RadioMenuElem (group, _("Traditional"), bind (slot (*this, &AudioTimeAxisView::set_waveform_shape), Traditional)));
      traditional_item = static_cast<RadioMenuItem *> (waveform_items.back());

      waveform_items.push_back (RadioMenuElem (group, _("Rectified"), bind (slot (*this, &AudioTimeAxisView::set_waveform_shape), Rectified)));
      rectified_item = static_cast<RadioMenuItem *> (waveform_items.back());

      items.push_back (MenuElem (_("Waveform"), *waveform_menu));

      if (is_audio_track()) {

            Menu* alignment_menu = manage (new Menu);
            MenuList& alignment_items = alignment_menu->items();
            alignment_menu->set_name ("ArdourContextMenu");

            RadioMenuItem::Group align_group;
            
            alignment_items.push_back (RadioMenuElem (align_group, _("align with existing material"), bind (slot (*this, &AudioTimeAxisView::set_align_style), ExistingMaterial)));
            align_existing_item = dynamic_cast<RadioMenuItem*>(alignment_items.back());
            if (get_diskstream()->alignment_style() == ExistingMaterial) {
                  align_existing_item->set_active();
            }
            alignment_items.push_back (RadioMenuElem (align_group, _("align with capture time"), bind (slot (*this, &AudioTimeAxisView::set_align_style), CaptureTime)));
            align_capture_item = dynamic_cast<RadioMenuItem*>(alignment_items.back());
            if (get_diskstream()->alignment_style() == CaptureTime) {
                  align_capture_item->set_active();
            }
            
            items.push_back (MenuElem (_("Alignment"), *alignment_menu));

            get_diskstream()->AlignmentStyleChanged.connect (slot (*this, &AudioTimeAxisView::align_style_changed));
      }

      items.push_back (SeparatorElem());
      items.push_back (CheckMenuElem (_("Active"), slot (*this, &RouteUI::toggle_route_active)));
      route_active_menu_item = dynamic_cast<CheckMenuItem *> (items.back());
      route_active_menu_item->set_active (_route.active());

      items.push_back (SeparatorElem());
      items.push_back (MenuElem (_("Remove"), slot (*this, &RouteUI::remove_this_route)));

}

void
AudioTimeAxisView::align_style_changed ()
{
      switch (get_diskstream()->alignment_style()) {
      case ExistingMaterial:
            if (!align_existing_item->get_active()) {
                  align_existing_item->set_active();
            }
            break;
      case CaptureTime:
            if (!align_capture_item->get_active()) {
                  align_capture_item->set_active();
            }
            break;
      }
}

void
AudioTimeAxisView::set_align_style (AlignStyle style)
{
      get_diskstream()->set_align_style (style);
}

void
AudioTimeAxisView::rename_current_playlist ()
{
      ArdourPrompter prompter (true);

      AudioPlaylist *pl;
      DiskStream *ds;

      if (((ds = get_diskstream()) == 0) ||((pl = ds->playlist()) == 0)) {
            return;
      }

      prompter.set_prompt (_("Name for playlist"));
      prompter.set_initial_text (pl->name());
      prompter.done.connect (Main::quit.slot());
      prompter.show_all ();

      Main::run ();

      if (prompter.status == Gtkmmext::Prompter::entered) {
            string name;
            prompter.get_result (name);
            pl->set_name (name);
      }
}

void
AudioTimeAxisView::playlist_selected (AudioPlaylist *pl)
{
      DiskStream *ds;

      if ((ds = get_diskstream()) != 0) {
            ds->use_playlist (pl);
      }
}

void
AudioTimeAxisView::use_copy_playlist ()
{
      AudioPlaylist *pl;
      DiskStream *ds;

      if (((ds = get_diskstream()) == 0) || ((pl = ds->playlist()) == 0)) {
            return;
      }

      ArdourPrompter prompter (true);
      string new_name = Playlist::bump_name (pl->name(), _session);

      prompter.set_prompt (_("Name for playlist"));
      prompter.set_initial_text (new_name);
      prompter.done.connect (Main::quit.slot());
      prompter.show_all ();

      Main::run ();

      if (prompter.status == Gtkmmext::Prompter::entered) {
            string name;
            prompter.get_result (name);

            ds->use_copy_playlist ();

            pl = ds->playlist();
            pl->set_name (name);
      }
}

void
AudioTimeAxisView::use_new_playlist ()
{
      AudioPlaylist *pl;
      DiskStream *ds;

      if (((ds = get_diskstream()) == 0) || ((pl = ds->playlist()) == 0)) {
            return;
      }

      ArdourPrompter prompter (true);
      string new_name = Playlist::bump_name (pl->name(), _session);

      prompter.set_prompt (_("Name for playlist"));
      prompter.set_initial_text (new_name);
      prompter.done.connect (Main::quit.slot());
      prompter.show_all ();

      Main::run ();

      if (prompter.status == Gtkmmext::Prompter::entered) {
            string name;
            prompter.get_result (name);

            ds->use_new_playlist ();

            pl = ds->playlist();
            pl->set_name (name);
      }
}     

void
AudioTimeAxisView::clear_playlist ()
{
      AudioPlaylist *pl;
      DiskStream *ds;
      
      if ((ds = get_diskstream()) != 0) {
            if ((pl = ds->playlist()) != 0) {
                  editor.clear_playlist (*pl);
            }
      }
}

void
AudioTimeAxisView::toggle_waveforms ()
{
      if (view && waveform_item && !ignore_toggle) {
            view->set_show_waveforms (waveform_item->is_active());
      }
}

void
AudioTimeAxisView::set_show_waveforms (bool yn)
{
      if (waveform_item) {
            waveform_item->set_active (yn);
      } else {
            view->set_show_waveforms (yn);
      }
}

void
AudioTimeAxisView::set_show_waveforms_recording (bool yn)
{
      if (view) {
            view->set_show_waveforms_recording (yn);
      }
}

void
AudioTimeAxisView::set_waveform_shape (WaveformShape shape)
{
      if (view) {
            view->set_waveform_shape (shape);
      }
}

void
AudioTimeAxisView::speed_changed ()
{
      Gtkmmext::UI::instance()->call_slot (slot (*this, &AudioTimeAxisView::reset_samples_per_unit));
}

void
AudioTimeAxisView::diskstream_changed (void *src)
{
      Gtkmmext::UI::instance()->call_slot (slot (*this, &AudioTimeAxisView::update_diskstream_display));
}     

void
AudioTimeAxisView::update_diskstream_display ()
{
      DiskStream *ds;

      if ((ds = get_diskstream()) != 0) {
            set_playlist (ds->playlist ());
      }

      map_frozen ();
}     

void
AudioTimeAxisView::selection_click (GdkEventButton* ev)
{
      PublicEditor::TrackViewList* tracks = editor.get_valid_views (this, _route.edit_group());

      if (Keyboard::modifier_state_contains (ev->state, Keyboard::Shift)) {
            if (editor.get_selection().selected (this)) {
                  editor.get_selection().remove (*tracks);
            } else {
                  editor.get_selection().add (*tracks);
            }
      } else {
            editor.get_selection().set (*tracks);
      }

      delete tracks;
}

void
AudioTimeAxisView::set_selected_regionviews (AudioRegionSelection& regions)
{
      if (view) {
            view->set_selected_regionviews (regions);
      }
}

void
AudioTimeAxisView::set_selected_points (PointSelection& points)
{
      for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
            (*i)->set_selected_points (points);
      }
}

void
AudioTimeAxisView::get_selectables (jack_nframes_t start, jack_nframes_t end, double top, double bot, list<Selectable*>& results)
{
      double speed = 1.0;
      
      if (get_diskstream() != 0) {
            speed = get_diskstream()->speed();
      }
      
      jack_nframes_t start_adjusted = session_frame_to_track_frame(start, speed);
      jack_nframes_t end_adjusted = session_frame_to_track_frame(end, speed);

      if (view && touched (top, bot)) {
            view->get_selectables (start_adjusted, end_adjusted, results);
      }

      /* pick up visible automation tracks */
      
      for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
            if (!(*i)->hidden()) {
                  (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
            }
      }
}

void
AudioTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
{
      if (view) {
            view->get_inverted_selectables (sel, results);
      }

      for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
            if (!(*i)->hidden()) {
                  (*i)->get_inverted_selectables (sel, results);
            }
      }

      return;
}

RouteGroup*
AudioTimeAxisView::edit_group() const
{
      return _route.edit_group();
}

string
AudioTimeAxisView::name() const
{
      return _route.name();
}

Playlist *
AudioTimeAxisView::playlist () const 
{
      DiskStream *ds;

      if ((ds = get_diskstream()) != 0) {
            return ds->playlist(); 
      } else {
            return 0; 
      }
}

gint 
AudioTimeAxisView::name_entry_button_press_handler (GdkEventButton *ev)
{
      if (ev->button == 3) {
            return stop_signal (name_entry, "button_press_event");
      }
      return FALSE;
}

gint 
AudioTimeAxisView::name_entry_button_release_handler (GdkEventButton *ev)
{
      return FALSE;
}

gint
AudioTimeAxisView::name_entry_focus_out_handler (GdkEventFocus* ev)
{
      name_entry_changed ();
      return TRUE;
}

gint
AudioTimeAxisView::name_entry_key_release_handler (GdkEventKey* ev)
{
      switch (ev->keyval) {
      case GDK_Tab:
      case GDK_Up:
      case GDK_Down:
            name_entry_changed ();
            return TRUE;

      default:
            return FALSE;
      }
}

void
AudioTimeAxisView::name_entry_activated ()
{
      /* this should drop focus from the entry,
         and cause a call to name_entry_changed()
      */
      controls_ebox.grab_focus();
}

void
AudioTimeAxisView::name_entry_changed ()
{
      string x;

      ARDOUR_UI::generic_focus_out_event (0);

      x = name_entry.get_text ();
      
      if (x == _route.name()) {
            return;
      }

      if (x.length() == 0) {
            name_entry.set_text (_route.name());
            return;
      }

      strip_whitespace_edges(x);

      if (_session.route_name_unique (x)) {
            _route.set_name (x, this);
      } else {
            ARDOUR_UI::instance()->popup_error (_("a track already exists with that name"));
            name_entry.set_text (_route.name());
      }
}

void
AudioTimeAxisView::visual_click ()
{
      popup_display_menu (0);
}

void
AudioTimeAxisView::hide_click ()
{
      editor.unselect_strip_in_display (*this);
}

Region*
AudioTimeAxisView::find_next_region (jack_nframes_t pos, RegionPoint point, int32_t dir)
{
      DiskStream *stream;
      AudioPlaylist *playlist;

      if ((stream = get_diskstream()) != 0 && (playlist = stream->playlist()) != 0) {
            return playlist->find_next_region (pos, point, dir);
      }

      return 0;
}

void
AudioTimeAxisView::add_gain_automation_child ()
{
      XMLProperty* prop;
      AutomationLine* line;

      gain_track = new GainAutomationTimeAxisView (_session, _route, editor, *this, parent_canvas, _("gain"),
                                         _route.gain_automation_curve());
      
      
      line = new AutomationGainLine ("automation gain", _session, *gain_track,
                                       gain_track->canvas_display,
                                       _route.gain_automation_curve(),
                                       PublicEditor::canvas_control_point_event,
                                       PublicEditor::canvas_line_event);
      line->set_line_color (color_map[cAutomationLine]);
      

      gain_track->add_line (*line);

      add_child (gain_track);

      gain_track->Hiding.connect (slot (*this, &AudioTimeAxisView::gain_hidden));

      bool hideit = true;
      
      XMLNode* node;

      if ((node = gain_track->get_state_node()) != 0) {
            if  ((prop = node->property ("shown")) != 0) {
                  if (prop->value() == "yes") {
                        hideit = false;
                  }
            } 
      }

      if (hideit) {
            gain_track->hide ();
      }
}

void
AudioTimeAxisView::add_pan_automation_child ()
{
      XMLProperty* prop;

      pan_track = new PanAutomationTimeAxisView (_session, _route, editor, *this, parent_canvas, _("pan"));

      update_pans ();
      
      add_child (pan_track);

      pan_track->Hiding.connect (slot (*this, &AudioTimeAxisView::pan_hidden));

      ensure_xml_node ();
      bool hideit = true;
      
      XMLNode* node;

      if ((node = pan_track->get_state_node()) != 0) {
            if ((prop = node->property ("shown")) != 0) {
                  if (prop->value() == "yes") {
                        hideit = false;
                  }
            } 
      }

      if (hideit) {
            pan_track->hide ();
      }
}

void
AudioTimeAxisView::update_pans ()
{
      Panner::iterator p;
      
      pan_track->clear_lines ();
      
      /* we don't draw lines for "greater than stereo" panning.
       */

      if (_route.n_outputs() > 2) {
            return;
      }

      for (p = _route.panner().begin(); p != _route.panner().end(); ++p) {

            AutomationLine* line;

            line = new AutomationPanLine ("automation pan", _session, *pan_track,
                                    pan_track->canvas_display, 
                                    (*p)->automation(),
                                    PublicEditor::canvas_control_point_event,
                                    PublicEditor::canvas_line_event);

            if (p == _route.panner().begin()) {
                  /* first line is a nice orange */
                  line->set_line_color (color_map[cLeftPanAutomationLine]);
            } else {
                  /* second line is a nice blue */
                  line->set_line_color (color_map[cRightPanAutomationLine]);
            }

            pan_track->add_line (*line);
      }
}
            
void
AudioTimeAxisView::toggle_gain_track ()
{

      bool showit = gain_automation_item->get_active();

      if (showit != gain_track->marked_for_display()) {
            if (showit) {
                  gain_track->set_marked_for_display (true);
                  gtk_canvas_item_show (gain_track->canvas_display);
                  gain_track->get_state_node()->add_property ("shown", X_("yes"));
            } else {
                  gain_track->set_marked_for_display (false);
                  gain_track->hide ();
                  gain_track->get_state_node()->add_property ("shown", X_("no"));
            }

            /* now trigger a redisplay */
            
            if (!no_redraw) {
                   _route.gui_changed (X_("track_height"), (void *) 0); /* EMIT_SIGNAL */
            }
      }
}

void
AudioTimeAxisView::gain_hidden ()
{
      gain_track->get_state_node()->add_property (X_("shown"), X_("no"));

      if (gain_automation_item && !_hidden) {
            gain_automation_item->set_active (false);
      }

       _route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
}

void
AudioTimeAxisView::toggle_pan_track ()
{
      bool showit = pan_automation_item->get_active();

      if (showit != pan_track->marked_for_display()) {
            if (showit) {
                  pan_track->set_marked_for_display (true);
                  gtk_canvas_item_show (pan_track->canvas_display);
                  pan_track->get_state_node()->add_property ("shown", X_("yes"));
            } else {
                  pan_track->set_marked_for_display (false);
                  pan_track->hide ();
                  pan_track->get_state_node()->add_property ("shown", X_("no"));
            }

            /* now trigger a redisplay */
            
            if (!no_redraw) {
                   _route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
            }
      }
}

void
AudioTimeAxisView::pan_hidden ()
{
      pan_track->get_state_node()->add_property ("shown", "no");

      if (pan_automation_item && !_hidden) {
            pan_automation_item->set_active (false);
      }

       _route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
}

AudioTimeAxisView::RedirectAutomationInfo::~RedirectAutomationInfo ()
{
      for (vector<RedirectAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
            delete *i;
      }
}


AudioTimeAxisView::RedirectAutomationNode::~RedirectAutomationNode ()
{
      parent.remove_ran (this);

      if (view) {
            delete view;
      }
}

void
AudioTimeAxisView::remove_ran (RedirectAutomationNode* ran)
{
      if (ran->view) {
            remove_child (ran->view);
      }
}

AudioTimeAxisView::RedirectAutomationNode*
AudioTimeAxisView::find_redirect_automation_node (Redirect *redirect, uint32_t what)
{
      for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) {

            if ((*i)->redirect == redirect) {

                  for (vector<RedirectAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
                        if ((*ii)->what == what) {
                              return *ii;
                        }
                  }
            }
      }

      return 0;
}

static string 
legalize_for_xml_node (string str)
{
      string::size_type pos;
      string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+=:";
      string legal;

      legal = str;
      pos = 0;

      while ((pos = legal.find_first_not_of (legal_chars, pos)) != string::npos) {
            legal.replace (pos, 1, "_");
            pos += 1;
      }

      return legal;
}


void
AudioTimeAxisView::add_redirect_automation_curve (Redirect *redirect, uint32_t what)
{
      RedirectAutomationLine* ral;
      string name;
      RedirectAutomationNode* ran;

      if ((ran = find_redirect_automation_node (redirect, what)) == 0) {
            fatal << _("programming error: ")
                  << compose (X_("redirect automation curve for %1:%2 not registered with audio track!"),
                          redirect->name(), what)
                  << endmsg;
            /*NOTREACHED*/
            return;
      }

      if (ran->view) {
            return;
      }

      name = redirect->describe_parameter (what);

      /* create a string that is a legal XML node name that can be used to refer to this redirect+port combination */

      char state_name[256];
      snprintf (state_name, sizeof (state_name), "Redirect-%s-%" PRIu32, legalize_for_xml_node (redirect->name()).c_str(), what);

      ran->view = new RedirectAutomationTimeAxisView (_session, _route, editor, *this, parent_canvas, name, what, *redirect, state_name);

      ral = new RedirectAutomationLine (name, 
                                *redirect, what, _session, *ran->view,
                                ran->view->canvas_display, redirect->automation_list (what), 
                                PublicEditor::canvas_control_point_event,
                                PublicEditor::canvas_line_event);
      
      ral->set_line_color (color_map[cRedirectAutomationLine]);
      ral->queue_reset ();

      ran->view->add_line (*ral);

      ran->view->Hiding.connect (bind (slot (*this, &AudioTimeAxisView::redirect_automation_track_hidden), ran, redirect));

      if (!ran->view->marked_for_display()) {
            ran->view->hide ();
      } else {
            ran->menu_item->set_active (true);
      }

      add_child (ran->view);

      view->foreach_regionview (bind (slot (*this, &AudioTimeAxisView::add_ghost_to_redirect), ran->view));

      redirect->mark_automation_visible (what, true);
}

void
AudioTimeAxisView::redirect_automation_track_hidden (AudioTimeAxisView::RedirectAutomationNode* ran, Redirect* r)
{
      if (!_hidden) {
            ran->menu_item->set_active (false);
      }

      r->mark_automation_visible (ran->what, false);

       _route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
}

void
AudioTimeAxisView::add_existing_redirect_automation_curves (Redirect *redirect)
{
      set<uint32_t> s;
      RedirectAutomationLine *ral;

      redirect->what_has_visible_automation (s);

      for (set<uint32_t>::iterator i = s.begin(); i != s.end(); ++i) {
            
            if ((ral = find_redirect_automation_curve (redirect, *i)) != 0) {
                  ral->queue_reset ();
            } else {
                  add_redirect_automation_curve (redirect, (*i));
            }
      }
}

void
AudioTimeAxisView::add_redirect_to_subplugin_menu (Redirect* r)
{
      using namespace Menu_Helpers;
      RedirectAutomationInfo *rai;
      list<RedirectAutomationInfo*>::iterator x;
      
      const std::set<uint32_t>& automatable = r->what_can_be_automated ();
      std::set<uint32_t> has_visible_automation;

      r->what_has_visible_automation(has_visible_automation);

      if (automatable.empty()) {
            return;
      }

      for (x = redirect_automation.begin(); x != redirect_automation.end(); ++x) {
            if ((*x)->redirect == r) {
                  break;
            }
      }

      if (x == redirect_automation.end()) {

            rai = new RedirectAutomationInfo (r);
            redirect_automation.push_back (rai);

      } else {

            rai = *x;

      }

      /* any older menu was deleted at the top of redirects_changed()
         when we cleared the subplugin menu.
      */

      rai->menu = manage (new Menu);
      MenuList& items = rai->menu->items();
      rai->menu->set_name ("ArdourContextMenu");

      items.clear ();

      for (std::set<uint32_t>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {

            RedirectAutomationNode* ran;
            CheckMenuItem* mitem;
            
            string name = r->describe_parameter (*i);
            
            items.push_back (CheckMenuElem (name));
            mitem = dynamic_cast<CheckMenuItem*> (items.back());

            if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
                  mitem->set_active(true);
            }

            if ((ran = find_redirect_automation_node (r, *i)) == 0) {

                  /* new item */
                  
                  ran = new RedirectAutomationNode (*i, mitem, *this);
                  
                  rai->lines.push_back (ran);

            } else {

                  ran->menu_item = mitem;

            }

            mitem->toggled.connect (bind (slot (*this, &AudioTimeAxisView::redirect_menu_item_toggled), rai, ran));
      }

      /* add the menu for this redirect, because the subplugin
         menu is always cleared at the top of redirects_changed().
         this is the result of some poor design in gtkmm and/or
         GTK+.
      */

      subplugin_menu.items().push_back (MenuElem (r->name(), *rai->menu));
      rai->valid = true;
}

void
AudioTimeAxisView::redirect_menu_item_toggled (AudioTimeAxisView::RedirectAutomationInfo* rai,
                                     AudioTimeAxisView::RedirectAutomationNode* ran)
{
      bool showit = ran->menu_item->get_active();
      bool redraw = false;

      if (ran->view == 0 && showit) {
            add_redirect_automation_curve (rai->redirect, ran->what);
            redraw = true;
      }

      if (showit != ran->view->marked_for_display()) {

            if (showit) {
                  ran->view->set_marked_for_display (true);
                  gtk_canvas_item_show (ran->view->canvas_display);
            } else {
                  rai->redirect->mark_automation_visible (ran->what, true);
                  ran->view->set_marked_for_display (false);
                  ran->view->hide ();
            }

            redraw = true;

      }

      if (redraw && !no_redraw) {

            /* now trigger a redisplay */
            
             _route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */

      }
}

void
AudioTimeAxisView::redirects_changed (void *src)
{
      using namespace Menu_Helpers;

      for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) {
            (*i)->valid = false;
      }

      subplugin_menu.items().clear ();

      _route.foreach_redirect (this, &AudioTimeAxisView::add_redirect_to_subplugin_menu);
      _route.foreach_redirect (this, &AudioTimeAxisView::add_existing_redirect_automation_curves);

      for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ) {

            list<RedirectAutomationInfo*>::iterator tmp;

            tmp = i;
            ++tmp;

            if (!(*i)->valid) {

                  delete *i;
                  redirect_automation.erase (i);

            } 

            i = tmp;
      }

      /* change in visibility was possible */

      _route.gui_changed ("track_height", this);
}

RedirectAutomationLine *
AudioTimeAxisView::find_redirect_automation_curve (Redirect *redirect, uint32_t what)
{
      RedirectAutomationNode* ran;

      if ((ran = find_redirect_automation_node (redirect, what)) != 0) {
            if (ran->view) {
                  return dynamic_cast<RedirectAutomationLine*> (ran->view->lines.front());
            } 
      }

      return 0;
}

void
AudioTimeAxisView::show_all_automation ()
{
      no_redraw = true;

      pan_automation_item->set_active (true);
      gain_automation_item->set_active (true);
      
      for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) {
            for (vector<RedirectAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
                  if ((*ii)->view == 0) {
                        add_redirect_automation_curve ((*i)->redirect, (*ii)->what);
                  } 

                  (*ii)->menu_item->set_active (true);
            }
      }

      no_redraw = false;

       _route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
}

void
AudioTimeAxisView::show_existing_automation ()
{
      no_redraw = true;

      pan_automation_item->set_active (true);
      gain_automation_item->set_active (true);

      for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) {
            for (vector<RedirectAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
                  if ((*ii)->view != 0) {
                        (*ii)->menu_item->set_active (true);
                  }
            }
      }

      no_redraw = false;

       _route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
}

void
AudioTimeAxisView::hide_all_automation ()
{
      no_redraw = true;

      pan_automation_item->set_active (false);
      gain_automation_item->set_active (false);

      for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) {
            for (vector<RedirectAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
                  (*ii)->menu_item->set_active (false);
            }
      }

      no_redraw = false;
       _route.gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
}

bool
AudioTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
{
      Playlist* what_we_got;
      DiskStream* ds = get_diskstream();
      Playlist* playlist;
      bool ret = false;

      if (ds == 0) {
            /* route is a bus, not a track */
            return false;
      }

      playlist = ds->playlist();


      TimeSelection time (selection.time);
      float speed = ds->speed();
      if (speed != 1.0f) {
            for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
                  (*i).start = session_frame_to_track_frame((*i).start, speed);
                  (*i).end   = session_frame_to_track_frame((*i).end,   speed);
            }
      }
      
      switch (op) {
      case Cut:
            _session.add_undo (playlist->get_memento());
            if ((what_we_got = playlist->cut (time)) != 0) {
                  editor.get_cut_buffer().add (what_we_got);
                  _session.add_redo_no_execute (playlist->get_memento());
                  ret = true;
            }
            break;
      case Copy:
            if ((what_we_got = playlist->copy (time)) != 0) {
                  editor.get_cut_buffer().add (what_we_got);
            }
            break;

      case Clear:
            _session.add_undo (playlist->get_memento());
            if ((what_we_got = playlist->cut (time)) != 0) {
                  _session.add_redo_no_execute (playlist->get_memento());
                  what_we_got->unref ();
                  ret = true;
            }
            break;
      }

      return ret;
}

bool
AudioTimeAxisView::paste (jack_nframes_t pos, float times, Selection& selection, size_t nth)
{
      if (!is_audio_track()) {
            return false;
      }

      Playlist* playlist = get_diskstream()->playlist();
      PlaylistSelection::iterator p;
      
      for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth);

      if (p == selection.playlists.end()) {
            return false;
      }

      if (get_diskstream()->speed() != 1.0f)
            pos = session_frame_to_track_frame(pos, get_diskstream()->speed() );
      
      _session.add_undo (playlist->get_memento());
      playlist->paste (**p, pos, times);
      _session.add_redo_no_execute (playlist->get_memento());

      return true;
}

void
AudioTimeAxisView::region_view_added (AudioRegionView* arv)
{
      for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
            AutomationTimeAxisView* atv;

            if ((atv = dynamic_cast<AutomationTimeAxisView*> (*i)) != 0) {
                  arv->add_ghost (*atv);
            }
      }
}

void
AudioTimeAxisView::add_ghost_to_redirect (AudioRegionView* arv, AutomationTimeAxisView* atv)
{
      arv->add_ghost (*atv);
}

list<TimeAxisView*>
AudioTimeAxisView::get_child_list()
{
  
      list<TimeAxisView*>redirect_children;
      
      for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
            if (!(*i)->hidden()) {
                  redirect_children.push_back(*i);
            }
      }
      return redirect_children;
}


void
AudioTimeAxisView::build_playlist_menu (Gtk::Menu * menu)
{
      using namespace Menu_Helpers;

      if (!menu || !is_audio_track()) {
            return;
      }

      MenuList& playlist_items = menu->items();
      menu->set_name ("ArdourContextMenu");
      playlist_items.clear();

      if (playlist_menu) {
            delete playlist_menu;
      }
      playlist_menu = new Menu;
      playlist_menu->set_name ("ArdourContextMenu");

      playlist_items.push_back (MenuElem (compose (_("Current: %1"), get_diskstream()->playlist()->name())));
      playlist_items.push_back (SeparatorElem());
      
      playlist_items.push_back (MenuElem (_("Rename"), slot (*this, &AudioTimeAxisView::rename_current_playlist)));
      playlist_items.push_back (SeparatorElem());

      playlist_items.push_back (MenuElem (_("New"), slot (*this, &AudioTimeAxisView::use_new_playlist)));
      playlist_items.push_back (MenuElem (_("New Copy"), slot(*this, &AudioTimeAxisView::use_copy_playlist)));
      playlist_items.push_back (SeparatorElem());
      playlist_items.push_back (MenuElem (_("Clear Current"), slot (*this, &AudioTimeAxisView::clear_playlist)));
      playlist_items.push_back (SeparatorElem());
      playlist_items.push_back (MenuElem(_("Select"), slot (*this, &AudioTimeAxisView::show_playlist_selector)));

}

void
AudioTimeAxisView::show_playlist_selector ()
{
      editor.playlist_selector().show_for (this);
}


void
AudioTimeAxisView::map_frozen ()
{
      if (!is_audio_track()) {
            return;
      }

      ENSURE_GUI_THREAD (slot (*this, &AudioTimeAxisView::map_frozen));


      switch (audio_track()->freeze_state()) {
      case AudioTrack::Frozen:
            playlist_button.set_sensitive (false);
            rec_enable_button->set_sensitive (false);
            break;
      default:
            playlist_button.set_sensitive (true);
            rec_enable_button->set_sensitive (true);
            break;
      }
}

void
AudioTimeAxisView::show_all_xfades ()
{
      if (view) {
            view->show_all_xfades ();
      }
}

void
AudioTimeAxisView::hide_all_xfades ()
{
      if (view) {
            view->hide_all_xfades ();
      }
}

void
AudioTimeAxisView::hide_dependent_views (TimeAxisViewItem& tavi)
{
      AudioRegionView* rv;

      if (view && (rv = dynamic_cast<AudioRegionView*>(&tavi)) != 0) {
            view->hide_xfades_involving (*rv);
      }
}

void
AudioTimeAxisView::reveal_dependent_views (TimeAxisViewItem& tavi)
{
      AudioRegionView* rv;

      if (view && (rv = dynamic_cast<AudioRegionView*>(&tavi)) != 0) {
            view->reveal_xfades_involving (*rv);
      }
}

void
AudioTimeAxisView::route_active_changed ()
{
      RouteUI::route_active_changed ();

      if (is_audio_track()) {
            if (_route.active()) {
                  controls_ebox.set_name ("AudioTrackControlsBaseUnselected");
                  controls_base_selected_name = "AudioTrackControlsBaseSelected";
                  controls_base_unselected_name = "AudioTrackControlsBaseUnselected";
            } else {
                  controls_ebox.set_name ("AudioTrackControlsBaseInactiveUnselected");
                  controls_base_selected_name = "AudioTrackControlsBaseInactiveSelected";
                  controls_base_unselected_name = "AudioTrackControlsBaseInactiveUnselected";
            }
      } else {
            if (_route.active()) {
                  controls_ebox.set_name ("BusControlsBaseUnselected");
                  controls_base_selected_name = "BusControlsBaseSelected";
                  controls_base_unselected_name = "BusControlsBaseUnselected";
            } else {
                  controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
                  controls_base_selected_name = "BusControlsBaseInactiveSelected";
                  controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
            }
      }
}

XMLNode* 
AudioTimeAxisView::get_child_xml_node (std::string childname)
{
      return RouteUI::get_child_xml_node (childname);
}

Generated by  Doxygen 1.6.0   Back to index