ardour/gtk2_ardour/track_record_axis.cc

907 lines
25 KiB
C++

/*
* Copyright (C) 2020 Robin Gareus <robin@gareus.org>
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <list>
#include <sigc++/bind.h>
#include "pbd/unwind.h"
#include "ardour/logmeter.h"
#include "ardour/meter.h"
#include "ardour/playlist.h"
#include "ardour/route.h"
#include "ardour/route_group.h"
#include "ardour/selection.h"
#include "ardour/session.h"
#include "ardour/track.h"
#include "ardour/audio_track.h"
#include "ardour/midi_track.h"
#include "gtkmm2ext/colors.h"
#include "gtkmm2ext/gtk_ui.h"
#include "gtkmm2ext/keyboard.h"
#include "gtkmm2ext/rgb_macros.h"
#include "gtkmm2ext/utils.h"
#include "widgets/tooltips.h"
#include "ardour_message.h"
#include "ardour_window.h"
#include "context_menu_helper.h"
#include "editor_cursors.h"
#include "group_tabs.h"
#include "gui_thread.h"
#include "keyboard.h"
#include "level_meter.h"
#include "meter_patterns.h"
#include "public_editor.h"
#include "route_group_menu.h"
#include "timers.h"
#include "ui_config.h"
#include "utils.h"
#include "track_record_axis.h"
#include "pbd/i18n.h"
using namespace ARDOUR;
using namespace ArdourMeter;
using namespace ArdourWidgets;
using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtk;
using namespace Gtkmm2ext;
using namespace std;
PBD::Signal1<void, TrackRecordAxis*> TrackRecordAxis::CatchDeletion;
PBD::Signal2<void, TrackRecordAxis*, bool> TrackRecordAxis::EditNextName;
#define PX_SCALE(pxmin, dflt) rint (std::max ((double)pxmin, (double)dflt* UIConfiguration::instance ().get_ui_scale ()))
bool TrackRecordAxis::_size_group_initialized = false;
Glib::RefPtr<Gtk::SizeGroup> TrackRecordAxis::_track_number_size_group;
TrackRecordAxis::TrackRecordAxis (Session* s, std::shared_ptr<ARDOUR::Route> rt)
: SessionHandlePtr (s)
, RouteUI (s)
, _clear_meters (true)
, _route_ops_menu (0)
, _renaming (false)
, _nameentry_ctx (false)
, _input_button (true)
, _playlist_button (S_("RTAV|P"))
, _name_frame (ArdourWidgets::Frame::Horizontal, true)
, _vseparator (1.0)
, _ctrls_button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_BOTH))
, _monitor_ctrl_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_BOTH))
, _track_summary (rt)
{
if (!_size_group_initialized) {
_size_group_initialized = true;
_track_number_size_group = Gtk::SizeGroup::create (Gtk::SIZE_GROUP_BOTH);
}
RouteUI::set_route (rt);
UI::instance ()->theme_changed.connect (sigc::mem_fun (*this, &TrackRecordAxis::on_theme_changed));
UIConfiguration::instance ().ColorsChanged.connect (sigc::mem_fun (*this, &TrackRecordAxis::on_theme_changed));
UIConfiguration::instance ().DPIReset.connect (sigc::mem_fun (*this, &TrackRecordAxis::on_theme_changed));
UIConfiguration::instance ().ParameterChanged.connect (sigc::mem_fun (*this, &TrackRecordAxis::parameter_changed));
Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&TrackRecordAxis::parameter_changed, this, _1), gui_context ());
s->config.ParameterChanged.connect (*this, invalidator (*this), ui_bind (&TrackRecordAxis::parameter_changed, this, _1), gui_context ());
PublicEditor::instance().playhead_cursor()->PositionChanged.connect (*this, invalidator (*this), boost::bind (&TrackSummary::playhead_position_changed, &_track_summary, _1), gui_context());
ResetAllPeakDisplays.connect (sigc::mem_fun (*this, &TrackRecordAxis::reset_peak_display));
ResetRoutePeakDisplays.connect (sigc::mem_fun (*this, &TrackRecordAxis::reset_route_peak_display));
ResetGroupPeakDisplays.connect (sigc::mem_fun (*this, &TrackRecordAxis::reset_group_peak_display));
_number_label.set_name ("tracknumber label");
_number_label.set_elements ((ArdourButton::Element) (ArdourButton::Edge | ArdourButton::Body | ArdourButton::Text | ArdourButton::Inactive));
_number_label.set_alignment (.5, .5);
_number_label.set_fallthrough_to_parent (true);
_number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &TrackRecordAxis::route_ops_click), false);
_playlist_button.set_name ("route button");
_playlist_button.signal_button_press_event().connect (sigc::mem_fun(*this, &TrackRecordAxis::playlist_click), false);
_level_meter = new LevelMeterVBox (s);
_level_meter->set_meter (_route->shared_peak_meter ().get ());
_level_meter->clear_meters ();
_level_meter->setup_meters (120, 10, 3);
name_label.set_name (X_("TrackNameEditor"));
name_label.set_alignment (0.0, 0.5);
name_label.set_padding (4, 0);
name_label.set_width_chars (12);
_namebox.add (name_label);
_namebox.add_events (Gdk::BUTTON_PRESS_MASK);
_namebox.signal_button_press_event ().connect (sigc::mem_fun (*this, &TrackRecordAxis::namebox_button_press));
_name_frame.add (_namebox);
_name_frame.set_edge_color (0x000000ff);
_name_frame.set_border_width (0);
_name_frame.set_padding (0);
_input_button.set_sizing_text ("Capture_8888");
_input_button.set_route (rt, this);
parameter_changed ("editor-stereo-only-meters");
parameter_changed ("time-axis-name-ellipsize-mode");
/* force the track header buttons into a boxy grid-shape */
rec_enable_button->set_tweaks(ArdourButton::Tweaks(ArdourButton::TrackHeader | ArdourButton::ForceBoxy));
mute_button->set_tweaks(ArdourButton::Tweaks(ArdourButton::TrackHeader | ArdourButton::ForceBoxy));
monitor_disk_button->set_tweaks(ArdourButton::Tweaks(ArdourButton::ForceBoxy));
monitor_input_button->set_tweaks(ArdourButton::Tweaks(ArdourButton::ForceBoxy));
_playlist_button.set_tweaks(ArdourButton::Tweaks(ArdourButton::TrackHeader | ArdourButton::ForceBoxy));
_input_button.set_tweaks(ArdourButton::Tweaks(ArdourButton::ForceBoxy));
_number_label.set_tweaks(ArdourButton::Tweaks(ArdourButton::ForceBoxy | ArdourButton::ForceFlat));
_ctrls.attach (*rec_enable_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::FILL, 0, 0);
_ctrls.attach (*mute_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::FILL, 0, 0);
_ctrls.attach (_input_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::FILL, 0, 0);
_ctrls.attach (_playlist_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::FILL, 0, 0);
_ctrls.attach (_name_frame, 5, 6, 0, 1, Gtk::FILL, Gtk::FILL, 0, 0);
_ctrls.attach (*monitor_input_button, 6, 7, 0, 1, Gtk::SHRINK, Gtk::FILL, 0, 0);
_ctrls.attach (*monitor_disk_button, 7, 8, 0, 1, Gtk::SHRINK, Gtk::FILL, 0, 0);
_ctrls.attach (*_level_meter, 8, 9, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
_ctrls.attach (_number_label, 9, 10, 0, 1, Gtk::SHRINK, Gtk::FILL, 0, 0);
_ctrls.attach (_vseparator, 10, 11, 0, 1, Gtk::SHRINK, Gtk::FILL, 0, 0);
_ctrls.attach (_track_summary, 11, 12, 0, 1, Gtk::EXPAND|FILL, Gtk::FILL, 1, 0);
set_tooltip (*mute_button, _("Mute"));
set_tooltip (*rec_enable_button, _("Record"));
set_tooltip (*mute_button, _("Mute"));
set_tooltip (_playlist_button, _("Playlist")); // playlist_tip ()
set_name_label ();
update_sensitivity ();
_track_number_size_group->add_widget (_number_label);
_ctrls_button_size_group->add_widget (*mute_button);
_ctrls_button_size_group->add_widget (_playlist_button);
_monitor_ctrl_size_group->add_widget (*monitor_input_button);
_monitor_ctrl_size_group->add_widget (*monitor_disk_button);
pack_start (_ctrls, false, false);
rec_enable_button->show ();
mute_button->show ();
monitor_input_button->show ();
monitor_disk_button->show ();
mute_button->show ();
_level_meter->show ();
_playlist_button.show();
_number_label.show ();
_name_frame.show ();
_namebox.show ();
name_label.show ();
_input_button.show ();
_track_summary.show ();
_vseparator.show ();
_ctrls.show ();
}
TrackRecordAxis::~TrackRecordAxis ()
{
delete _level_meter;
delete _route_ops_menu;
CatchDeletion (this);
}
void
TrackRecordAxis::self_delete ()
{
delete this;
}
void
TrackRecordAxis::set_session (Session* s)
{
RouteUI::set_session (s);
if (!s) {
return;
}
s->config.ParameterChanged.connect (*this, invalidator (*this), ui_bind (&TrackRecordAxis::parameter_changed, this, _1), gui_context ());
}
void
TrackRecordAxis::route_rec_enable_changed ()
{
if (_route->rec_enable_control()->get_value()) {
/* end renaming when rec-arm engages
* (due to modal grab this can only be triggered by ctrl surfaces) */
end_rename (true);
}
RouteUI::route_rec_enable_changed ();
}
void
TrackRecordAxis::blink_rec_display (bool onoff)
{
RouteUI::blink_rec_display (onoff);
}
std::string
TrackRecordAxis::state_id () const
{
if (_route) {
return string_compose ("recctrl %1", _route->id ().to_s ());
} else {
return string ();
}
}
void
TrackRecordAxis::set_button_names ()
{
mute_button->set_text (S_("Mute|M"));
#if 0
monitor_input_button->set_text (S_("MonitorInput|I"));
monitor_disk_button->set_text (S_("MonitorDisk|D"));
#else
monitor_input_button->set_text (S_("Monitor|In"));
monitor_disk_button->set_text (S_("Monitor|Disk"));
#endif
/* Solo/Listen is N/A */
}
void
TrackRecordAxis::route_property_changed (const PropertyChange& what_changed)
{
if (!what_changed.contains (ARDOUR::Properties::name)) {
return;
}
ENSURE_GUI_THREAD (*this, &TrackRecordAxis::route_property_changed, what_changed);
set_name_label ();
set_tooltip (*_level_meter, _route->name ());
}
void
TrackRecordAxis::route_color_changed ()
{
_number_label.set_fixed_colors (gdk_color_to_rgba (color ()), gdk_color_to_rgba (color ()));
}
void
TrackRecordAxis::on_theme_changed ()
{
}
void
TrackRecordAxis::on_size_request (Gtk::Requisition* r)
{
VBox::on_size_request (r);
}
void
TrackRecordAxis::on_size_allocate (Gtk::Allocation& a)
{
VBox::on_size_allocate (a);
}
void
TrackRecordAxis::parameter_changed (std::string const& p)
{
if (p == "editor-stereo-only-meters") {
#if 0
if (UIConfiguration::instance ().get_editor_stereo_only_meters ()) {
_level_meter->set_max_audio_meter_count (2);
} else {
_level_meter->set_max_audio_meter_count (0);
}
#endif
} else if (p == "time-axis-name-ellipsize-mode") {
set_name_ellipsize_mode ();
}
}
string
TrackRecordAxis::name () const
{
return _route->name ();
}
Gdk::Color
TrackRecordAxis::color () const
{
return RouteUI::route_color ();
}
void
TrackRecordAxis::set_name_label ()
{
string x = _route->name ();
if (x != name_label.get_text ()) {
name_label.set_text (x);
}
set_tooltip (name_label, _route->name ());
const int64_t track_number = _route->track_number ();
assert (track_number > 0);
_number_label.set_text (PBD::to_string (track_number));
}
bool
TrackRecordAxis::namebox_button_press (GdkEventButton* ev)
{
if (_renaming) {
return false;
}
if ((ev->button == 1 && ev->type == GDK_2BUTTON_PRESS) || Keyboard::is_edit_event (ev)) {
if (!start_rename ()) {
ArdourMessageDialog msg (_("Inactive and record-armed tracks cannot be renamed"));
msg.run ();
}
return true;
}
return false;
}
void
TrackRecordAxis::route_active_changed ()
{
RouteUI::route_active_changed ();
update_sensitivity ();
}
void
TrackRecordAxis::map_frozen ()
{
RouteUI::map_frozen ();
switch (track()->freeze_state()) {
case Track::Frozen:
_playlist_button.set_sensitive (false);
break;
default:
_playlist_button.set_sensitive (true);
break;
}
update_sensitivity ();
}
void
TrackRecordAxis::update_sensitivity ()
{
bool en = _route->active ();
monitor_input_button->set_sensitive (en);
monitor_disk_button->set_sensitive (en);
_input_button.set_sensitive (en);
_ctrls.set_sensitive (en);
if (!en) {
end_rename (true);
}
if (!is_track() || track()->mode() != ARDOUR::Normal) {
_playlist_button.set_sensitive (false);
}
}
void
TrackRecordAxis::set_gui_extents (samplepos_t s, samplepos_t e)
{
_track_summary.set_gui_extents (s, e);
}
bool
TrackRecordAxis::rec_extent (samplepos_t& s, samplepos_t& e) const
{
return _track_summary.rec_extent (s, e);
}
int
TrackRecordAxis::summary_xpos () const
{
return _ctrls.get_width () - _track_summary.get_width ();
}
int
TrackRecordAxis::summary_width () const
{
return _track_summary.get_width ();
}
void
TrackRecordAxis::fast_update ()
{
if (_clear_meters) {
_level_meter->clear_meters ();
_clear_meters = false;
}
_level_meter->update_meters ();
}
void
TrackRecordAxis::reset_route_peak_display (Route* route)
{
if (_route && _route.get () == route) {
reset_peak_display ();
}
}
void
TrackRecordAxis::reset_group_peak_display (RouteGroup* group)
{
if (_route && group == _route->route_group ()) {
reset_peak_display ();
}
}
void
TrackRecordAxis::reset_peak_display ()
{
_route->shared_peak_meter ()->reset_max ();
_clear_meters = true;
}
bool
TrackRecordAxis::playlist_click (GdkEventButton* ev)
{
if (ev->button != 1) {
return true;
}
build_playlist_menu ();
_route->session ().selection().select_stripable_and_maybe_group (_route, false, true, 0);
Gtkmm2ext::anchored_menu_popup (playlist_action_menu, &_playlist_button, "", 1, ev->time);
return true;
}
bool
TrackRecordAxis::route_ops_click (GdkEventButton* ev)
{
if (ev->button != 3 ) {
return false;
}
build_route_ops_menu ();
_route->session ().selection().select_stripable_and_maybe_group (_route, false, true, 0);
Gtkmm2ext::anchored_menu_popup (_route_ops_menu, &_number_label, "", 1, ev->time);
return true;
}
void
TrackRecordAxis::build_route_ops_menu ()
{
using namespace Menu_Helpers;
delete _route_ops_menu;
_route_ops_menu = new Menu;
_route_ops_menu->set_name ("ArdourContextMenu");
MenuList& items = _route_ops_menu->items ();
items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
items.push_back (SeparatorElem());
items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
/* do not allow rename if the track is record-enabled */
items.back().set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
}
/* ****************************************************************************/
bool
TrackRecordAxis::start_rename ()
{
if (_renaming || _route->rec_enable_control()->get_value() || !_route->active ()) {
return false;
}
assert (_entry_connections.empty ());
GtkRequisition r (name_label.size_request ());
_nameentry.set_size_request (r.width, -1);
_nameentry.set_text (_route->name ());
_namebox.remove ();
_namebox.add (_nameentry);
_nameentry.show ();
_nameentry.grab_focus ();
_nameentry.add_modal_grab ();
_renaming = true;
_entry_connections.push_back (_nameentry.signal_changed().connect (sigc::mem_fun (*this, &TrackRecordAxis::entry_changed)));
_entry_connections.push_back (_nameentry.signal_activate().connect (sigc::mem_fun (*this, &TrackRecordAxis::entry_activated)));
_entry_connections.push_back (_nameentry.signal_key_press_event().connect (sigc::mem_fun (*this, &TrackRecordAxis::entry_key_press), false));
_entry_connections.push_back (_nameentry.signal_key_release_event().connect (sigc::mem_fun (*this, &TrackRecordAxis::entry_key_release), false));
_entry_connections.push_back (_nameentry.signal_button_press_event ().connect (sigc::mem_fun (*this, &TrackRecordAxis::entry_button_press), false));
_entry_connections.push_back (_nameentry.signal_focus_in_event ().connect (sigc::mem_fun (*this, &TrackRecordAxis::entry_focus_in)));
_entry_connections.push_back (_nameentry.signal_focus_out_event ().connect (sigc::mem_fun (*this, &TrackRecordAxis::entry_focus_out)));
_entry_connections.push_back (_nameentry.signal_populate_popup ().connect (sigc::mem_fun (*this, &TrackRecordAxis::entry_populate_popup)));
return true;
}
void
TrackRecordAxis::end_rename (bool ignore_change)
{
if (!_renaming) {
return;
}
std::string result = _nameentry.get_text ();
disconnect_entry_signals ();
_nameentry.remove_modal_grab ();
_namebox.remove ();
_namebox.add (name_label);
name_label.show ();
_renaming = false;
if (ignore_change) {
return;
}
if (verify_new_route_name (result)) {
_route->set_name (result);
}
}
void
TrackRecordAxis::entry_changed ()
{
}
void
TrackRecordAxis::entry_activated ()
{
end_rename (false);
}
void
TrackRecordAxis::entry_populate_popup (Gtk::Menu*)
{
_nameentry_ctx = true;
}
bool
TrackRecordAxis::entry_focus_in (GdkEventFocus*)
{
_nameentry_ctx = false;
return false;
}
bool
TrackRecordAxis::entry_focus_out (GdkEventFocus*)
{
if (!_nameentry_ctx) {
end_rename (false);
}
return false;
}
bool
TrackRecordAxis::entry_button_press (GdkEventButton* ev)
{
if (Keyboard::is_context_menu_event (ev)) {
return false;
} else if (Gtkmm2ext::event_inside_widget_window (_namebox, (GdkEvent*) ev)) {
return false;
} else {
end_rename (false);
return false;
}
}
bool
TrackRecordAxis::entry_key_press (GdkEventKey* ev)
{
switch (ev->keyval) {
case GDK_Escape:
/* fallthrough */
case GDK_ISO_Left_Tab:
/* fallthrough */
case GDK_Tab:
/* fallthrough */
return true;
default:
break;
}
return false;
}
bool
TrackRecordAxis::entry_key_release (GdkEventKey* ev)
{
switch (ev->keyval) {
case GDK_Escape:
end_rename (true);
return true;
case GDK_ISO_Left_Tab:
end_rename (false);
EditNextName (this, false); /* EMIT SIGNAL */
return true;
case GDK_Tab:
end_rename (false);
EditNextName (this, true); /* EMIT SIGNAL */
return true;
default:
break;
}
return false;
}
void
TrackRecordAxis::disconnect_entry_signals ()
{
for (std::list<sigc::connection>::iterator i = _entry_connections.begin(); i != _entry_connections.end(); ++i) {
i->disconnect ();
}
_entry_connections.clear ();
}
/* ****************************************************************************/
TrackRecordAxis::TrackSummary::TrackSummary (std::shared_ptr<ARDOUR::Route> r)
: _start (0)
, _end (480000)
, _xscale (1)
, _last_playhead (0)
, _rec_updating (false)
, _rec_active (false)
{
_track = std::dynamic_pointer_cast<Track> (r);
assert (_track);
_track->PlaylistChanged.connect (_connections, invalidator (*this), boost::bind (&TrackSummary::playlist_changed, this), gui_context ());
_track->playlist()->ContentsChanged.connect (_playlist_connections, invalidator (*this), boost::bind (&TrackSummary::playlist_contents_changed, this), gui_context ());
_track->presentation_info().PropertyChanged.connect (_connections, invalidator (*this), boost::bind (&TrackSummary::property_changed, this, _1), gui_context ());
_track->rec_enable_control()->Changed.connect (_connections, invalidator (*this), boost::bind (&TrackSummary::maybe_setup_rec_box, this), gui_context());
_track->session().TransportStateChange.connect (_connections, invalidator (*this), boost::bind (&TrackSummary::maybe_setup_rec_box, this), gui_context());
_track->session().TransportLooped.connect (_connections, invalidator (*this), boost::bind (&TrackSummary::maybe_setup_rec_box, this), gui_context());
_track->session().RecordStateChanged.connect (_connections, invalidator (*this), boost::bind (&TrackSummary::maybe_setup_rec_box, this), gui_context());
}
TrackRecordAxis::TrackSummary::~TrackSummary ()
{
_rec_active = false;
if (_rec_updating) {
_screen_update_connection.disconnect();
}
}
void
TrackRecordAxis::TrackSummary::render (Cairo::RefPtr<Cairo::Context> const& cr, cairo_rectangle_t* r)
{
cr->rectangle (r->x, r->y, r->width, r->height);
cr->clip ();
RouteGroup* g = _track->route_group ();
if (g && g->is_color()) {
Gtkmm2ext::set_source_rgba (cr, GroupTabs::group_color (g));
} else {
Gtkmm2ext::set_source_rgba (cr, _track->presentation_info ().color ());
}
double w = get_width();
double h = get_height();
double ht = h - 2;
double yc = 1 + ht / 2.;
cr->set_line_width (ht);
_track->playlist()->foreach_region(sigc::bind (sigc::mem_fun (*this, &TrackSummary::render_region), cr, yc));
/* Record Boxes */
if (_rec_rects.size () > 0) {
Gtkmm2ext::set_source_rgba (cr, UIConfiguration::instance().color_mod("recording rect", "recording_rect"));
for (std::vector<RecInfo>::const_iterator i = _rec_rects.begin (); i != _rec_rects.end (); ++i) {
const samplepos_t rs = i->capture_start;
const samplecnt_t re = i->capture_end;
if (re > rs) {
cr->move_to (sample_to_xpos (rs), yc);
cr->line_to (sample_to_xpos (re), yc);
cr->stroke ();
}
}
}
/* top & btm border */
Gtkmm2ext::set_source_rgba (cr, UIConfiguration::instance().color ("neutral:backgroundest"));
cr->set_line_width (1.0);
cr->move_to (0, 0.5);
cr->line_to (w, 0.5);
cr->stroke ();
cr->move_to (0, h);
cr->line_to (w, h);
cr->stroke ();
/* Playhead */
Gtkmm2ext::set_source_rgba (cr, UIConfiguration::instance().color ("play head"));
const double phx = sample_to_xpos (PublicEditor::instance().playhead_cursor ()->current_sample());
cr->set_line_width (1.0);
cr->move_to (floor (phx) + .5, 0);
cr->line_to (floor (phx) + .5, h);
cr->stroke ();
_last_playhead = phx;
}
void
TrackRecordAxis::TrackSummary::render_region (std::shared_ptr<ARDOUR::Region> r, Cairo::RefPtr<Cairo::Context> const& cr, double y)
{
const samplepos_t rp = r->position_sample ();
const samplecnt_t rl = r->length_samples ();
if (rp > _start) {
cr->move_to (sample_to_xpos (rp), y);
} else {
cr->move_to (0, y);
}
if (rp + rl > _start) {
cr->line_to (sample_to_xpos (rp + rl), y);
cr->stroke ();
} else {
cr->begin_new_path ();
}
}
void
TrackRecordAxis::TrackSummary::maybe_setup_rec_box ()
{
if (_track->session ().transport_stopped_or_stopping () || !(_track->session ().transport_rolling () || _track->session ().get_record_enabled ())) {
/* stopped, or not roll/rec */
if (_rec_updating) {
_rec_rects.clear ();
_screen_update_connection.disconnect();
_rec_updating = false;
_rec_active = false;
set_dirty ();
}
return;
}
if (!_track->rec_enable_control()->get_value() || !_track->session ().actively_recording ()) {
/* rolling but not (or no longer) recording [yet] */
_rec_active = false;
return;
}
if (!_rec_active) {
const samplepos_t rs = _track->current_capture_start ();
_rec_rects.push_back (RecInfo (rs, rs));
}
_rec_active = true;
if (!_rec_updating) {
_screen_update_connection.disconnect();
_screen_update_connection = Timers::rapid_connect (sigc::mem_fun(*this, &TrackSummary::update_rec_box));
_rec_updating = true;
}
}
void
TrackRecordAxis::TrackSummary::update_rec_box ()
{
if (_rec_active && _rec_rects.size () > 0) {
RecInfo& rect = _rec_rects.back ();
rect.capture_start = _track->current_capture_start ();
rect.capture_end = _track->current_capture_end ();
set_dirty ();
}
}
void
TrackRecordAxis::TrackSummary::playhead_position_changed (samplepos_t p)
{
int const o = _last_playhead;
int const n = sample_to_xpos (p);
if (o != n) {
int a = max (2, min (o, n));
int b = max (o, n);
cairo_rectangle_t r;
r.x = a - 2;
r.y = 0;
r.width = b - a + 4;
r.height = get_height ();
set_dirty (&r);
}
}
void
TrackRecordAxis::TrackSummary::playlist_changed ()
{
_playlist_connections.disconnect ();
_track->playlist()->ContentsChanged.connect (_playlist_connections, invalidator (*this), boost::bind (&TrackSummary::playlist_contents_changed, this), gui_context ());
set_dirty ();
}
void
TrackRecordAxis::TrackSummary::playlist_contents_changed ()
{
set_dirty ();
}
void
TrackRecordAxis::TrackSummary::property_changed (PropertyChange const& what_changed)
{
if (what_changed.contains (Properties::color)) {
set_dirty ();
}
}
void
TrackRecordAxis::TrackSummary::on_size_request (Gtk::Requisition* req)
{
req->width = 200;
req->height = 16;
}
void
TrackRecordAxis::TrackSummary::on_size_allocate (Gtk::Allocation& a)
{
CairoWidget::on_size_allocate (a);
if (_end > _start) {
_xscale = static_cast<double> (a.get_width ()) / (_end - _start);
}
}
void
TrackRecordAxis::TrackSummary::set_gui_extents (samplepos_t start, samplepos_t end)
{
if (_start == start && _end == end) {
return;
}
_start = start;
_end = end;
_xscale = static_cast<double> (get_width ()) / (_end - _start);
set_dirty ();
}
bool
TrackRecordAxis::TrackSummary::on_button_press_event (GdkEventButton* ev)
{
if (_track->session ().actively_recording ()) {
return false;
}
// use _start + ev->x / _xscale
_track->session ().request_locate (_start + (double) (_end - _start) * ev->x / get_width ());
return true;
}
bool
TrackRecordAxis::TrackSummary::rec_extent (samplepos_t& start, samplepos_t& end) const
{
if (_rec_rects.size () == 0) {
return false;
}
for (std::vector<RecInfo>::const_iterator i = _rec_rects.begin (); i != _rec_rects.end (); ++i) {
start = std::min (start, i->capture_start);
end = std::max (end, i->capture_end);
}
return true;
}