mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-07 07:14:56 +01:00
unecessary changes. (Sorry, doing a "sprint" based thing, this is the end of the first one) Achieved MIDI track and bus creation, associated Jack port and diskstream creation, and minimal GUI stuff for creating them. Should be set to start work on actually recording and playing midi to/from disk now. Relevant (significant) changes: - Creation of a Buffer class. Base class is type agnostic so things can point to a buffer but not care what kind it is (otherwise it'd be a template). Derived into AudioBuffer and MidiBuffer, with a type tag because checking type is necessary in parts of the code where dynamic_cast wouldn't be wise. Originally I considered this a hack, but passing around a type proved to be a very good solution to all the other problems (below). There is a 1:1 mapping between jack port data types and ardour Buffer types (with a conversion function), but that's easily removed if it ever becomes necessary. Having the type scoped in the Buffer class is maybe not the best spot for it, but whatever (this is proof of concept kinda stuff right now...) - IO now has a "default" port type (passed to the constructor and stored as a member), used by ensure_io (and similar) to create n ports. IO::register_***_port has a type argument that defaults to the default type if not passed. Rationale: previous IO API is identical, no changes needed to existing code, but path is paved for multiple port types in one IO, which we will need for eg synth plugin inserts, among other things. This is not quite ideal (best would be to only have the two port register functions and have them take a type), but the alternative is a lot of work (namely destroying the 'ensure' functions and everything that uses them) for very little gain. (I am convinced after quite a few tries at the whiteboard that subclassing IO in any way is not a feasible option, look at it's inheritance diagram in Doxygen and you can see why) - AudioEngine::register_audio_input_port is now register_input_port and takes a type argument. Ditto for output. - (Most significant change) AudioDiskstream abstracted into Distream, and sibling MidiDiskstream created. Very much still a work in progress, but Diskstream is there to switch references over to (most already are), which is the important part. It is still unclear what the MIDI diskstream's relation to channels is, but I'm pretty sure they will be single channel only (so SMF Type 0) since noone can come up with a reason otherwise. - MidiTrack creation. Same thing as AudioTrack but with a different default type basically. No big deal here. - Random cleanups and variable renamings etc. because I have OCD and can't help myself. :) Known broken: Loading of sessions containing MIDI tracks. git-svn-id: svn://localhost/ardour2/branches/midi@641 d708f5d6-7413-0410-9779-e7cbd77b26cf
1985 lines
49 KiB
C++
1985 lines
49 KiB
C++
/*
|
|
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$
|
|
*/
|
|
|
|
#include <cstdlib>
|
|
#include <cmath>
|
|
|
|
#include <algorithm>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <sigc++/bind.h>
|
|
|
|
#include <pbd/error.h>
|
|
#include <pbd/stl_delete.h>
|
|
#include <pbd/whitespace.h>
|
|
|
|
#include <gtkmm2ext/bindable_button.h>
|
|
#include <gtkmm2ext/gtk_ui.h>
|
|
#include <gtkmm2ext/selector.h>
|
|
#include <gtkmm2ext/stop_signal.h>
|
|
#include <gtkmm2ext/utils.h>
|
|
|
|
#include <ardour/audioplaylist.h>
|
|
#include <ardour/audio_diskstream.h>
|
|
#include <ardour/insert.h>
|
|
#include <ardour/ladspa_plugin.h>
|
|
#include <ardour/location.h>
|
|
#include <ardour/panner.h>
|
|
#include <ardour/playlist.h>
|
|
#include <ardour/session.h>
|
|
#include <ardour/session_playlist.h>
|
|
#include <ardour/utils.h>
|
|
|
|
#include "ardour_ui.h"
|
|
#include "audio_time_axis.h"
|
|
#include "automation_gain_line.h"
|
|
#include "automation_pan_line.h"
|
|
#include "automation_time_axis.h"
|
|
#include "canvas_impl.h"
|
|
#include "crossfade_view.h"
|
|
#include "enums.h"
|
|
#include "gain_automation_time_axis.h"
|
|
#include "gui_thread.h"
|
|
#include "keyboard.h"
|
|
#include "pan_automation_time_axis.h"
|
|
#include "playlist_selector.h"
|
|
#include "plugin_selector.h"
|
|
#include "plugin_ui.h"
|
|
#include "point_selection.h"
|
|
#include "prompter.h"
|
|
#include "public_editor.h"
|
|
#include "redirect_automation_line.h"
|
|
#include "redirect_automation_time_axis.h"
|
|
#include "regionview.h"
|
|
#include "rgb_macros.h"
|
|
#include "selection.h"
|
|
#include "simplerect.h"
|
|
#include "streamview.h"
|
|
#include "utils.h"
|
|
|
|
#include <ardour/audio_track.h>
|
|
|
|
#include "i18n.h"
|
|
|
|
using namespace ARDOUR;
|
|
using namespace PBD;
|
|
using namespace LADSPA;
|
|
using namespace Gtk;
|
|
using namespace Editing;
|
|
|
|
|
|
AudioTimeAxisView::AudioTimeAxisView (PublicEditor& ed, Session& sess, Route& rt, Canvas& 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"))
|
|
|
|
{
|
|
_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 Image (get_xpm("small_x.xpm")))));
|
|
|
|
solo_button->signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
|
|
mute_button->signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
|
|
rec_enable_button->signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
|
|
playlist_button.signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
|
|
automation_button.signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
|
|
size_button.signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
|
|
visual_button.signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
|
|
hide_button.signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
|
|
|
|
solo_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::solo_press), false);
|
|
solo_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::solo_release), false);
|
|
mute_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::mute_press), false);
|
|
mute_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::mute_release), false);
|
|
rec_enable_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::rec_enable_press));
|
|
edit_group_button.signal_button_release_event().connect (mem_fun(*this, &AudioTimeAxisView::edit_click), false);
|
|
playlist_button.signal_clicked().connect (mem_fun(*this, &AudioTimeAxisView::playlist_click));
|
|
automation_button.signal_clicked().connect (mem_fun(*this, &AudioTimeAxisView::automation_click));
|
|
size_button.signal_button_release_event().connect (mem_fun(*this, &AudioTimeAxisView::size_click), false);
|
|
visual_button.signal_clicked().connect (mem_fun(*this, &AudioTimeAxisView::visual_click));
|
|
hide_button.signal_clicked().connect (mem_fun(*this, &AudioTimeAxisView::hide_click));
|
|
|
|
if (is_audio_track()) {
|
|
controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
|
|
}
|
|
controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
|
|
controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::FILL|Gtk::EXPAND, 0, 0);
|
|
|
|
controls_table.attach (edit_group_button, 6, 7, 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() && audio_track()->mode() == ARDOUR::Normal) {
|
|
controls_table.attach (playlist_button, 5, 6, 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 ();
|
|
y_position = -1;
|
|
|
|
ensure_xml_node ();
|
|
|
|
set_state (*xml_node);
|
|
|
|
_route.mute_changed.connect (mem_fun(*this, &RouteUI::mute_changed));
|
|
_route.solo_changed.connect (mem_fun(*this, &RouteUI::solo_changed));
|
|
_route.redirects_changed.connect (mem_fun(*this, &AudioTimeAxisView::redirects_changed));
|
|
_route.name_changed.connect (mem_fun(*this, &AudioTimeAxisView::route_name_changed));
|
|
_route.solo_safe_changed.connect (mem_fun(*this, &RouteUI::solo_changed));
|
|
_route.panner().Changed.connect (mem_fun(*this, &AudioTimeAxisView::update_pans));
|
|
|
|
if (is_audio_track()) {
|
|
|
|
/* track */
|
|
|
|
audio_track()->FreezeChange.connect (mem_fun(*this, &AudioTimeAxisView::map_frozen));
|
|
|
|
audio_track()->diskstream_changed.connect (mem_fun(*this, &AudioTimeAxisView::diskstream_changed));
|
|
get_diskstream()->speed_changed.connect (mem_fun(*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 (mem_fun(*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 (mem_fun(*this, &AudioTimeAxisView::reset_samples_per_unit));
|
|
ColorChanged.connect (mem_fun (*this, &AudioTimeAxisView::color_handler));
|
|
}
|
|
|
|
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 (mem_fun(*this, &AudioTimeAxisView::playlist_state_changed));
|
|
modified_connection = pl->Modified.connect (mem_fun(*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 ();
|
|
RadioMenuItem::Group group;
|
|
|
|
items.clear ();
|
|
items.push_back (RadioMenuElem (group, _("No group"),
|
|
bind (mem_fun(*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 (bind (mem_fun (*this, &AudioTimeAxisView::add_edit_group_menu_item), &group));
|
|
edit_group_menu.popup (ev->button, ev->time);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
AudioTimeAxisView::add_edit_group_menu_item (RouteGroup *eg, RadioMenuItem::Group* group)
|
|
{
|
|
using namespace Menu_Helpers;
|
|
|
|
MenuList &items = edit_group_menu.items();
|
|
|
|
cerr << "adding edit group called " << eg->name() << endl;
|
|
|
|
items.push_back (RadioMenuElem (*group, eg->name(), bind (mem_fun(*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 (mem_fun(*this, &AudioTimeAxisView::playlist_state_changed), ignored));
|
|
// why are we here ?
|
|
}
|
|
|
|
void
|
|
AudioTimeAxisView::playlist_changed ()
|
|
|
|
{
|
|
label_view ();
|
|
|
|
if (is_audio_track()) {
|
|
set_playlist (dynamic_cast<AudioPlaylist*>(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 = new SimpleRect (*canvas_display);
|
|
timestretch_rect->property_x1() = 0.0;
|
|
timestretch_rect->property_y1() = 0.0;
|
|
timestretch_rect->property_x2() = 0.0;
|
|
timestretch_rect->property_y2() = 0.0;
|
|
timestretch_rect->property_fill_color_rgba() = color_map[cTimeStretchFill];
|
|
timestretch_rect->property_outline_color_rgba() = color_map[cTimeStretchOutline];
|
|
}
|
|
|
|
timestretch_rect->show ();
|
|
timestretch_rect->raise_to_top ();
|
|
|
|
x1 = start / editor.get_current_zoom();
|
|
x2 = (end - 1) / editor.get_current_zoom();
|
|
y2 = height - 2;
|
|
|
|
timestretch_rect->property_x1() = x1;
|
|
timestretch_rect->property_y1() = 1.0;
|
|
timestretch_rect->property_x2() = x2;
|
|
timestretch_rect->property_y2() = y2;
|
|
}
|
|
|
|
void
|
|
AudioTimeAxisView::hide_timestretch ()
|
|
{
|
|
TimeAxisView::hide_timestretch ();
|
|
|
|
if (timestretch_rect) {
|
|
timestretch_rect->hide ();
|
|
}
|
|
}
|
|
|
|
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 = (height == 0) || (h != height_style);
|
|
|
|
TimeAxisView::set_height (h);
|
|
|
|
ensure_xml_node ();
|
|
|
|
view->set_height ((double) height);
|
|
|
|
switch (height_style) {
|
|
case Largest:
|
|
xml_node->add_property ("track_height", "largest");
|
|
show_name_entry ();
|
|
hide_name_label ();
|
|
controls_table.show_all();
|
|
break;
|
|
case Large:
|
|
xml_node->add_property ("track_height", "large");
|
|
show_name_entry ();
|
|
hide_name_label ();
|
|
controls_table.show_all();
|
|
break;
|
|
case Larger:
|
|
xml_node->add_property ("track_height", "larger");
|
|
show_name_entry ();
|
|
hide_name_label ();
|
|
controls_table.show_all();
|
|
break;
|
|
case Normal:
|
|
xml_node->add_property ("track_height", "normal");
|
|
show_name_entry ();
|
|
hide_name_label ();
|
|
controls_table.show_all();
|
|
break;
|
|
case Smaller:
|
|
xml_node->add_property ("track_height", "smaller");
|
|
controls_table.show_all ();
|
|
show_name_entry ();
|
|
hide_name_label ();
|
|
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 ();
|
|
hide_name_entry ();
|
|
show_name_label ();
|
|
name_label.set_text (_route.name());
|
|
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"), mem_fun(*this, &AudioTimeAxisView::select_track_color)));
|
|
|
|
|
|
items.push_back (SeparatorElem());
|
|
items.push_back (MenuElem (_("Hide all crossfades"), mem_fun(*this, &AudioTimeAxisView::hide_all_xfades)));
|
|
items.push_back (MenuElem (_("Show all crossfades"), mem_fun(*this, &AudioTimeAxisView::show_all_xfades)));
|
|
items.push_back (SeparatorElem());
|
|
|
|
build_remote_control_menu ();
|
|
items.push_back (MenuElem (_("Remote Control ID"), *remote_control_menu));
|
|
|
|
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"),
|
|
mem_fun(*this, &AudioTimeAxisView::show_all_automation)));
|
|
|
|
automation_items.push_back (MenuElem (_("Show existing automation"),
|
|
mem_fun(*this, &AudioTimeAxisView::show_existing_automation)));
|
|
|
|
automation_items.push_back (MenuElem (_("Hide all automation"),
|
|
mem_fun(*this, &AudioTimeAxisView::hide_all_automation)));
|
|
|
|
automation_items.push_back (SeparatorElem());
|
|
|
|
automation_items.push_back (CheckMenuElem (_("Fader"),
|
|
mem_fun(*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"),
|
|
mem_fun(*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"), mem_fun(*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 (mem_fun(*this, &AudioTimeAxisView::set_waveform_shape), Traditional)));
|
|
traditional_item = static_cast<RadioMenuItem *> (&waveform_items.back());
|
|
|
|
waveform_items.push_back (RadioMenuElem (group, _("Rectified"), bind (mem_fun(*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 (mem_fun(*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 (mem_fun(*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 (mem_fun(*this, &AudioTimeAxisView::align_style_changed));
|
|
}
|
|
|
|
items.push_back (SeparatorElem());
|
|
items.push_back (CheckMenuElem (_("Active"), mem_fun(*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"), mem_fun(*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);
|
|
string name;
|
|
|
|
AudioPlaylist *pl;
|
|
AudioDiskstream *ds;
|
|
|
|
if (((ds = get_diskstream()) == 0) || ds->destructive()
|
|
|| ((pl = dynamic_cast<AudioPlaylist*>(ds->playlist())) == 0)) {
|
|
return;
|
|
}
|
|
|
|
prompter.set_prompt (_("Name for playlist"));
|
|
prompter.set_initial_text (pl->name());
|
|
prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
|
|
prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
|
|
|
|
switch (prompter.run ()) {
|
|
case Gtk::RESPONSE_ACCEPT:
|
|
prompter.get_result (name);
|
|
if (name.length()) {
|
|
pl->set_name (name);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
AudioTimeAxisView::use_copy_playlist (bool prompt)
|
|
{
|
|
AudioPlaylist *pl;
|
|
AudioDiskstream *ds;
|
|
string name;
|
|
|
|
if (((ds = get_diskstream()) == 0) || ds->destructive()
|
|
|| ((pl = dynamic_cast<AudioPlaylist*>(ds->playlist())) == 0)) {
|
|
return;
|
|
}
|
|
|
|
name = Playlist::bump_name (pl->name(), _session);
|
|
|
|
if (prompt) {
|
|
|
|
ArdourPrompter prompter (true);
|
|
|
|
prompter.set_prompt (_("Name for Playlist"));
|
|
prompter.set_initial_text (name);
|
|
prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
|
|
prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
|
|
prompter.show_all ();
|
|
|
|
switch (prompter.run ()) {
|
|
case Gtk::RESPONSE_ACCEPT:
|
|
prompter.get_result (name);
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (name.length()) {
|
|
ds->use_copy_playlist ();
|
|
pl = dynamic_cast<AudioPlaylist*>(ds->playlist());
|
|
pl->set_name (name);
|
|
}
|
|
}
|
|
|
|
void
|
|
AudioTimeAxisView::use_new_playlist (bool prompt)
|
|
{
|
|
AudioPlaylist *pl;
|
|
AudioDiskstream *ds;
|
|
string name;
|
|
|
|
if (((ds = get_diskstream()) == 0) || ds->destructive()
|
|
|| ((pl = dynamic_cast<AudioPlaylist*>(ds->playlist())) == 0)) {
|
|
return;
|
|
}
|
|
|
|
name = Playlist::bump_name (pl->name(), _session);
|
|
|
|
if (prompt) {
|
|
|
|
ArdourPrompter prompter (true);
|
|
|
|
prompter.set_prompt (_("Name for Playlist"));
|
|
prompter.set_initial_text (name);
|
|
prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
|
|
prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
|
|
|
|
switch (prompter.run ()) {
|
|
case Gtk::RESPONSE_ACCEPT:
|
|
prompter.get_result (name);
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (name.length()) {
|
|
ds->use_new_playlist ();
|
|
pl = dynamic_cast<AudioPlaylist*>(ds->playlist());
|
|
pl->set_name (name);
|
|
}
|
|
}
|
|
|
|
void
|
|
AudioTimeAxisView::clear_playlist ()
|
|
{
|
|
AudioPlaylist *pl;
|
|
AudioDiskstream *ds;
|
|
|
|
if ((ds = get_diskstream()) != 0) {
|
|
if ((pl = dynamic_cast<AudioPlaylist*>(ds->playlist())) != 0) {
|
|
editor.clear_playlist (*pl);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
AudioTimeAxisView::toggle_waveforms ()
|
|
{
|
|
if (view && waveform_item && !ignore_toggle) {
|
|
view->set_show_waveforms (waveform_item->get_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 ()
|
|
{
|
|
Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &AudioTimeAxisView::reset_samples_per_unit));
|
|
}
|
|
|
|
void
|
|
AudioTimeAxisView::diskstream_changed (void *src)
|
|
{
|
|
Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &AudioTimeAxisView::update_diskstream_display));
|
|
}
|
|
|
|
void
|
|
AudioTimeAxisView::update_diskstream_display ()
|
|
{
|
|
AudioDiskstream *ds;
|
|
|
|
if ((ds = get_diskstream()) != 0) {
|
|
set_playlist (dynamic_cast<AudioPlaylist*> (ds->playlist ()));
|
|
}
|
|
|
|
map_frozen ();
|
|
}
|
|
|
|
void
|
|
AudioTimeAxisView::selection_click (GdkEventButton* ev)
|
|
{
|
|
PublicEditor::TrackViewList* tracks = editor.get_valid_views (this, _route.edit_group());
|
|
|
|
switch (Keyboard::selection_type (ev->state)) {
|
|
case Selection::Toggle:
|
|
/* XXX this is not right */
|
|
editor.get_selection().add (*tracks);
|
|
break;
|
|
|
|
case Selection::Set:
|
|
editor.get_selection().set (*tracks);
|
|
break;
|
|
|
|
case Selection::Extend:
|
|
/* not defined yet */
|
|
break;
|
|
}
|
|
|
|
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 && ((top < 0.0 && bot < 0.0)) || 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
|
|
{
|
|
AudioDiskstream *ds;
|
|
|
|
if ((ds = get_diskstream()) != 0) {
|
|
return ds->playlist();
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
AudioTimeAxisView::name_entry_changed ()
|
|
{
|
|
string x;
|
|
|
|
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.hide_track_in_display (*this);
|
|
}
|
|
|
|
Region*
|
|
AudioTimeAxisView::find_next_region (jack_nframes_t pos, RegionPoint point, int32_t dir)
|
|
{
|
|
AudioDiskstream *stream;
|
|
Playlist *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());
|
|
|
|
line->set_line_color (color_map[cAutomationLine]);
|
|
|
|
|
|
gain_track->add_line (*line);
|
|
|
|
add_child (gain_track);
|
|
|
|
gain_track->Hiding.connect (mem_fun(*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 (mem_fun(*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());
|
|
|
|
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);
|
|
gain_track->canvas_display->show();
|
|
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);
|
|
pan_track->canvas_display->show();
|
|
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: ")
|
|
<< string_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));
|
|
|
|
ral->set_line_color (color_map[cRedirectAutomationLine]);
|
|
ral->queue_reset ();
|
|
|
|
ran->view->add_line (*ral);
|
|
|
|
ran->view->Hiding.connect (bind (mem_fun(*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 (mem_fun(*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->signal_toggled().connect (bind (mem_fun(*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);
|
|
ran->view->canvas_display->show();
|
|
} 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;
|
|
AudioDiskstream* 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 (string_compose (_("Current: %1"), get_diskstream()->playlist()->name())));
|
|
playlist_items.push_back (SeparatorElem());
|
|
|
|
playlist_items.push_back (MenuElem (_("Rename"), mem_fun(*this, &AudioTimeAxisView::rename_current_playlist)));
|
|
playlist_items.push_back (SeparatorElem());
|
|
|
|
playlist_items.push_back (MenuElem (_("New"), mem_fun(editor, &PublicEditor::new_playlists)));
|
|
playlist_items.push_back (MenuElem (_("New Copy"), mem_fun(editor, &PublicEditor::copy_playlists)));
|
|
playlist_items.push_back (SeparatorElem());
|
|
playlist_items.push_back (MenuElem (_("Clear Current"), mem_fun(editor, &PublicEditor::clear_playlists)));
|
|
playlist_items.push_back (SeparatorElem());
|
|
playlist_items.push_back (MenuElem(_("Select"), mem_fun(*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 (mem_fun(*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 (const string & childname)
|
|
{
|
|
return RouteUI::get_child_xml_node (childname);
|
|
}
|
|
|
|
void
|
|
AudioTimeAxisView::color_handler (ColorID id, uint32_t val)
|
|
{
|
|
switch (id) {
|
|
case cTimeStretchOutline:
|
|
timestretch_rect->property_outline_color_rgba() = val;
|
|
break;
|
|
case cTimeStretchFill:
|
|
timestretch_rect->property_fill_color_rgba() = val;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool
|
|
AudioTimeAxisView::select_me (GdkEventButton* ev)
|
|
{
|
|
editor.get_selection().add (this);
|
|
return false;
|
|
}
|