X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Faudio_clock.cc;h=fd8aca8df18466016f9494a761bc093b22b59ef1;hb=611dcdd24932222d676da4d9a4dca643f79db4a4;hp=68df057b953ed903779c35a373e236f01b92b3df;hpb=8687895abba4209a6de8d8a8fc1bda5996f0d875;p=ardour.git diff --git a/gtk2_ardour/audio_clock.cc b/gtk2_ardour/audio_clock.cc index 68df057b95..fd8aca8df1 100644 --- a/gtk2_ardour/audio_clock.cc +++ b/gtk2_ardour/audio_clock.cc @@ -23,18 +23,24 @@ #include "pbd/convert.h" #include "pbd/enumwriter.h" -#include +#include +#include + +#include "gtkmm2ext/cairocell.h" +#include "gtkmm2ext/utils.h" +#include "gtkmm2ext/rgb_macros.h" -#include "ardour/ardour.h" +#include "ardour/types.h" #include "ardour/session.h" #include "ardour/tempo.h" #include "ardour/profile.h" -#include #include "ardour_ui.h" #include "audio_clock.h" +#include "global_signals.h" #include "utils.h" #include "keyboard.h" +#include "gui_thread.h" #include "i18n.h" using namespace ARDOUR; @@ -44,1143 +50,1439 @@ using namespace std; using Gtkmm2ext::Keyboard; -using PBD::atoi; -using PBD::atof; - sigc::signal AudioClock::ModeChanged; vector AudioClock::clocks; +const double AudioClock::info_font_scale_factor = 0.6; +const double AudioClock::separator_height = 2.0; +const double AudioClock::x_leading_padding = 6.0; + +#define BBT_BAR_CHAR "|" +#define BBT_SCANF_FORMAT "%" PRIu32 "%*c%" PRIu32 "%*c%" PRIu32 + +AudioClock::AudioClock (const string& clock_name, bool transient, const string& widget_name, + bool allow_edit, bool follows_playhead, bool duration, bool with_info) + : ops_menu (0) + , _name (clock_name) + , is_transient (transient) + , is_duration (duration) + , editable (allow_edit) + , _follows_playhead (follows_playhead) + , _off (false) + , _fixed_width (true) + , layout_x_offset (0) + , em_width (0) + , _edit_by_click_field (false) + , editing_attr (0) + , foreground_attr (0) + , first_height (0) + , first_width (0) + , layout_height (0) + , layout_width (0) + , info_height (0) + , upper_height (0) + , mode_based_info_ratio (1.0) + , corner_radius (9) + , editing (false) + , bbt_reference_time (-1) + , last_when(0) + , last_pdelta (0) + , last_sdelta (0) + , dragging (false) + , drag_field (Field (0)) -const uint32_t AudioClock::field_length[(int) AudioClock::AudioFrames+1] = { - 2, /* Timecode_Hours */ - 2, /* Timecode_Minutes */ - 2, /* Timecode_Seconds */ - 2, /* Timecode_Frames */ - 2, /* MS_Hours */ - 2, /* MS_Minutes */ - 5, /* MS_Seconds */ - 3, /* Bars */ - 2, /* Beats */ - 4, /* Tick */ - 10 /* Audio Frame */ -}; - -AudioClock::AudioClock ( - std::string clock_name, bool transient, std::string widget_name, bool allow_edit, bool follows_playhead, bool duration, bool with_info - ) - : _name (clock_name), - is_transient (transient), - is_duration (duration), - editable (allow_edit), - _follows_playhead (follows_playhead), - colon1 (":"), - colon2 (":"), - colon3 (":"), - colon4 (":"), - colon5 (":"), - b1 ("|"), - b2 ("|"), - last_when(0) { - session = 0; - last_when = 0; - last_pdelta = 0; - last_sdelta = 0; - key_entry_state = 0; - ops_menu = 0; - dragging = false; - bbt_reference_time = -1; - - if (with_info) { - frames_upper_info_label = manage (new Label); - frames_lower_info_label = manage (new Label); - timecode_upper_info_label = manage (new Label); - timecode_lower_info_label = manage (new Label); - bbt_upper_info_label = manage (new Label); - bbt_lower_info_label = manage (new Label); - - frames_upper_info_label->set_name ("AudioClockFramesUpperInfo"); - frames_lower_info_label->set_name ("AudioClockFramesLowerInfo"); - timecode_upper_info_label->set_name ("AudioClockTimecodeUpperInfo"); - timecode_lower_info_label->set_name ("AudioClockTimecodeLowerInfo"); - bbt_upper_info_label->set_name ("AudioClockBBTUpperInfo"); - bbt_lower_info_label->set_name ("AudioClockBBTLowerInfo"); - - Gtkmm2ext::set_size_request_to_display_given_text(*timecode_upper_info_label, "23.98",0,0); - Gtkmm2ext::set_size_request_to_display_given_text(*timecode_lower_info_label, "NDF",0,0); - - Gtkmm2ext::set_size_request_to_display_given_text(*bbt_upper_info_label, "88|88",0,0); - Gtkmm2ext::set_size_request_to_display_given_text(*bbt_lower_info_label, "888.88",0,0); - - frames_info_box.pack_start (*frames_upper_info_label, true, true); - frames_info_box.pack_start (*frames_lower_info_label, true, true); - timecode_info_box.pack_start (*timecode_upper_info_label, true, true); - timecode_info_box.pack_start (*timecode_lower_info_label, true, true); - bbt_info_box.pack_start (*bbt_upper_info_label, true, true); - bbt_info_box.pack_start (*bbt_lower_info_label, true, true); - - } else { - frames_upper_info_label = 0; - frames_lower_info_label = 0; - timecode_upper_info_label = 0; - timecode_lower_info_label = 0; - bbt_upper_info_label = 0; - bbt_lower_info_label = 0; - } - - audio_frames_ebox.add (audio_frames_label); - - frames_packer.set_homogeneous (false); - frames_packer.set_border_width (2); - frames_packer.pack_start (audio_frames_ebox, false, false); - - if (with_info) { - frames_packer.pack_start (frames_info_box, false, false, 5); - } - - frames_packer_hbox.pack_start (frames_packer, true, false); - - hours_ebox.add (hours_label); - minutes_ebox.add (minutes_label); - seconds_ebox.add (seconds_label); - frames_ebox.add (frames_label); - bars_ebox.add (bars_label); - beats_ebox.add (beats_label); - ticks_ebox.add (ticks_label); - ms_hours_ebox.add (ms_hours_label); - ms_minutes_ebox.add (ms_minutes_label); - ms_seconds_ebox.add (ms_seconds_label); - - timecode_packer.set_homogeneous (false); - timecode_packer.set_border_width (2); - timecode_packer.pack_start (hours_ebox, false, false); - timecode_packer.pack_start (colon1, false, false); - timecode_packer.pack_start (minutes_ebox, false, false); - timecode_packer.pack_start (colon2, false, false); - timecode_packer.pack_start (seconds_ebox, false, false); - timecode_packer.pack_start (colon3, false, false); - timecode_packer.pack_start (frames_ebox, false, false); - - if (with_info) { - timecode_packer.pack_start (timecode_info_box, false, false, 5); - } + set_flags (CAN_FOCUS); - timecode_packer_hbox.pack_start (timecode_packer, true, false); - - bbt_packer.set_homogeneous (false); - bbt_packer.set_border_width (2); - bbt_packer.pack_start (bars_ebox, false, false); - bbt_packer.pack_start (b1, false, false); - bbt_packer.pack_start (beats_ebox, false, false); - bbt_packer.pack_start (b2, false, false); - bbt_packer.pack_start (ticks_ebox, false, false); + _layout = Pango::Layout::create (get_pango_context()); + _layout->set_attributes (normal_attributes); if (with_info) { - bbt_packer.pack_start (bbt_info_box, false, false, 5); + _left_layout = Pango::Layout::create (get_pango_context()); + _right_layout = Pango::Layout::create (get_pango_context()); } - bbt_packer_hbox.pack_start (bbt_packer, true, false); - - minsec_packer.set_homogeneous (false); - minsec_packer.set_border_width (2); - minsec_packer.pack_start (ms_hours_ebox, false, false); - minsec_packer.pack_start (colon4, false, false); - minsec_packer.pack_start (ms_minutes_ebox, false, false); - minsec_packer.pack_start (colon5, false, false); - minsec_packer.pack_start (ms_seconds_ebox, false, false); - - minsec_packer_hbox.pack_start (minsec_packer, true, false); - - clock_frame.set_shadow_type (Gtk::SHADOW_IN); - clock_frame.set_name ("BaseFrame"); - - clock_frame.add (clock_base); - set_widget_name (widget_name); _mode = BBT; /* lie to force mode switch */ set_mode (Timecode); - - pack_start (clock_frame, true, true); - - /* the clock base handles button releases for menu popup regardless of - editable status. if the clock is editable, the clock base is where - we pass focus to after leaving the last editable "field", which - will then shutdown editing till the user starts it up again. - - it does this because the focus out event on the field disables - keyboard event handling, and we don't connect anything up to - notice focus in on the clock base. hence, keyboard event handling - stays disabled. - */ - - clock_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::SCROLL_MASK); - clock_base.signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &AudioClock::field_button_release_event), Timecode_Hours)); - - Session::TimecodeOffsetChanged.connect (sigc::mem_fun (*this, &AudioClock::timecode_offset_changed)); - - if (editable) { - setup_events (); - } - set (last_when, true); if (!is_transient) { clocks.push_back (this); } + + ColorsChanged.connect (sigc::mem_fun (*this, &AudioClock::set_colors)); + DPIReset.connect (sigc::mem_fun (*this, &AudioClock::dpi_reset)); } -void -AudioClock::set_widget_name (string name) +AudioClock::~AudioClock () { - Widget::set_name (name); - - clock_base.set_name (name); - - audio_frames_label.set_name (name); - hours_label.set_name (name); - minutes_label.set_name (name); - seconds_label.set_name (name); - frames_label.set_name (name); - bars_label.set_name (name); - beats_label.set_name (name); - ticks_label.set_name (name); - ms_hours_label.set_name (name); - ms_minutes_label.set_name (name); - ms_seconds_label.set_name (name); - hours_ebox.set_name (name); - minutes_ebox.set_name (name); - seconds_ebox.set_name (name); - frames_ebox.set_name (name); - audio_frames_ebox.set_name (name); - bars_ebox.set_name (name); - beats_ebox.set_name (name); - ticks_ebox.set_name (name); - ms_hours_ebox.set_name (name); - ms_minutes_ebox.set_name (name); - ms_seconds_ebox.set_name (name); - - colon1.set_name (name); - colon2.set_name (name); - colon3.set_name (name); - colon4.set_name (name); - colon5.set_name (name); - b1.set_name (name); - b2.set_name (name); - - queue_draw (); + delete foreground_attr; + delete editing_attr; } void -AudioClock::setup_events () +AudioClock::set_widget_name (const string& str) { - clock_base.set_flags (Gtk::CAN_FOCUS); - - hours_ebox.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::FOCUS_CHANGE_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK); - minutes_ebox.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::FOCUS_CHANGE_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK); - seconds_ebox.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::FOCUS_CHANGE_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK); - frames_ebox.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::FOCUS_CHANGE_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK); - bars_ebox.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::FOCUS_CHANGE_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK); - beats_ebox.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::FOCUS_CHANGE_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK); - ticks_ebox.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::FOCUS_CHANGE_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK); - ms_hours_ebox.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::FOCUS_CHANGE_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK); - ms_minutes_ebox.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::FOCUS_CHANGE_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK); - ms_seconds_ebox.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::FOCUS_CHANGE_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK); - audio_frames_ebox.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::FOCUS_CHANGE_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK); - - hours_ebox.set_flags (Gtk::CAN_FOCUS); - minutes_ebox.set_flags (Gtk::CAN_FOCUS); - seconds_ebox.set_flags (Gtk::CAN_FOCUS); - frames_ebox.set_flags (Gtk::CAN_FOCUS); - audio_frames_ebox.set_flags (Gtk::CAN_FOCUS); - bars_ebox.set_flags (Gtk::CAN_FOCUS); - beats_ebox.set_flags (Gtk::CAN_FOCUS); - ticks_ebox.set_flags (Gtk::CAN_FOCUS); - ms_hours_ebox.set_flags (Gtk::CAN_FOCUS); - ms_minutes_ebox.set_flags (Gtk::CAN_FOCUS); - ms_seconds_ebox.set_flags (Gtk::CAN_FOCUS); - - hours_ebox.signal_motion_notify_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_motion_notify_event), Timecode_Hours)); - minutes_ebox.signal_motion_notify_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_motion_notify_event), Timecode_Minutes)); - seconds_ebox.signal_motion_notify_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_motion_notify_event), Timecode_Seconds)); - frames_ebox.signal_motion_notify_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_motion_notify_event), Timecode_Frames)); - audio_frames_ebox.signal_motion_notify_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_motion_notify_event), AudioFrames)); - bars_ebox.signal_motion_notify_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_motion_notify_event), Bars)); - beats_ebox.signal_motion_notify_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_motion_notify_event), Beats)); - ticks_ebox.signal_motion_notify_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_motion_notify_event), Ticks)); - ms_hours_ebox.signal_motion_notify_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_motion_notify_event), MS_Hours)); - ms_minutes_ebox.signal_motion_notify_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_motion_notify_event), MS_Minutes)); - ms_seconds_ebox.signal_motion_notify_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_motion_notify_event), MS_Seconds)); - - hours_ebox.signal_button_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_press_event), Timecode_Hours)); - minutes_ebox.signal_button_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_press_event), Timecode_Minutes)); - seconds_ebox.signal_button_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_press_event), Timecode_Seconds)); - frames_ebox.signal_button_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_press_event), Timecode_Frames)); - audio_frames_ebox.signal_button_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_press_event), AudioFrames)); - bars_ebox.signal_button_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_press_event), Bars)); - beats_ebox.signal_button_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_press_event), Beats)); - ticks_ebox.signal_button_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_press_event), Ticks)); - ms_hours_ebox.signal_button_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_press_event), MS_Hours)); - ms_minutes_ebox.signal_button_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_press_event), MS_Minutes)); - ms_seconds_ebox.signal_button_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_press_event), MS_Seconds)); - - hours_ebox.signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_release_event), Timecode_Hours)); - minutes_ebox.signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_release_event), Timecode_Minutes)); - seconds_ebox.signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_release_event), Timecode_Seconds)); - frames_ebox.signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_release_event), Timecode_Frames)); - audio_frames_ebox.signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_release_event), AudioFrames)); - bars_ebox.signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_release_event), Bars)); - beats_ebox.signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_release_event), Beats)); - ticks_ebox.signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_release_event), Ticks)); - ms_hours_ebox.signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_release_event), MS_Hours)); - ms_minutes_ebox.signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_release_event), MS_Minutes)); - ms_seconds_ebox.signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_release_event), MS_Seconds)); - - hours_ebox.signal_scroll_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_scroll_event), Timecode_Hours)); - minutes_ebox.signal_scroll_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_scroll_event), Timecode_Minutes)); - seconds_ebox.signal_scroll_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_scroll_event), Timecode_Seconds)); - frames_ebox.signal_scroll_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_scroll_event), Timecode_Frames)); - audio_frames_ebox.signal_scroll_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_scroll_event), AudioFrames)); - bars_ebox.signal_scroll_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_scroll_event), Bars)); - beats_ebox.signal_scroll_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_scroll_event), Beats)); - ticks_ebox.signal_scroll_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_scroll_event), Ticks)); - ms_hours_ebox.signal_scroll_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_scroll_event), MS_Hours)); - ms_minutes_ebox.signal_scroll_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_scroll_event), MS_Minutes)); - ms_seconds_ebox.signal_scroll_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_button_scroll_event), MS_Seconds)); - - hours_ebox.signal_key_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_press_event), Timecode_Hours)); - minutes_ebox.signal_key_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_press_event), Timecode_Minutes)); - seconds_ebox.signal_key_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_press_event), Timecode_Seconds)); - frames_ebox.signal_key_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_press_event), Timecode_Frames)); - audio_frames_ebox.signal_key_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_press_event), AudioFrames)); - bars_ebox.signal_key_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_press_event), Bars)); - beats_ebox.signal_key_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_press_event), Beats)); - ticks_ebox.signal_key_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_press_event), Ticks)); - ms_hours_ebox.signal_key_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_press_event), MS_Hours)); - ms_minutes_ebox.signal_key_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_press_event), MS_Minutes)); - ms_seconds_ebox.signal_key_press_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_press_event), MS_Seconds)); - - hours_ebox.signal_key_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_release_event), Timecode_Hours)); - minutes_ebox.signal_key_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_release_event), Timecode_Minutes)); - seconds_ebox.signal_key_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_release_event), Timecode_Seconds)); - frames_ebox.signal_key_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_release_event), Timecode_Frames)); - audio_frames_ebox.signal_key_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_release_event), AudioFrames)); - bars_ebox.signal_key_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_release_event), Bars)); - beats_ebox.signal_key_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_release_event), Beats)); - ticks_ebox.signal_key_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_release_event), Ticks)); - ms_hours_ebox.signal_key_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_release_event), MS_Hours)); - ms_minutes_ebox.signal_key_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_release_event), MS_Minutes)); - ms_seconds_ebox.signal_key_release_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_key_release_event), MS_Seconds)); - - hours_ebox.signal_focus_in_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_in_event), Timecode_Hours)); - minutes_ebox.signal_focus_in_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_in_event), Timecode_Minutes)); - seconds_ebox.signal_focus_in_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_in_event), Timecode_Seconds)); - frames_ebox.signal_focus_in_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_in_event), Timecode_Frames)); - audio_frames_ebox.signal_focus_in_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_in_event), AudioFrames)); - bars_ebox.signal_focus_in_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_in_event), Bars)); - beats_ebox.signal_focus_in_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_in_event), Beats)); - ticks_ebox.signal_focus_in_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_in_event), Ticks)); - ms_hours_ebox.signal_focus_in_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_in_event), MS_Hours)); - ms_minutes_ebox.signal_focus_in_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_in_event), MS_Minutes)); - ms_seconds_ebox.signal_focus_in_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_in_event), MS_Seconds)); - - hours_ebox.signal_focus_out_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_out_event), Timecode_Hours)); - minutes_ebox.signal_focus_out_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_out_event), Timecode_Minutes)); - seconds_ebox.signal_focus_out_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_out_event), Timecode_Seconds)); - frames_ebox.signal_focus_out_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_out_event), Timecode_Frames)); - audio_frames_ebox.signal_focus_out_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_out_event), AudioFrames)); - bars_ebox.signal_focus_out_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_out_event), Bars)); - beats_ebox.signal_focus_out_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_out_event), Beats)); - ticks_ebox.signal_focus_out_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_out_event), Ticks)); - ms_hours_ebox.signal_focus_out_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_out_event), MS_Hours)); - ms_minutes_ebox.signal_focus_out_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_out_event), MS_Minutes)); - ms_seconds_ebox.signal_focus_out_event().connect (sigc::bind (sigc::mem_fun(*this, &AudioClock::field_focus_out_event), MS_Seconds)); - - clock_base.signal_focus_in_event().connect (sigc::mem_fun (*this, &AudioClock::drop_focus_handler)); -} + if (str.empty()) { + set_name ("clock"); + } else { + set_name (str + " clock"); + } -bool -AudioClock::drop_focus_handler (GdkEventFocus*) -{ - Keyboard::magic_widget_drop_focus (); - return false; + if (is_realized()) { + set_colors (); + } } + void AudioClock::on_realize () { - HBox::on_realize (); - - /* styles are not available until the widgets are bound to a window */ - - set_size_requests (); + CairoWidget::on_realize (); + set_font (); + set_colors (); } void -AudioClock::set (nframes_t when, bool force, nframes_t offset, char which) +AudioClock::set_font () { + Glib::RefPtr style = get_style (); + Pango::FontDescription font; + Pango::AttrFontDesc* font_attr; - if ((!force && !is_visible()) || session == 0) { - return; - } - - if (when == last_when && !offset && !force) { - return; - } - - bool pdelta = Config->get_primary_clock_delta_edit_cursor(); - bool sdelta = Config->get_secondary_clock_delta_edit_cursor(); - - if (offset && which == 'p' && pdelta) { - when = (when > offset) ? when - offset : offset - when; - } else if (offset && which == 's' && sdelta) { - when = (when > offset) ? when - offset : offset - when; + if (!is_realized()) { + font = get_font_for_style (get_name()); + } else { + font = style->get_font(); } - if (which == 'p' && pdelta && !last_pdelta) { - set_widget_name("TransportClockDisplayDelta"); - last_pdelta = true; - } else if (which == 'p' && !pdelta && last_pdelta) { - set_widget_name("TransportClockDisplay"); - last_pdelta = false; - } else if (which == 's' && sdelta && !last_sdelta) { - set_widget_name("SecondaryClockDisplayDelta"); - last_sdelta = true; - } else if (which == 's' && !sdelta && last_sdelta) { - set_widget_name("SecondaryClockDisplay"); - last_sdelta = false; - } + font_attr = new Pango::AttrFontDesc (Pango::Attribute::create_attr_font_desc (font)); - switch (_mode) { - case Timecode: - set_timecode (when, force); - break; + normal_attributes.change (*font_attr); + editing_attributes.change (*font_attr); - case BBT: - set_bbt (when, force); - break; + /* now a smaller version of the same font */ - case MinSec: - set_minsec (when, force); - break; + delete font_attr; + font.set_size ((int) lrint (font.get_size() * info_font_scale_factor)); + font.set_weight (Pango::WEIGHT_NORMAL); + font_attr = new Pango::AttrFontDesc (Pango::Attribute::create_attr_font_desc (font)); + + info_attributes.change (*font_attr); + + delete font_attr; - case Frames: - set_frames (when, force); - break; + /* get the figure width for the font. This doesn't have to super + * accurate since we only use it to measure the (roughly 1 character) + * offset from the position Pango tells us for the "cursor" + */ - case Off: - break; - } + Glib::RefPtr tmp = Pango::Layout::create (get_pango_context()); + int ignore_height; - last_when = when; + tmp->set_text ("8"); + tmp->get_pixel_size (em_width, ignore_height); } void -AudioClock::timecode_offset_changed () +AudioClock::set_active_state (Gtkmm2ext::ActiveState s) { - nframes_t current; - - switch (_mode) { - case Timecode: - if (is_duration) { - current = current_duration(); - } else { - current = current_time (); - } - set (current, true); - break; - default: - break; - } + CairoWidget::set_active_state (s); + set_colors (); } void -AudioClock::set_frames (nframes_t when, bool /*force*/) +AudioClock::set_colors () { - char buf[32]; - snprintf (buf, sizeof (buf), "%u", when); - audio_frames_label.set_text (buf); + int r, g, b, a; + + uint32_t bg_color; + uint32_t text_color; + uint32_t editing_color; + uint32_t cursor_color; + + if (active_state()) { + bg_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1 active: background", get_name())); + text_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1 active: text", get_name())); + editing_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1 active: edited text", get_name())); + cursor_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1 active: cursor", get_name())); + } else { + bg_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: background", get_name())); + text_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: text", get_name())); + editing_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: edited text", get_name())); + cursor_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: cursor", get_name())); + } - if (frames_upper_info_label) { - nframes_t rate = session->frame_rate(); + /* store for bg and cursor in render() */ - if (fmod (rate, 1000.0) == 0.000) { - sprintf (buf, "%uK", rate/1000); - } else { - sprintf (buf, "%.3fK", rate/1000.0f); - } + UINT_TO_RGBA (bg_color, &r, &g, &b, &a); - if (frames_upper_info_label->get_text() != buf) { - frames_upper_info_label->set_text (buf); - } + bg_r = r/255.0; + bg_g = g/255.0; + bg_b = b/255.0; + bg_a = a/255.0; - float vid_pullup = session->config.get_video_pullup(); + UINT_TO_RGBA (cursor_color, &r, &g, &b, &a); - if (vid_pullup == 0.0) { - if (frames_lower_info_label->get_text () != _("none")) { - frames_lower_info_label->set_text(_("none")); - } - } else { - sprintf (buf, "%-6.4f", vid_pullup); - if (frames_lower_info_label->get_text() != buf) { - frames_lower_info_label->set_text (buf); - } - } - } -} + cursor_r = r/255.0; + cursor_g = g/255.0; + cursor_b = b/255.0; + cursor_a = a/255.0; -void -AudioClock::set_minsec (nframes_t when, bool force) -{ - char buf[32]; - nframes_t left; - int hrs; - int mins; - float secs; + /* rescale for Pango colors ... sigh */ - left = when; - hrs = (int) floor (left / (session->frame_rate() * 60.0f * 60.0f)); - left -= (nframes_t) floor (hrs * session->frame_rate() * 60.0f * 60.0f); - mins = (int) floor (left / (session->frame_rate() * 60.0f)); - left -= (nframes_t) floor (mins * session->frame_rate() * 60.0f); - secs = left / (float) session->frame_rate(); + r = lrint (r * 65535.0); + g = lrint (g * 65535.0); + b = lrint (b * 65535.0); - if (force || hrs != ms_last_hrs) { - sprintf (buf, "%02d", hrs); - ms_hours_label.set_text (buf); - ms_last_hrs = hrs; - } + UINT_TO_RGBA (text_color, &r, &g, &b, &a); + r = lrint ((r/255.0) * 65535.0); + g = lrint ((g/255.0) * 65535.0); + b = lrint ((b/255.0) * 65535.0); + foreground_attr = new Pango::AttrColor (Pango::Attribute::create_attr_foreground (r, g, b)); - if (force || mins != ms_last_mins) { - sprintf (buf, "%02d", mins); - ms_minutes_label.set_text (buf); - ms_last_mins = mins; + UINT_TO_RGBA (editing_color, &r, &g, &b, &a); + r = lrint ((r/255.0) * 65535.0); + g = lrint ((g/255.0) * 65535.0); + b = lrint ((b/255.0) * 65535.0); + editing_attr = new Pango::AttrColor (Pango::Attribute::create_attr_foreground (r, g, b)); + + normal_attributes.change (*foreground_attr); + info_attributes.change (*foreground_attr); + editing_attributes.change (*foreground_attr); + editing_attributes.change (*editing_attr); + + if (!editing) { + _layout->set_attributes (normal_attributes); + } else { + _layout->set_attributes (editing_attributes); } - if (force || secs != ms_last_secs) { - sprintf (buf, "%06.3f", secs); - ms_seconds_label.set_text (buf); - ms_last_secs = secs; + if (_left_layout) { + _left_layout->set_attributes (info_attributes); + _right_layout->set_attributes (info_attributes); } + + queue_draw (); } void -AudioClock::set_timecode (nframes_t when, bool force) +AudioClock::render (cairo_t* cr) { - char buf[32]; - Timecode::Time timecode; + /* main layout: rounded rect, plus the text */ + + if (_need_bg) { + cairo_set_source_rgba (cr, bg_r, bg_g, bg_b, bg_a); + if (corner_radius) { + Gtkmm2ext::rounded_rectangle (cr, 0, 0, get_width(), upper_height, corner_radius); + } else { + cairo_rectangle (cr, 0, 0, get_width(), upper_height); + } + cairo_fill (cr); + } - if (is_duration) { - session->timecode_duration (when, timecode); + if (!_fixed_width) { + cairo_move_to (cr, layout_x_offset, 0); } else { - session->timecode_time (when, timecode); + cairo_move_to (cr, layout_x_offset, (upper_height - layout_height) / 2.0); } - if (force || timecode.hours != last_hrs || timecode.negative != last_negative) { - if (timecode.negative) { - sprintf (buf, "-%02" PRIu32, timecode.hours); - } else { - sprintf (buf, " %02" PRIu32, timecode.hours); + pango_cairo_show_layout (cr, _layout->gobj()); + + if (_left_layout) { + + double h = get_height() - upper_height - separator_height; + + if (_need_bg) { + cairo_set_source_rgba (cr, bg_r, bg_g, bg_b, bg_a); } - hours_label.set_text (buf); - last_hrs = timecode.hours; - last_negative = timecode.negative; - } - if (force || timecode.minutes != last_mins) { - sprintf (buf, "%02" PRIu32, timecode.minutes); - minutes_label.set_text (buf); - last_mins = timecode.minutes; - } + if (mode_based_info_ratio != 1.0) { - if (force || timecode.seconds != last_secs) { - sprintf (buf, "%02" PRIu32, timecode.seconds); - seconds_label.set_text (buf); - last_secs = timecode.seconds; - } + double left_rect_width = round (((get_width() - separator_height) * mode_based_info_ratio) + 0.5); - if (force || timecode.frames != last_frames) { - sprintf (buf, "%02" PRIu32, timecode.frames); - frames_label.set_text (buf); - last_frames = timecode.frames; - } + if (_need_bg) { + if (corner_radius) { + Gtkmm2ext::rounded_rectangle (cr, 0, upper_height + separator_height, left_rect_width, h, corner_radius); + } else { + cairo_rectangle (cr, 0, upper_height + separator_height, left_rect_width, h); + } + cairo_fill (cr); + } + + cairo_move_to (cr, x_leading_padding, upper_height + separator_height + ((h - info_height)/2.0)); + pango_cairo_show_layout (cr, _left_layout->gobj()); + + if (_need_bg) { + if (corner_radius) { + Gtkmm2ext::rounded_rectangle (cr, left_rect_width + separator_height, upper_height + separator_height, + get_width() - separator_height - left_rect_width, h, + corner_radius); + } else { + cairo_rectangle (cr, left_rect_width + separator_height, upper_height + separator_height, + get_width() - separator_height - left_rect_width, h); + } + cairo_fill (cr); + } - if (timecode_upper_info_label) { - double timecode_frames = session->timecode_frames_per_second(); + cairo_move_to (cr, x_leading_padding + left_rect_width + separator_height, upper_height + separator_height + ((h - info_height)/2.0)); + pango_cairo_show_layout (cr, _right_layout->gobj()); - if ( fmod(timecode_frames, 1.0) == 0.0) { - sprintf (buf, "%u", int (timecode_frames)); } else { - sprintf (buf, "%.2f", timecode_frames); - } + /* no info to display, or just one */ - if (timecode_upper_info_label->get_text() != buf) { - timecode_upper_info_label->set_text (buf); + if (_need_bg) { + if (corner_radius) { + Gtkmm2ext::rounded_rectangle (cr, 0, upper_height + separator_height, get_width(), h, corner_radius); + } else { + cairo_rectangle (cr, 0, upper_height + separator_height, get_width(), h); + } + cairo_fill (cr); + } } + } - if ((fabs(timecode_frames - 29.97) < 0.0001) || timecode_frames == 30) { - if (session->timecode_drop_frames()) { - sprintf (buf, "DF"); + if (editing) { + if (!insert_map.empty()) { + + + if (input_string.length() < insert_map.size()) { + Pango::Rectangle cursor; + + if (input_string.empty()) { + /* nothing entered yet, put cursor at the end + of string + */ + cursor = _layout->get_cursor_strong_pos (edit_string.length() - 1); + } else { + cursor = _layout->get_cursor_strong_pos (insert_map[input_string.length()]); + } + + cairo_set_source_rgba (cr, cursor_r, cursor_g, cursor_b, cursor_a); + if (!_fixed_width) { + cairo_rectangle (cr, + min (get_width() - 2.0, + (double) cursor.get_x()/PANGO_SCALE + layout_x_offset + em_width), 0, + 2.0, cursor.get_height()/PANGO_SCALE); + } else { + cairo_rectangle (cr, + min (get_width() - 2.0, + (double) layout_x_offset + cursor.get_x()/PANGO_SCALE + em_width), + (upper_height - layout_height)/2.0, + 2.0, cursor.get_height()/PANGO_SCALE); + } + cairo_fill (cr); } else { - sprintf (buf, "NDF"); + /* we've entered all possible digits, no cursor */ } - } else { - // there is no drop frame alternative - buf[0] = '\0'; - } - if (timecode_lower_info_label->get_text() != buf) { - timecode_lower_info_label->set_text (buf); + } else { + if (input_string.empty()) { + cairo_set_source_rgba (cr, cursor_r, cursor_g, cursor_b, cursor_a); + if (!_fixed_width) { + cairo_rectangle (cr, + (get_width()/2.0), + 0, + 2.0, upper_height); + } else { + cairo_rectangle (cr, + (get_width()/2.0), + (upper_height - layout_height)/2.0, + 2.0, upper_height); + } + cairo_fill (cr); + } } } } void -AudioClock::set_bbt (nframes_t when, bool force) +AudioClock::on_size_allocate (Gtk::Allocation& alloc) { - char buf[16]; - BBT_Time bbt; - - /* handle a common case */ - if (is_duration) { - if (when == 0) { - bbt.bars = 0; - bbt.beats = 0; - bbt.ticks = 0; - } else { - session->tempo_map().bbt_time (when, bbt); - bbt.bars--; - bbt.beats--; - } + CairoWidget::on_size_allocate (alloc); + + if (_left_layout) { + upper_height = (get_height()/2.0) - 1.0; } else { - session->tempo_map().bbt_time (when, bbt); + upper_height = get_height(); } - sprintf (buf, "%03" PRIu32, bbt.bars); - if (force || bars_label.get_text () != buf) { - bars_label.set_text (buf); - } - sprintf (buf, "%02" PRIu32, bbt.beats); - if (force || beats_label.get_text () != buf) { - beats_label.set_text (buf); + if (_fixed_width) { + /* center display in available space */ + layout_x_offset = (get_width() - layout_width)/2.0; + } else { + /* left justify */ + layout_x_offset = 0; } - sprintf (buf, "%04" PRIu32, bbt.ticks); - if (force || ticks_label.get_text () != buf) { - ticks_label.set_text (buf); +} + +void +AudioClock::on_size_request (Gtk::Requisition* req) +{ + /* even for non fixed width clocks, the size we *ask* for never changes, + even though the size we receive might. so once we've computed it, + just return it. + */ + + if (first_width) { + req->width = first_width; + req->height = first_height; + return; } - if (bbt_upper_info_label) { - nframes64_t pos; + Glib::RefPtr tmp; + Glib::RefPtr style = get_style (); + Pango::FontDescription font; - if (bbt_reference_time < 0) { - pos = when; - } else { - pos = bbt_reference_time; - } + tmp = Pango::Layout::create (get_pango_context()); + + if (!is_realized()) { + font = get_font_for_style (get_name()); + } else { + font = style->get_font(); + } - TempoMetric m (session->tempo_map().metric_at (pos)); + tmp->set_font_description (font); - sprintf (buf, "%-5.2f", m.tempo().beats_per_minute()); - if (bbt_lower_info_label->get_text() != buf) { - bbt_lower_info_label->set_text (buf); - } - sprintf (buf, "%g|%g", m.meter().beats_per_bar(), m.meter().note_divisor()); - if (bbt_upper_info_label->get_text() != buf) { - bbt_upper_info_label->set_text (buf); + if (_fixed_width) { + /* this string is the longest thing we will ever display, + and also includes the BBT bar char that may descends below + the baseline a bit, and a comma for the minsecs mode + where we printf a fractional value (XXX or should) + */ + + tmp->set_text (" 8888888888:,|"); + } else { + switch (_mode) { + case Timecode: + tmp->set_text (" 88:88:88:88"); + break; + case BBT: + tmp->set_text (" 888|88|8888"); + break; + case MinSec: + tmp->set_text (" 88:88:88,888"); + break; + case Frames: + tmp->set_text (" 8888888888"); + break; } } -} -void -AudioClock::set_session (Session *s) -{ - session = s; + tmp->get_pixel_size (req->width, req->height); - if (s) { + layout_height = req->height; + layout_width = req->width; - XMLProperty* prop; - XMLNode* node = session->extra_xml (X_("ClockModes")); - AudioClock::Mode amode; + /* now tackle height, for which we need to know the height of the lower + * layout + */ - if (node) { - if ((prop = node->property (_name)) != 0) { - amode = AudioClock::Mode (string_2_enum (prop->value(), amode)); - set_mode (amode); - } - } + if (_left_layout) { - set (last_when, true); + int w; + + font.set_size ((int) lrint (font.get_size() * info_font_scale_factor)); + font.set_weight (Pango::WEIGHT_NORMAL); + tmp->set_font_description (font); + + /* we only care about height, so put as much stuff in here + as possible that might change the height. + */ + tmp->set_text ("qyhH|"); /* one ascender, one descender */ + + tmp->get_pixel_size (w, info_height); + + /* silly extra padding that seems necessary to correct the info + * that pango just gave us. I have no idea why. + */ + + info_height += 4; + + req->height += info_height; + req->height += separator_height; } + + first_height = req->height; + first_width = req->width; } void -AudioClock::focus () +AudioClock::show_edit_status (int length) { - switch (_mode) { - case Timecode: - hours_ebox.grab_focus (); - break; - - case BBT: - bars_ebox.grab_focus (); - break; + editing_attr->set_start_index (edit_string.length() - length); + editing_attr->set_end_index (edit_string.length()); + + editing_attributes.change (*foreground_attr); + editing_attributes.change (*editing_attr); - case MinSec: - ms_hours_ebox.grab_focus (); - break; + _layout->set_attributes (editing_attributes); +} - case Frames: - frames_ebox.grab_focus (); - break; +void +AudioClock::start_edit (Field f) +{ + pre_edit_string = _layout->get_text (); + if (!insert_map.empty()) { + edit_string = pre_edit_string; + } else { + edit_string.clear (); + _layout->set_text (""); + } + input_string.clear (); + editing = true; - case Off: - break; + if (f) { + input_string = get_field (f); + show_edit_status (merge_input_and_edit_string ()); + _layout->set_text (edit_string); } -} + queue_draw (); -bool -AudioClock::field_key_press_event (GdkEventKey */*ev*/, Field /*field*/) -{ - /* all key activity is handled on key release */ - return true; + Keyboard::magic_widget_grab_focus (); + grab_focus (); } -bool -AudioClock::field_key_release_event (GdkEventKey *ev, Field field) +string +AudioClock::get_field (Field f) { - Label *label = 0; - string new_text; - char new_char = 0; - bool move_on = false; - - switch (field) { + switch (f) { case Timecode_Hours: - label = &hours_label; + return edit_string.substr (1, 2); break; case Timecode_Minutes: - label = &minutes_label; + return edit_string.substr (4, 2); break; case Timecode_Seconds: - label = &seconds_label; + return edit_string.substr (7, 2); break; case Timecode_Frames: - label = &frames_label; - break; - - case AudioFrames: - label = &audio_frames_label; + return edit_string.substr (10, 2); break; - case MS_Hours: - label = &ms_hours_label; + return edit_string.substr (1, 2); break; case MS_Minutes: - label = &ms_minutes_label; + return edit_string.substr (4, 2); break; case MS_Seconds: - label = &ms_seconds_label; + return edit_string.substr (7, 2); + break; + case MS_Milliseconds: + return edit_string.substr (10, 3); break; - case Bars: - label = &bars_label; + return edit_string.substr (1, 3); break; case Beats: - label = &beats_label; + return edit_string.substr (5, 2); break; case Ticks: - label = &ticks_label; + return edit_string.substr (8, 4); + break; + case AudioFrames: + return edit_string; break; - default: - return false; } + return ""; +} - switch (ev->keyval) { - case GDK_0: - case GDK_KP_0: - new_char = '0'; - break; - case GDK_1: - case GDK_KP_1: - new_char = '1'; - break; - case GDK_2: - case GDK_KP_2: - new_char = '2'; - break; - case GDK_3: - case GDK_KP_3: - new_char = '3'; - break; - case GDK_4: - case GDK_KP_4: - new_char = '4'; - break; - case GDK_5: - case GDK_KP_5: - new_char = '5'; - break; - case GDK_6: - case GDK_KP_6: - new_char = '6'; - break; - case GDK_7: - case GDK_KP_7: - new_char = '7'; - break; - case GDK_8: - case GDK_KP_8: - new_char = '8'; - break; - case GDK_9: - case GDK_KP_9: - new_char = '9'; - break; +void +AudioClock::end_edit (bool modify) +{ + if (modify) { - case GDK_period: - case GDK_KP_Decimal: - if (_mode == MinSec && field == MS_Seconds) { - new_char = '.'; + bool ok = true; + + switch (_mode) { + case Timecode: + ok = timecode_validate_edit (edit_string); + break; + + case BBT: + ok = bbt_validate_edit (edit_string); + break; + + case MinSec: + ok = minsec_validate_edit (edit_string); + break; + + case Frames: + break; + } + + if (!ok) { + edit_string = pre_edit_string; + input_string.clear (); + _layout->set_text (edit_string); + show_edit_status (0); + /* edit attributes remain in use */ } else { - return false; + + editing = false; + framepos_t pos = 0; /* stupid gcc */ + + switch (_mode) { + case Timecode: + pos = frames_from_timecode_string (edit_string); + break; + + case BBT: + if (is_duration) { + pos = frame_duration_from_bbt_string (0, edit_string); + } else { + pos = frames_from_bbt_string (0, edit_string); + } + break; + + case MinSec: + pos = frames_from_minsec_string (edit_string); + break; + + case Frames: + pos = frames_from_audioframes_string (edit_string); + break; + } + + set (pos, true); + _layout->set_attributes (normal_attributes); + ValueChanged(); /* EMIT_SIGNAL */ } - break; - case GDK_Tab: - case GDK_Return: - case GDK_KP_Enter: - move_on = true; + } else { + + editing = false; + _layout->set_attributes (normal_attributes); + _layout->set_text (pre_edit_string); + } + + queue_draw (); + + if (!editing) { + drop_focus (); + } +} + +void +AudioClock::drop_focus () +{ + Keyboard::magic_widget_drop_focus (); + + if (has_focus()) { + + /* move focus back to the default widget in the top level window */ + + Widget* top = get_toplevel(); + + if (top->is_toplevel ()) { + Window* win = dynamic_cast (top); + win->grab_focus (); + } + } +} + +framecnt_t +AudioClock::parse_as_frames_distance (const std::string& str) +{ + framecnt_t f; + + if (sscanf (str.c_str(), "%" PRId64, &f) == 1) { + return f; + } + + return 0; +} + +framecnt_t +AudioClock::parse_as_minsec_distance (const std::string& str) +{ + framecnt_t sr = _session->frame_rate(); + int msecs; + int secs; + int mins; + int hrs; + + switch (str.length()) { + case 0: + return 0; + case 1: + case 2: + case 3: + case 4: + sscanf (str.c_str(), "%" PRId32, &msecs); + return msecs * (sr / 1000); + + case 5: + sscanf (str.c_str(), "%1" PRId32 "%" PRId32, &secs, &msecs); + return (secs * sr) + (msecs * (sr/1000)); + + case 6: + sscanf (str.c_str(), "%2" PRId32 "%" PRId32, &secs, &msecs); + return (secs * sr) + (msecs * (sr/1000)); + + case 7: + sscanf (str.c_str(), "%1" PRId32 "%2" PRId32 "%" PRId32, &mins, &secs, &msecs); + return (mins * 60 * sr) + (secs * sr) + (msecs * (sr/1000)); + + case 8: + sscanf (str.c_str(), "%2" PRId32 "%2" PRId32 "%" PRId32, &mins, &secs, &msecs); + return (mins * 60 * sr) + (secs * sr) + (msecs * (sr/1000)); + + case 9: + sscanf (str.c_str(), "%1" PRId32 "%2" PRId32 "%2" PRId32 "%" PRId32, &hrs, &mins, &secs, &msecs); + return (hrs * 3600 * sr) + (mins * 60 * sr) + (secs * sr) + (msecs * (sr/1000)); + + case 10: + sscanf (str.c_str(), "%1" PRId32 "%2" PRId32 "%2" PRId32 "%" PRId32, &hrs, &mins, &secs, &msecs); + return (hrs * 3600 * sr) + (mins * 60 * sr) + (secs * sr) + (msecs * (sr/1000)); + + default: break; + } - case GDK_Escape: - key_entry_state = 0; - clock_base.grab_focus (); - ChangeAborted(); /* EMIT SIGNAL */ - return true; + return 0; +} + +framecnt_t +AudioClock::parse_as_timecode_distance (const std::string& str) +{ + double fps = _session->timecode_frames_per_second(); + framecnt_t sr = _session->frame_rate(); + int frames; + int secs; + int mins; + int hrs; + + switch (str.length()) { + case 0: + return 0; + case 1: + case 2: + sscanf (str.c_str(), "%" PRId32, &frames); + return lrint ((frames/(float)fps) * sr); + case 3: + sscanf (str.c_str(), "%1" PRId32 "%" PRId32, &secs, &frames); + return (secs * sr) + lrint ((frames/(float)fps) * sr); + + case 4: + sscanf (str.c_str(), "%2" PRId32 "%" PRId32, &secs, &frames); + return (secs * sr) + lrint ((frames/(float)fps) * sr); + + case 5: + sscanf (str.c_str(), "%1" PRId32 "%2" PRId32 "%" PRId32, &mins, &secs, &frames); + return (mins * 60 * sr) + (secs * sr) + lrint ((frames/(float)fps) * sr); + + case 6: + sscanf (str.c_str(), "%2" PRId32 "%2" PRId32 "%" PRId32, &mins, &secs, &frames); + return (mins * 60 * sr) + (secs * sr) + lrint ((frames/(float)fps) * sr); + + case 7: + sscanf (str.c_str(), "%1" PRId32 "%2" PRId32 "%2" PRId32 "%" PRId32, &hrs, &mins, &secs, &frames); + return (hrs * 3600 * sr) + (mins * 60 * sr) + (secs * sr) + lrint ((frames/(float)fps) * sr); + + case 8: + sscanf (str.c_str(), "%2" PRId32 "%2" PRId32 "%2" PRId32 "%" PRId32, &hrs, &mins, &secs, &frames); + return (hrs * 3600 * sr) + (mins * 60 * sr) + (secs * sr) + lrint ((frames/(float)fps) * sr); + default: - return false; + break; + } + + return 0; +} + +framecnt_t +AudioClock::parse_as_bbt_distance (const std::string&) +{ + return 0; +} + +framecnt_t +AudioClock::parse_as_distance (const std::string& instr) +{ + switch (_mode) { + case Timecode: + return parse_as_timecode_distance (instr); + break; + case Frames: + return parse_as_frames_distance (instr); + break; + case BBT: + return parse_as_bbt_distance (instr); + break; + case MinSec: + return parse_as_minsec_distance (instr); + break; } + return 0; +} - if (!move_on) { +void +AudioClock::end_edit_relative (bool add) +{ + bool ok = true; + + switch (_mode) { + case Timecode: + ok = timecode_validate_edit (edit_string); + break; + + case BBT: + ok = bbt_validate_edit (edit_string); + break; + + case MinSec: + ok = minsec_validate_edit (edit_string); + break; + + case Frames: + break; + } + + if (!ok) { + edit_string = pre_edit_string; + input_string.clear (); + _layout->set_text (edit_string); + show_edit_status (0); + /* edit attributes remain in use */ + queue_draw (); + return; + } - if (key_entry_state == 0) { + framecnt_t frames = parse_as_distance (input_string); - /* initialize with a fresh new string */ + editing = false; - if (field != AudioFrames) { - for (uint32_t xn = 0; xn < field_length[field] - 1; ++xn) { - new_text += '0'; - } - } else { - new_text = ""; - } + editing = false; + _layout->set_attributes (normal_attributes); + if (frames != 0) { + if (add) { + set (current_time() + frames, true); } else { + framepos_t c = current_time(); - string existing = label->get_text(); - if (existing.length() >= field_length[field]) { - new_text = existing.substr (1, field_length[field] - 1); + if (c > frames) { + set (c - frames, true); } else { - new_text = existing.substr (0, field_length[field] - 1); + set (0, true); } } - - new_text += new_char; - label->set_text (new_text); - key_entry_state++; + ValueChanged (); /* EMIT SIGNAL */ } - if (key_entry_state == field_length[field]) { - move_on = true; - } + input_string.clear (); + queue_draw (); + drop_focus (); +} - if (move_on) { +void +AudioClock::session_configuration_changed (std::string p) +{ + if (p == "sync-source" || p == "external-sync") { + set (current_time(), true); + return; + } - if (key_entry_state) { + if (p != "timecode-offset" && p != "timecode-offset-negative") { + return; + } - switch (field) { - case Timecode_Hours: - case Timecode_Minutes: - case Timecode_Seconds: - case Timecode_Frames: - // Check Timecode fields for sanity (may also adjust fields) - timecode_sanitize_display(); - break; - case Bars: - case Beats: - case Ticks: - // Bars should never be, unless this clock is for a duration - if (atoi(bars_label.get_text()) == 0 && !is_duration) { - bars_label.set_text("001"); - } - // beats should never be 0, unless this clock is for a duration - if (atoi(beats_label.get_text()) == 0 && !is_duration) { - beats_label.set_text("01"); - } - break; - default: - break; - } + framecnt_t current; - ValueChanged(); /* EMIT_SIGNAL */ + switch (_mode) { + case Timecode: + if (is_duration) { + current = current_duration (); + } else { + current = current_time (); } + set (current, true); + break; + default: + break; + } +} - /* move on to the next field. - */ +void +AudioClock::set (framepos_t when, bool force, framecnt_t offset) +{ + if ((!force && !is_visible()) || _session == 0) { + return; + } + + if (is_duration) { + when = when - offset; + } - switch (field) { + if (when == last_when && !force) { + return; + } - /* Timecode */ + if (!editing) { - case Timecode_Hours: - minutes_ebox.grab_focus (); + switch (_mode) { + case Timecode: + set_timecode (when, force); break; - case Timecode_Minutes: - seconds_ebox.grab_focus (); + + case BBT: + set_bbt (when, force); break; - case Timecode_Seconds: - frames_ebox.grab_focus (); + + case MinSec: + set_minsec (when, force); break; - case Timecode_Frames: - clock_base.grab_focus (); + + case Frames: + set_frames (when, force); break; + } + } - /* audio frames */ - case AudioFrames: - clock_base.grab_focus (); - break; + queue_draw (); + last_when = when; +} - /* Min:Sec */ +void +AudioClock::set_frames (framepos_t when, bool /*force*/) +{ + char buf[32]; + bool negative = false; - case MS_Hours: - ms_minutes_ebox.grab_focus (); - break; - case MS_Minutes: - ms_seconds_ebox.grab_focus (); - break; - case MS_Seconds: - clock_base.grab_focus (); - break; + if (_off) { + _layout->set_text ("\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012"); - /* BBT */ + if (_left_layout) { + _left_layout->set_text (""); + _right_layout->set_text (""); + } + + return; + } + + if (when < 0) { + when = -when; + negative = true; + } - case Bars: - beats_ebox.grab_focus (); - break; - case Beats: - ticks_ebox.grab_focus (); - break; - case Ticks: - clock_base.grab_focus (); - break; + if (negative) { + snprintf (buf, sizeof (buf), "-%10" PRId64, when); + } else { + snprintf (buf, sizeof (buf), " %10" PRId64, when); + } - default: - break; + _layout->set_text (buf); + + if (_left_layout) { + framecnt_t rate = _session->frame_rate(); + + if (fmod (rate, 100.0) == 0.0) { + sprintf (buf, "SR %.1fkHz", rate/1000.0); + } else { + sprintf (buf, "SR %" PRId64, rate); } + _left_layout->set_text (buf); + + float vid_pullup = _session->config.get_video_pullup(); + + if (vid_pullup == 0.0) { + _right_layout->set_text (_("pullup: \u2012")); + } else { + sprintf (buf, _("pullup %-6.4f"), vid_pullup); + _right_layout->set_text (buf); + } } +} - //if user hit Enter, lose focus - switch (ev->keyval) { - case GDK_Return: - case GDK_KP_Enter: - clock_base.grab_focus (); +void +AudioClock::set_minsec (framepos_t when, bool /*force*/) +{ + char buf[32]; + framecnt_t left; + int hrs; + int mins; + int secs; + int millisecs; + bool negative = false; + + if (_off) { + _layout->set_text ("\u2012\u2012:\u2012\u2012:\u2012\u2012.\u2012\u2012\u2012"); + + if (_left_layout) { + _left_layout->set_text (""); + _right_layout->set_text (""); + } + + return; + } + + if (when < 0) { + when = -when; + negative = true; } - return true; + left = when; + hrs = (int) floor (left / (_session->frame_rate() * 60.0f * 60.0f)); + left -= (framecnt_t) floor (hrs * _session->frame_rate() * 60.0f * 60.0f); + mins = (int) floor (left / (_session->frame_rate() * 60.0f)); + left -= (framecnt_t) floor (mins * _session->frame_rate() * 60.0f); + secs = (int) floor (left / (float) _session->frame_rate()); + left -= (framecnt_t) floor (secs * _session->frame_rate()); + millisecs = floor (left * 1000.0 / (float) _session->frame_rate()); + + if (negative) { + snprintf (buf, sizeof (buf), "-%02" PRId32 ":%02" PRId32 ":%02" PRId32 ".%03" PRId32, hrs, mins, secs, millisecs); + } else { + snprintf (buf, sizeof (buf), " %02" PRId32 ":%02" PRId32 ":%02" PRId32 ".%03" PRId32, hrs, mins, secs, millisecs); + } + + _layout->set_text (buf); } -bool -AudioClock::field_focus_in_event (GdkEventFocus */*ev*/, Field field) +void +AudioClock::set_timecode (framepos_t when, bool /*force*/) { - key_entry_state = 0; + char buf[32]; + Timecode::Time TC; + bool negative = false; + + if (_off) { + _layout->set_text ("\u2012\u2012:\u2012\u2012:\u2012\u2012:\u2012\u2012"); + if (_left_layout) { + _left_layout->set_text (""); + _right_layout->set_text (""); + } + + return; + } - Keyboard::magic_widget_grab_focus (); + if (when < 0) { + when = -when; + negative = true; + } - switch (field) { - case Timecode_Hours: - hours_ebox.set_flags (Gtk::HAS_FOCUS); - hours_ebox.set_state (Gtk::STATE_ACTIVE); - break; - case Timecode_Minutes: - minutes_ebox.set_flags (Gtk::HAS_FOCUS); - minutes_ebox.set_state (Gtk::STATE_ACTIVE); - break; - case Timecode_Seconds: - seconds_ebox.set_flags (Gtk::HAS_FOCUS); - seconds_ebox.set_state (Gtk::STATE_ACTIVE); - break; - case Timecode_Frames: - frames_ebox.set_flags (Gtk::HAS_FOCUS); - frames_ebox.set_state (Gtk::STATE_ACTIVE); - break; + if (is_duration) { + _session->timecode_duration (when, TC); + } else { + _session->timecode_time (when, TC); + } + + if (TC.negative || negative) { + snprintf (buf, sizeof (buf), "-%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32, TC.hours, TC.minutes, TC.seconds, TC.frames); + } else { + snprintf (buf, sizeof (buf), " %02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32, TC.hours, TC.minutes, TC.seconds, TC.frames); + } - case AudioFrames: - audio_frames_ebox.set_flags (Gtk::HAS_FOCUS); - audio_frames_ebox.set_state (Gtk::STATE_ACTIVE); - break; + _layout->set_text (buf); - case MS_Hours: - ms_hours_ebox.set_flags (Gtk::HAS_FOCUS); - ms_hours_ebox.set_state (Gtk::STATE_ACTIVE); - break; - case MS_Minutes: - ms_minutes_ebox.set_flags (Gtk::HAS_FOCUS); - ms_minutes_ebox.set_state (Gtk::STATE_ACTIVE); - break; - case MS_Seconds: - ms_seconds_ebox.set_flags (Gtk::HAS_FOCUS); - ms_seconds_ebox.set_state (Gtk::STATE_ACTIVE); - break; - case Bars: - bars_ebox.set_flags (Gtk::HAS_FOCUS); - bars_ebox.set_state (Gtk::STATE_ACTIVE); - break; - case Beats: - beats_ebox.set_flags (Gtk::HAS_FOCUS); - beats_ebox.set_state (Gtk::STATE_ACTIVE); - break; - case Ticks: - ticks_ebox.set_flags (Gtk::HAS_FOCUS); - ticks_ebox.set_state (Gtk::STATE_ACTIVE); - break; + if (_left_layout) { + + if (_session->config.get_external_sync()) { + switch (_session->config.get_sync_source()) { + case JACK: + _left_layout->set_text ("JACK"); + break; + case MTC: + _left_layout->set_text ("MTC"); + break; + case MIDIClock: + _left_layout->set_text ("M-Clock"); + break; + } + } else { + _left_layout->set_text ("INT"); + } + + double timecode_frames = _session->timecode_frames_per_second(); + + if (fmod(timecode_frames, 1.0) == 0.0) { + sprintf (buf, "FPS %u %s", int (timecode_frames), (_session->timecode_drop_frames() ? "D" : "")); + } else { + sprintf (buf, "%.2f %s", timecode_frames, (_session->timecode_drop_frames() ? "D" : "")); + } + + _right_layout->set_text (buf); } +} - return false; +void +AudioClock::set_bbt (framepos_t when, bool /*force*/) +{ + char buf[16]; + Timecode::BBT_Time BBT; + bool negative = false; + + if (_off) { + _layout->set_text ("\u2012\u2012\u2012|\u2012\u2012|\u2012\u2012\u2012\u2012"); + if (_left_layout) { + _left_layout->set_text (""); + _right_layout->set_text (""); + } + return; + } + + if (when < 0) { + when = -when; + negative = true; + } + + /* handle a common case */ + if (is_duration) { + if (when == 0) { + BBT.bars = 0; + BBT.beats = 0; + BBT.ticks = 0; + } else { + _session->tempo_map().bbt_time (when, BBT); + BBT.bars--; + BBT.beats--; + } + } else { + _session->tempo_map().bbt_time (when, BBT); + } + + if (negative) { + snprintf (buf, sizeof (buf), "-%03" PRIu32 BBT_BAR_CHAR "%02" PRIu32 BBT_BAR_CHAR "%04" PRIu32, + BBT.bars, BBT.beats, BBT.ticks); + } else { + snprintf (buf, sizeof (buf), " %03" PRIu32 BBT_BAR_CHAR "%02" PRIu32 BBT_BAR_CHAR "%04" PRIu32, + BBT.bars, BBT.beats, BBT.ticks); + } + + _layout->set_text (buf); + + if (_right_layout) { + framepos_t pos; + + if (bbt_reference_time < 0) { + pos = when; + } else { + pos = bbt_reference_time; + } + + TempoMetric m (_session->tempo_map().metric_at (pos)); + + sprintf (buf, "%-5.2f", m.tempo().beats_per_minute()); + _left_layout->set_text (buf); + + sprintf (buf, "%g/%g", m.meter().divisions_per_bar(), m.meter().note_divisor()); + _right_layout->set_text (buf); + } +} + +void +AudioClock::set_session (Session *s) +{ + SessionHandlePtr::set_session (s); + + if (_session) { + + _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&AudioClock::session_configuration_changed, this, _1), gui_context()); + + const XMLProperty* prop; + XMLNode* node = _session->extra_xml (X_("ClockModes")); + AudioClock::Mode amode; + + if (node) { + for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) { + if ((prop = (*i)->property (X_("name"))) && prop->value() == _name) { + + if ((prop = (*i)->property (X_("mode"))) != 0) { + amode = AudioClock::Mode (string_2_enum (prop->value(), amode)); + set_mode (amode); + } + if ((prop = (*i)->property (X_("on"))) != 0) { + set_off (!string_is_affirmative (prop->value())); + } + break; + } + } + } + + set (last_when, true); + } } bool -AudioClock::field_focus_out_event (GdkEventFocus */*ev*/, Field field) +AudioClock::on_key_press_event (GdkEventKey* ev) { - switch (field) { + if (!editing) { + return false; + } - case Timecode_Hours: - hours_ebox.unset_flags (Gtk::HAS_FOCUS); - hours_ebox.set_state (Gtk::STATE_NORMAL); + string new_text; + char new_char = 0; + int highlight_length; + framepos_t pos; + + switch (ev->keyval) { + case GDK_0: + case GDK_KP_0: + new_char = '0'; break; - case Timecode_Minutes: - minutes_ebox.unset_flags (Gtk::HAS_FOCUS); - minutes_ebox.set_state (Gtk::STATE_NORMAL); + case GDK_1: + case GDK_KP_1: + new_char = '1'; break; - case Timecode_Seconds: - seconds_ebox.unset_flags (Gtk::HAS_FOCUS); - seconds_ebox.set_state (Gtk::STATE_NORMAL); + case GDK_2: + case GDK_KP_2: + new_char = '2'; break; - case Timecode_Frames: - frames_ebox.unset_flags (Gtk::HAS_FOCUS); - frames_ebox.set_state (Gtk::STATE_NORMAL); + case GDK_3: + case GDK_KP_3: + new_char = '3'; break; - - case AudioFrames: - audio_frames_ebox.unset_flags (Gtk::HAS_FOCUS); - audio_frames_ebox.set_state (Gtk::STATE_NORMAL); + case GDK_4: + case GDK_KP_4: + new_char = '4'; break; - - case MS_Hours: - ms_hours_ebox.unset_flags (Gtk::HAS_FOCUS); - ms_hours_ebox.set_state (Gtk::STATE_NORMAL); + case GDK_5: + case GDK_KP_5: + new_char = '5'; break; - case MS_Minutes: - ms_minutes_ebox.unset_flags (Gtk::HAS_FOCUS); - ms_minutes_ebox.set_state (Gtk::STATE_NORMAL); + case GDK_6: + case GDK_KP_6: + new_char = '6'; break; - case MS_Seconds: - ms_seconds_ebox.unset_flags (Gtk::HAS_FOCUS); - ms_seconds_ebox.set_state (Gtk::STATE_NORMAL); + case GDK_7: + case GDK_KP_7: + new_char = '7'; + break; + case GDK_8: + case GDK_KP_8: + new_char = '8'; + break; + case GDK_9: + case GDK_KP_9: + new_char = '9'; break; - case Bars: - bars_ebox.unset_flags (Gtk::HAS_FOCUS); - bars_ebox.set_state (Gtk::STATE_NORMAL); + case GDK_minus: + case GDK_KP_Subtract: + end_edit_relative (false); + return true; break; - case Beats: - beats_ebox.unset_flags (Gtk::HAS_FOCUS); - beats_ebox.set_state (Gtk::STATE_NORMAL); + + case GDK_plus: + case GDK_KP_Add: + end_edit_relative (true); + return true; break; - case Ticks: - ticks_ebox.unset_flags (Gtk::HAS_FOCUS); - ticks_ebox.set_state (Gtk::STATE_NORMAL); + + case GDK_Tab: + case GDK_Return: + case GDK_KP_Enter: + end_edit (true); + return true; break; + + case GDK_Escape: + end_edit (false); + ChangeAborted(); /* EMIT SIGNAL */ + return true; + + case GDK_Delete: + case GDK_BackSpace: + if (!input_string.empty()) { + /* delete the last key entered + */ + input_string = input_string.substr (0, input_string.length() - 1); + } + goto use_input_string; + + default: + return false; } - Keyboard::magic_widget_drop_focus (); + if (!insert_map.empty() && (input_string.length() >= insert_map.size())) { + /* too many digits: eat the key event, but do nothing with it */ + return true; + } - return false; + input_string.push_back (new_char); + + use_input_string: + + switch (_mode) { + case Frames: + /* get this one in the right order, and to the right width */ + if (ev->keyval == GDK_Delete || ev->keyval == GDK_BackSpace) { + edit_string = edit_string.substr (0, edit_string.length() - 1); + } else { + edit_string.push_back (new_char); + } + if (!edit_string.empty()) { + char buf[32]; + sscanf (edit_string.c_str(), "%" PRId64, &pos); + snprintf (buf, sizeof (buf), " %10" PRId64, pos); + edit_string = buf; + } + /* highlight the whole thing */ + highlight_length = edit_string.length(); + break; + + default: + highlight_length = merge_input_and_edit_string (); + } + + show_edit_status (highlight_length); + _layout->set_text (edit_string); + queue_draw (); + + return true; } -bool -AudioClock::field_button_release_event (GdkEventButton *ev, Field field) +int +AudioClock::merge_input_and_edit_string () { - if (dragging) { - cerr << "button event on clock but we are dragging\n"; - gdk_pointer_ungrab (GDK_CURRENT_TIME); - dragging = false; - if (ev->y > drag_start_y+1 || ev->y < drag_start_y-1 || Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)){ - // we actually dragged so return without setting editing focus, or we shift clicked - return true; - } + /* merge with pre-edit-string into edit string */ + + edit_string = pre_edit_string; + + if (input_string.empty()) { + return 0; + } + + string::size_type target; + for (string::size_type i = 0; i < input_string.length(); ++i) { + target = insert_map[input_string.length() - 1 - i]; + edit_string[target] = input_string[i]; } + /* highlight from end to wherever the last character was added */ + return edit_string.length() - insert_map[input_string.length()-1]; +} - if (!editable) { - if (ops_menu == 0) { - build_ops_menu (); - } - ops_menu->popup (1, ev->time); + +bool +AudioClock::on_key_release_event (GdkEventKey *ev) +{ + if (!editing) { + return false; + } + + /* return true for keys that we used on press + so that they cannot possibly do double-duty + */ + switch (ev->keyval) { + case GDK_0: + case GDK_KP_0: + case GDK_1: + case GDK_KP_1: + case GDK_2: + case GDK_KP_2: + case GDK_3: + case GDK_KP_3: + case GDK_4: + case GDK_KP_4: + case GDK_5: + case GDK_KP_5: + case GDK_6: + case GDK_KP_6: + case GDK_7: + case GDK_KP_7: + case GDK_8: + case GDK_KP_8: + case GDK_9: + case GDK_KP_9: + case GDK_period: + case GDK_comma: + case GDK_KP_Decimal: + case GDK_Tab: + case GDK_Return: + case GDK_KP_Enter: + case GDK_Escape: + case GDK_minus: + case GDK_plus: + case GDK_KP_Add: + case GDK_KP_Subtract: return true; + default: + return false; } +} - if (Keyboard::is_context_menu_event (ev)) { - cerr << "Context menu event on clock\n"; - if (ops_menu == 0) { - build_ops_menu (); +AudioClock::Field +AudioClock::index_to_field (int index) const +{ + switch (_mode) { + case Timecode: + if (index < 4) { + return Timecode_Hours; + } else if (index < 7) { + return Timecode_Minutes; + } else if (index < 10) { + return Timecode_Seconds; + } else { + return Timecode_Frames; } - ops_menu->popup (1, ev->time); - return true; + break; + case BBT: + if (index < 5) { + return Bars; + } else if (index < 7) { + return Beats; + } else { + return Ticks; + } + break; + case MinSec: + if (index < 3) { + return Timecode_Hours; + } else if (index < 6) { + return MS_Minutes; + } else if (index < 9) { + return MS_Seconds; + } else { + return MS_Milliseconds; + } + break; + case Frames: + return AudioFrames; + break; } - switch (ev->button) { - case 1: - switch (field) { - case Timecode_Hours: - hours_ebox.grab_focus(); - break; - case Timecode_Minutes: - minutes_ebox.grab_focus(); - break; - case Timecode_Seconds: - seconds_ebox.grab_focus(); - break; - case Timecode_Frames: - frames_ebox.grab_focus(); - break; - - case AudioFrames: - audio_frames_ebox.grab_focus(); - break; - - case MS_Hours: - ms_hours_ebox.grab_focus(); - break; - case MS_Minutes: - ms_minutes_ebox.grab_focus(); - break; - case MS_Seconds: - ms_seconds_ebox.grab_focus(); - break; + return Field (0); +} - case Bars: - bars_ebox.grab_focus (); - break; - case Beats: - beats_ebox.grab_focus (); - break; - case Ticks: - ticks_ebox.grab_focus (); - break; +bool +AudioClock::on_button_press_event (GdkEventButton *ev) +{ + switch (ev->button) { + case 1: + if (editable && !_off) { + int index; + int trailing; + int y; + int x; + + /* the text has been centered vertically, so adjust + * x and y. + */ + + y = ev->y - ((upper_height - layout_height)/2); + x = ev->x - layout_x_offset; + + if (_layout->xy_to_index (x * PANGO_SCALE, y * PANGO_SCALE, index, trailing)) { + drag_field = index_to_field (index); + dragging = true; + /* make absolutely sure that the pointer is grabbed */ + gdk_pointer_grab(ev->window,false , + GdkEventMask( Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK |Gdk::BUTTON_RELEASE_MASK), + NULL,NULL,ev->time); + drag_accum = 0; + drag_start_y = ev->y; + drag_y = ev->y; + } } break; - + default: + return false; break; } @@ -1188,108 +1490,149 @@ AudioClock::field_button_release_event (GdkEventButton *ev, Field field) } bool -AudioClock::field_button_press_event (GdkEventButton *ev, Field /*field*/) +AudioClock::on_button_release_event (GdkEventButton *ev) { - if (session == 0) { - return false; - } - - nframes_t frames = 0; - - switch (ev->button) { - case 1: - if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { - set (frames, true); - ValueChanged (); /* EMIT_SIGNAL */ + if (editable && !_off) { + if (dragging) { + gdk_pointer_ungrab (GDK_CURRENT_TIME); + dragging = false; + if (ev->y > drag_start_y+1 || ev->y < drag_start_y-1 || Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)){ + // we actually dragged so return without + // setting editing focus, or we shift clicked + return true; + } else { + if (ev->button == 1) { + + if (_edit_by_click_field) { + + int index = 0; + int trailing; + int y = ev->y - ((upper_height - layout_height)/2); + int x = ev->x - layout_x_offset; + Field f; + + if (!_layout->xy_to_index (x * PANGO_SCALE, y * PANGO_SCALE, index, trailing)) { + return true; + } + + f = index_to_field (index); + + switch (f) { + case Timecode_Frames: + case MS_Milliseconds: + case Ticks: + f = Field (0); + break; + default: + break; + } + start_edit (f); + } else { + start_edit (); } + } + } + } + } - /* make absolutely sure that the pointer is grabbed */ - gdk_pointer_grab(ev->window,false , - GdkEventMask( Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK |Gdk::BUTTON_RELEASE_MASK), - NULL,NULL,ev->time); - dragging = true; - drag_accum = 0; - drag_start_y = ev->y; - drag_y = ev->y; - break; - - case 2: - if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { - set (frames, true); - ValueChanged (); /* EMIT_SIGNAL */ + if (Keyboard::is_context_menu_event (ev)) { + if (ops_menu == 0) { + build_ops_menu (); } - break; + ops_menu->popup (1, ev->time); + return true; + } - case 3: - /* used for context sensitive menu */ - return false; - break; + return false; +} - default: - return false; - break; +bool +AudioClock::on_focus_out_event (GdkEventFocus* ev) +{ + bool ret = CairoWidget::on_focus_out_event (ev); + + if (editing) { + end_edit (false); } - return true; + return ret; } bool -AudioClock::field_button_scroll_event (GdkEventScroll *ev, Field field) +AudioClock::on_scroll_event (GdkEventScroll *ev) { - if (session == 0) { + int index; + int trailing; + + if (editing || _session == 0 || !editable || _off) { return false; } - nframes_t frames = 0; + int y; + int x; + + /* the text has been centered vertically, so adjust + * x and y. + */ + + y = ev->y - ((upper_height - layout_height)/2); + x = ev->x - layout_x_offset; + + if (!_layout->xy_to_index (x * PANGO_SCALE, y * PANGO_SCALE, index, trailing)) { + /* not in the main layout */ + return false; + } + + Field f = index_to_field (index); + framepos_t frames = 0; switch (ev->direction) { case GDK_SCROLL_UP: - frames = get_frames (field); - if (frames != 0) { - if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { - frames *= 10; - } - set (current_time() + frames, true); - ValueChanged (); /* EMIT_SIGNAL */ - } - break; - + frames = get_frame_step (f); + if (frames != 0) { + if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { + frames *= 10; + } + set (current_time() + frames, true); + ValueChanged (); /* EMIT_SIGNAL */ + } + break; + case GDK_SCROLL_DOWN: - frames = get_frames (field); - if (frames != 0) { - if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { - frames *= 10; - } - - if ((double)current_time() - (double)frames < 0.0) { - set (0, true); - } else { - set (current_time() - frames, true); - } - - ValueChanged (); /* EMIT_SIGNAL */ - } - break; - + frames = get_frame_step (f); + if (frames != 0) { + if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { + frames *= 10; + } + + if ((double)current_time() - (double)frames < 0.0) { + set (0, true); + } else { + set (current_time() - frames, true); + } + + ValueChanged (); /* EMIT_SIGNAL */ + } + break; + default: return false; break; } - + return true; } bool -AudioClock::field_motion_notify_event (GdkEventMotion *ev, Field field) +AudioClock::on_motion_notify_event (GdkEventMotion *ev) { - if (session == 0 || !dragging) { + if (editing || _session == 0 || !dragging) { return false; } float pixel_frame_scale_factor = 0.2f; -/* if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { pixel_frame_scale_factor = 0.1f; } @@ -1300,605 +1643,243 @@ AudioClock::field_motion_notify_event (GdkEventMotion *ev, Field field) pixel_frame_scale_factor = 0.025f; } -*/ + double y_delta = ev->y - drag_y; drag_accum += y_delta*pixel_frame_scale_factor; drag_y = ev->y; - if (trunc(drag_accum) != 0) { + if (trunc (drag_accum) != 0) { - nframes_t frames; - nframes_t pos ; + framepos_t frames; + framepos_t pos; int dir; dir = (drag_accum < 0 ? 1:-1); pos = current_time(); - frames = get_frames (field,pos,dir); + frames = get_frame_step (drag_field, pos, dir); if (frames != 0 && frames * drag_accum < current_time()) { - - set ((nframes_t) floor (pos - drag_accum * frames), false); // minus because up is negative in computer-land - + set ((framepos_t) floor (pos - drag_accum * frames), false); // minus because up is negative in GTK } else { set (0 , false); - } drag_accum= 0; ValueChanged(); /* EMIT_SIGNAL */ - - } return true; } -nframes_t -AudioClock::get_frames (Field field,nframes_t pos,int dir) +framepos_t +AudioClock::get_frame_step (Field field, framepos_t pos, int dir) { - - nframes_t frames = 0; - BBT_Time bbt; + framecnt_t f = 0; + Timecode::BBT_Time BBT; switch (field) { case Timecode_Hours: - frames = (nframes_t) floor (3600.0 * session->frame_rate()); + f = (framecnt_t) floor (3600.0 * _session->frame_rate()); break; case Timecode_Minutes: - frames = (nframes_t) floor (60.0 * session->frame_rate()); + f = (framecnt_t) floor (60.0 * _session->frame_rate()); break; case Timecode_Seconds: - frames = session->frame_rate(); + f = _session->frame_rate(); break; case Timecode_Frames: - frames = (nframes_t) floor (session->frame_rate() / session->timecode_frames_per_second()); + f = (framecnt_t) floor (_session->frame_rate() / _session->timecode_frames_per_second()); break; case AudioFrames: - frames = 1; + f = 1; break; case MS_Hours: - frames = (nframes_t) floor (3600.0 * session->frame_rate()); + f = (framecnt_t) floor (3600.0 * _session->frame_rate()); break; case MS_Minutes: - frames = (nframes_t) floor (60.0 * session->frame_rate()); + f = (framecnt_t) floor (60.0 * _session->frame_rate()); break; case MS_Seconds: - frames = session->frame_rate(); + f = (framecnt_t) _session->frame_rate(); + break; + case MS_Milliseconds: + f = (framecnt_t) floor (_session->frame_rate() / 1000.0); break; case Bars: - bbt.bars = 1; - bbt.beats = 0; - bbt.ticks = 0; - frames = session->tempo_map().bbt_duration_at(pos,bbt,dir); + BBT.bars = 1; + BBT.beats = 0; + BBT.ticks = 0; + f = _session->tempo_map().bbt_duration_at (pos,BBT,dir); break; case Beats: - bbt.bars = 0; - bbt.beats = 1; - bbt.ticks = 0; - frames = session->tempo_map().bbt_duration_at(pos,bbt,dir); + BBT.bars = 0; + BBT.beats = 1; + BBT.ticks = 0; + f = _session->tempo_map().bbt_duration_at(pos,BBT,dir); break; case Ticks: - bbt.bars = 0; - bbt.beats = 0; - bbt.ticks = 1; - frames = session->tempo_map().bbt_duration_at(pos,bbt,dir); + BBT.bars = 0; + BBT.beats = 0; + BBT.ticks = 1; + f = _session->tempo_map().bbt_duration_at(pos,BBT,dir); + break; + default: + error << string_compose (_("programming error: %1"), "attempt to get frames from non-text field!") << endmsg; + f = 0; break; } - return frames; + return f; } -nframes_t -AudioClock::current_time (nframes_t pos) const +framepos_t +AudioClock::current_time (framepos_t) const { - nframes_t ret = 0; - - switch (_mode) { - case Timecode: - ret = timecode_frame_from_display (); - break; - case BBT: - ret = bbt_frame_from_display (pos); - break; - - case MinSec: - ret = minsec_frame_from_display (); - break; - - case Frames: - ret = audio_frame_from_display (); - break; - - case Off: - break; - } - - return ret; + return last_when; } -nframes_t -AudioClock::current_duration (nframes_t pos) const +framepos_t +AudioClock::current_duration (framepos_t pos) const { - nframes_t ret = 0; + framepos_t ret = 0; switch (_mode) { case Timecode: - ret = timecode_frame_from_display (); + ret = last_when; break; case BBT: - ret = bbt_frame_duration_from_display (pos); + ret = frame_duration_from_bbt_string (pos, _layout->get_text()); break; case MinSec: - ret = minsec_frame_from_display (); + ret = last_when; break; case Frames: - ret = audio_frame_from_display (); - break; - - case Off: + ret = last_when; break; } return ret; } -void -AudioClock::timecode_sanitize_display() +bool +AudioClock::bbt_validate_edit (const string& str) { - // Check Timecode fields for sanity, possibly adjusting values - if (atoi(minutes_label.get_text()) > 59) { - minutes_label.set_text("59"); + AnyTime any; + + if (sscanf (str.c_str(), BBT_SCANF_FORMAT, &any.bbt.bars, &any.bbt.beats, &any.bbt.ticks) != 3) { + return false; } - if (atoi(seconds_label.get_text()) > 59) { - seconds_label.set_text("59"); + if (any.bbt.ticks > Timecode::BBT_Time::ticks_per_beat) { + return false; } - switch ((long)rint(session->timecode_frames_per_second())) { - case 24: - if (atoi(frames_label.get_text()) > 23) { - frames_label.set_text("23"); - } - break; - case 25: - if (atoi(frames_label.get_text()) > 24) { - frames_label.set_text("24"); - } - break; - case 30: - if (atoi(frames_label.get_text()) > 29) { - frames_label.set_text("29"); - } - break; - default: - break; + if (!is_duration && any.bbt.bars == 0) { + return false; } - if (session->timecode_drop_frames()) { - if ((atoi(minutes_label.get_text()) % 10) && (atoi(seconds_label.get_text()) == 0) && (atoi(frames_label.get_text()) < 2)) { - frames_label.set_text("02"); - } + if (!is_duration && any.bbt.beats == 0) { + return false; } + + return true; } -nframes_t -AudioClock::timecode_frame_from_display () const +bool +AudioClock::timecode_validate_edit (const string&) { - if (session == 0) { - return 0; - } + Timecode::Time TC; - Timecode::Time timecode; - nframes_t sample; - - timecode.hours = atoi (hours_label.get_text()); - timecode.minutes = atoi (minutes_label.get_text()); - timecode.seconds = atoi (seconds_label.get_text()); - timecode.frames = atoi (frames_label.get_text()); - timecode.rate = session->timecode_frames_per_second(); - timecode.drop= session->timecode_drop_frames(); - - session->timecode_to_sample( timecode, sample, false /* use_offset */, false /* use_subframes */ ); - - -#if 0 -#define Timecode_SAMPLE_TEST_1 -#define Timecode_SAMPLE_TEST_2 -#define Timecode_SAMPLE_TEST_3 -#define Timecode_SAMPLE_TEST_4 -#define Timecode_SAMPLE_TEST_5 -#define Timecode_SAMPLE_TEST_6 -#define Timecode_SAMPLE_TEST_7 - - // Testcode for timecode<->sample conversions (P.S.) - Timecode::Time timecode1; - nframes_t sample1; - nframes_t oldsample = 0; - Timecode::Time timecode2; - nframes_t sample_increment; - - sample_increment = (long)rint(session->frame_rate() / session->timecode_frames_per_second); - -#ifdef Timecode_SAMPLE_TEST_1 - // Test 1: use_offset = false, use_subframes = false - cout << "use_offset = false, use_subframes = false" << endl; - for (int i = 0; i < 108003; i++) { - session->timecode_to_sample( timecode1, sample1, false /* use_offset */, false /* use_subframes */ ); - session->sample_to_timecode( sample1, timecode2, false /* use_offset */, false /* use_subframes */ ); - - if ((i > 0) && ( ((sample1 - oldsample) != sample_increment) && ((sample1 - oldsample) != (sample_increment + 1)) && ((sample1 - oldsample) != (sample_increment - 1)))) { - cout << "ERROR: sample increment not right: " << (sample1 - oldsample) << " != " << sample_increment << endl; - cout << "timecode1: " << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; - cout << "sample: " << sample1 << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode2: " << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; - break; - } + if (sscanf (_layout->get_text().c_str(), "%" PRId32 ":%" PRId32 ":%" PRId32 ":%" PRId32, + &TC.hours, &TC.minutes, &TC.seconds, &TC.frames) != 4) { + return false; + } - if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames) { - cout << "ERROR: timecode2 not equal timecode1" << endl; - cout << "timecode1: " << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; - cout << "sample: " << sample1 << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode2: " << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; - break; - } - oldsample = sample1; - session->timecode_increment( timecode1 ); - } - - cout << "sample_increment: " << sample_increment << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode: " << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; -#endif - -#ifdef Timecode_SAMPLE_TEST_2 - // Test 2: use_offset = true, use_subframes = false - cout << "use_offset = true, use_subframes = false" << endl; - - timecode1.hours = 0; - timecode1.minutes = 0; - timecode1.seconds = 0; - timecode1.frames = 0; - timecode1.subframes = 0; - sample1 = oldsample = 0; - - session->sample_to_timecode( sample1, timecode1, true /* use_offset */, false /* use_subframes */ ); - cout << "Starting at sample: " << sample1 << " -> "; - cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << endl; - - for (int i = 0; i < 108003; i++) { - session->timecode_to_sample( timecode1, sample1, true /* use_offset */, false /* use_subframes */ ); - session->sample_to_timecode( sample1, timecode2, true /* use_offset */, false /* use_subframes */ ); - -// cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; -// cout << "sample: " << sample1 << endl; -// cout << "sample: " << sample1 << " -> "; -// cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; - - if ((i > 0) && ( ((sample1 - oldsample) != sample_increment) && ((sample1 - oldsample) != (sample_increment + 1)) && ((sample1 - oldsample) != (sample_increment - 1)))) { - cout << "ERROR: sample increment not right: " << (sample1 - oldsample) << " != " << sample_increment << endl; - cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; - cout << "sample: " << sample1 << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; - break; - } + if (TC.hours > 23U || TC.minutes > 59U || TC.seconds > 59U) { + return false; + } - if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames) { - cout << "ERROR: timecode2 not equal timecode1" << endl; - cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; - cout << "sample: " << sample1 << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; - break; - } - oldsample = sample1; - session->timecode_increment( timecode1 ); - } - - cout << "sample_increment: " << sample_increment << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; -#endif - -#ifdef Timecode_SAMPLE_TEST_3 - // Test 3: use_offset = true, use_subframes = false, decrement - cout << "use_offset = true, use_subframes = false, decrement" << endl; - - session->sample_to_timecode( sample1, timecode1, true /* use_offset */, false /* use_subframes */ ); - cout << "Starting at sample: " << sample1 << " -> "; - cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << endl; - - for (int i = 0; i < 108003; i++) { - session->timecode_to_sample( timecode1, sample1, true /* use_offset */, false /* use_subframes */ ); - session->sample_to_timecode( sample1, timecode2, true /* use_offset */, false /* use_subframes */ ); - -// cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; -// cout << "sample: " << sample1 << endl; -// cout << "sample: " << sample1 << " -> "; -// cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; - - if ((i > 0) && ( ((oldsample - sample1) != sample_increment) && ((oldsample - sample1) != (sample_increment + 1)) && ((oldsample - sample1) != (sample_increment - 1)))) { - cout << "ERROR: sample increment not right: " << (oldsample - sample1) << " != " << sample_increment << endl; - cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; - cout << "sample: " << sample1 << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; - break; - } + if (TC.frames > (uint32_t) rint (_session->timecode_frames_per_second()) - 1) { + return false; + } - if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames) { - cout << "ERROR: timecode2 not equal timecode1" << endl; - cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; - cout << "sample: " << sample1 << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; - break; + if (_session->timecode_drop_frames()) { + if (TC.minutes % 10 && TC.seconds == 0U && TC.frames < 2U) { + return false; } - oldsample = sample1; - session->timecode_decrement( timecode1 ); } - cout << "sample_decrement: " << sample_increment << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; -#endif - - -#ifdef Timecode_SAMPLE_TEST_4 - // Test 4: use_offset = true, use_subframes = true - cout << "use_offset = true, use_subframes = true" << endl; - - for (long sub = 5; sub < 80; sub += 5) { - timecode1.hours = 0; - timecode1.minutes = 0; - timecode1.seconds = 0; - timecode1.frames = 0; - timecode1.subframes = 0; - sample1 = oldsample = (sample_increment * sub) / 80; - - session->sample_to_timecode( sample1, timecode1, true /* use_offset */, true /* use_subframes */ ); - - cout << "starting at sample: " << sample1 << " -> "; - cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << endl; + return true; +} - for (int i = 0; i < 108003; i++) { - session->timecode_to_sample( timecode1, sample1, true /* use_offset */, true /* use_subframes */ ); - session->sample_to_timecode( sample1, timecode2, true /* use_offset */, true /* use_subframes */ ); +bool +AudioClock::minsec_validate_edit (const string& str) +{ + int hrs, mins, secs, millisecs; - if ((i > 0) && ( ((sample1 - oldsample) != sample_increment) && ((sample1 - oldsample) != (sample_increment + 1)) && ((sample1 - oldsample) != (sample_increment - 1)))) { - cout << "ERROR: sample increment not right: " << (sample1 - oldsample) << " != " << sample_increment << endl; - cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; - cout << "sample: " << sample1 << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; - //break; - } + if (sscanf (str.c_str(), "%d:%d:%d.%d", &hrs, &mins, &secs, &millisecs) != 4) { + return false; + } + + if (hrs > 23 || mins > 59 || secs > 59 || millisecs > 999) { + return false; + } - if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames || timecode2.subframes != timecode1.subframes) { - cout << "ERROR: timecode2 not equal timecode1" << endl; - cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; - cout << "sample: " << sample1 << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; - break; - } - oldsample = sample1; - session->timecode_increment( timecode1 ); - } + return true; +} - cout << "sample_increment: " << sample_increment << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; - - for (int i = 0; i < 108003; i++) { - session->timecode_to_sample( timecode1, sample1, true /* use_offset */, true /* use_subframes */ ); - session->sample_to_timecode( sample1, timecode2, true /* use_offset */, true /* use_subframes */ ); - - if ((i > 0) && ( ((oldsample - sample1) != sample_increment) && ((oldsample - sample1) != (sample_increment + 1)) && ((oldsample - sample1) != (sample_increment - 1)))) { - cout << "ERROR: sample increment not right: " << (oldsample - sample1) << " != " << sample_increment << endl; - cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; - cout << "sample: " << sample1 << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; - //break; - } +framepos_t +AudioClock::frames_from_timecode_string (const string& str) const +{ + if (_session == 0) { + return 0; + } - if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames || timecode2.subframes != timecode1.subframes) { - cout << "ERROR: timecode2 not equal timecode1" << endl; - cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; - cout << "sample: " << sample1 << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; - break; - } - oldsample = sample1; - session->timecode_decrement( timecode1 ); - } + Timecode::Time TC; + framepos_t sample; - cout << "sample_decrement: " << sample_increment << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; - } -#endif - - -#ifdef Timecode_SAMPLE_TEST_5 - // Test 5: use_offset = true, use_subframes = false, increment seconds - cout << "use_offset = true, use_subframes = false, increment seconds" << endl; - - timecode1.hours = 0; - timecode1.minutes = 0; - timecode1.seconds = 0; - timecode1.frames = 0; - timecode1.subframes = 0; - sample1 = oldsample = 0; - sample_increment = session->frame_rate(); - - session->sample_to_timecode( sample1, timecode1, true /* use_offset */, false /* use_subframes */ ); - cout << "Starting at sample: " << sample1 << " -> "; - cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << endl; - - for (int i = 0; i < 3600; i++) { - session->timecode_to_sample( timecode1, sample1, true /* use_offset */, false /* use_subframes */ ); - session->sample_to_timecode( sample1, timecode2, true /* use_offset */, false /* use_subframes */ ); - -// cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; -// cout << "sample: " << sample1 << endl; -// cout << "sample: " << sample1 << " -> "; -// cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; - -// if ((i > 0) && ( ((sample1 - oldsample) != sample_increment) && ((sample1 - oldsample) != (sample_increment + 1)) && ((sample1 - oldsample) != (sample_increment - 1)))) -// { -// cout << "ERROR: sample increment not right: " << (sample1 - oldsample) << " != " << sample_increment << endl; -// break; -// } - - if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames) { - cout << "ERROR: timecode2 not equal timecode1" << endl; - cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; - cout << "sample: " << sample1 << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; - break; - } - oldsample = sample1; - session->timecode_increment_seconds( timecode1 ); - } - - cout << "sample_increment: " << sample_increment << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; -#endif - - -#ifdef Timecode_SAMPLE_TEST_6 - // Test 6: use_offset = true, use_subframes = false, increment minutes - cout << "use_offset = true, use_subframes = false, increment minutes" << endl; - - timecode1.hours = 0; - timecode1.minutes = 0; - timecode1.seconds = 0; - timecode1.frames = 0; - timecode1.subframes = 0; - sample1 = oldsample = 0; - sample_increment = session->frame_rate() * 60; - - session->sample_to_timecode( sample1, timecode1, true /* use_offset */, false /* use_subframes */ ); - cout << "Starting at sample: " << sample1 << " -> "; - cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << endl; - - for (int i = 0; i < 60; i++) { - session->timecode_to_sample( timecode1, sample1, true /* use_offset */, false /* use_subframes */ ); - session->sample_to_timecode( sample1, timecode2, true /* use_offset */, false /* use_subframes */ ); - -// cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; -// cout << "sample: " << sample1 << endl; -// cout << "sample: " << sample1 << " -> "; -// cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; - -// if ((i > 0) && ( ((sample1 - oldsample) != sample_increment) && ((sample1 - oldsample) != (sample_increment + 1)) && ((sample1 - oldsample) != (sample_increment - 1)))) -// { -// cout << "ERROR: sample increment not right: " << (sample1 - oldsample) << " != " << sample_increment << endl; -// break; -// } - - if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames) { - cout << "ERROR: timecode2 not equal timecode1" << endl; - cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; - cout << "sample: " << sample1 << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; - break; - } - oldsample = sample1; - session->timecode_increment_minutes( timecode1 ); - } - - cout << "sample_increment: " << sample_increment << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; -#endif - -#ifdef Timecode_SAMPLE_TEST_7 - // Test 7: use_offset = true, use_subframes = false, increment hours - cout << "use_offset = true, use_subframes = false, increment hours" << endl; - - timecode1.hours = 0; - timecode1.minutes = 0; - timecode1.seconds = 0; - timecode1.frames = 0; - timecode1.subframes = 0; - sample1 = oldsample = 0; - sample_increment = session->frame_rate() * 60 * 60; - - session->sample_to_timecode( sample1, timecode1, true /* use_offset */, false /* use_subframes */ ); - cout << "Starting at sample: " << sample1 << " -> "; - cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << endl; - - for (int i = 0; i < 10; i++) { - session->timecode_to_sample( timecode1, sample1, true /* use_offset */, false /* use_subframes */ ); - session->sample_to_timecode( sample1, timecode2, true /* use_offset */, false /* use_subframes */ ); - -// cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; -// cout << "sample: " << sample1 << endl; -// cout << "sample: " << sample1 << " -> "; -// cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; - -// if ((i > 0) && ( ((sample1 - oldsample) != sample_increment) && ((sample1 - oldsample) != (sample_increment + 1)) && ((sample1 - oldsample) != (sample_increment - 1)))) -// { -// cout << "ERROR: sample increment not right: " << (sample1 - oldsample) << " != " << sample_increment << endl; -// break; -// } - - if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames) { - cout << "ERROR: timecode2 not equal timecode1" << endl; - cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> "; - cout << "sample: " << sample1 << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; - break; - } - oldsample = sample1; - session->timecode_increment_hours( timecode1 ); + if (sscanf (str.c_str(), "%d:%d:%d:%d", &TC.hours, &TC.minutes, &TC.seconds, &TC.frames) != 4) { + error << string_compose (_("programming error: %1 %2"), "badly formatted timecode clock string", str) << endmsg; + return 0; } - cout << "sample_increment: " << sample_increment << endl; - cout << "sample: " << sample1 << " -> "; - cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl; -#endif + TC.rate = _session->timecode_frames_per_second(); + TC.drop= _session->timecode_drop_frames(); -#endif + _session->timecode_to_sample (TC, sample, false /* use_offset */, false /* use_subframes */ ); + + // timecode_tester (); return sample; } -nframes_t -AudioClock::minsec_frame_from_display () const +framepos_t +AudioClock::frames_from_minsec_string (const string& str) const { - if (session == 0) { + if (_session == 0) { return 0; } - int hrs = atoi (ms_hours_label.get_text()); - int mins = atoi (ms_minutes_label.get_text()); - float secs = atof (ms_seconds_label.get_text()); + int hrs, mins, secs, millisecs; + framecnt_t sr = _session->frame_rate(); - nframes_t sr = session->frame_rate(); + if (sscanf (str.c_str(), "%d:%d:%d.%d", &hrs, &mins, &secs, &millisecs) != 4) { + error << string_compose (_("programming error: %1 %2"), "badly formatted minsec clock string", str) << endmsg; + return 0; + } - return (nframes_t) floor ((hrs * 60.0f * 60.0f * sr) + (mins * 60.0f * sr) + (secs * sr)); + return (framepos_t) floor ((hrs * 60.0f * 60.0f * sr) + (mins * 60.0f * sr) + (secs * sr) + (millisecs * sr / 1000.0)); } -nframes_t -AudioClock::bbt_frame_from_display (nframes_t pos) const +framepos_t +AudioClock::frames_from_bbt_string (framepos_t pos, const string& str) const { - if (session == 0) { + if (_session == 0) { error << "AudioClock::current_time() called with BBT mode but without session!" << endmsg; return 0; } @@ -1906,43 +1887,43 @@ AudioClock::bbt_frame_from_display (nframes_t pos) const AnyTime any; any.type = AnyTime::BBT; - any.bbt.bars = atoi (bars_label.get_text()); - any.bbt.beats = atoi (beats_label.get_text()); - any.bbt.ticks = atoi (ticks_label.get_text()); - - if (is_duration) { - any.bbt.bars++; - any.bbt.beats++; - } - - nframes_t ret = session->convert_to_frames_at (pos, any); - - return ret; + if (sscanf (str.c_str(), BBT_SCANF_FORMAT, &any.bbt.bars, &any.bbt.beats, &any.bbt.ticks) != 3) { + return 0; + } + + if (is_duration) { + any.bbt.bars++; + any.bbt.beats++; + return _session->any_duration_to_frames (pos, any); + } else { + return _session->convert_to_frames (any); + } } -nframes_t -AudioClock::bbt_frame_duration_from_display (nframes_t pos) const +framepos_t +AudioClock::frame_duration_from_bbt_string (framepos_t pos, const string& str) const { - if (session == 0) { + if (_session == 0) { error << "AudioClock::current_time() called with BBT mode but without session!" << endmsg; return 0; } - BBT_Time bbt; + Timecode::BBT_Time bbt; - - bbt.bars = atoi (bars_label.get_text()); - bbt.beats = atoi (beats_label.get_text()); - bbt.ticks = atoi (ticks_label.get_text()); - - return session->tempo_map().bbt_duration_at(pos,bbt,1); + if (sscanf (str.c_str(), BBT_SCANF_FORMAT, &bbt.bars, &bbt.beats, &bbt.ticks) != 3) { + return 0; + } + + return _session->tempo_map().bbt_duration_at(pos,bbt,1); } -nframes_t -AudioClock::audio_frame_from_display () const +framepos_t +AudioClock::frames_from_audioframes_string (const string& str) const { - return (nframes_t) atoi (audio_frames_label.get_text()); + framepos_t f; + sscanf (str.c_str(), "%" PRId64, &f); + return f; } void @@ -1959,127 +1940,185 @@ AudioClock::build_ops_menu () ops_items.push_back (MenuElem (_("Bars:Beats"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), BBT))); ops_items.push_back (MenuElem (_("Minutes:Seconds"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), MinSec))); ops_items.push_back (MenuElem (_("Samples"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), Frames))); - ops_items.push_back (MenuElem (_("Off"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), Off))); - if (editable && !is_duration && !_follows_playhead) { + if (editable && !_off && !is_duration && !_follows_playhead) { ops_items.push_back (SeparatorElem()); ops_items.push_back (MenuElem (_("Set From Playhead"), sigc::mem_fun(*this, &AudioClock::set_from_playhead))); - ops_items.push_back (MenuElem (_("Locate to this time"), sigc::mem_fun(*this, &AudioClock::locate))); + ops_items.push_back (MenuElem (_("Locate to This Time"), sigc::mem_fun(*this, &AudioClock::locate))); } } void AudioClock::set_from_playhead () { - if (!session) { + if (!_session) { return; } - - set (session->transport_frame()); + + set (_session->transport_frame()); + ValueChanged (); } void AudioClock::locate () { - if (!session || is_duration) { + if (!_session || is_duration) { return; } - - session->request_locate (current_time(), false); + + _session->request_locate (current_time(), _session->transport_rolling ()); } void AudioClock::set_mode (Mode m) { - /* slightly tricky: this is called from within the ARDOUR_UI - constructor by some of its clock members. at that time - the instance pointer is unset, so we have to be careful. - the main idea is to drop keyboard focus in case we had - started editing the clock and then we switch clock mode. - */ - - clock_base.grab_focus (); - if (_mode == m) { return; } - clock_base.remove (); - _mode = m; + insert_map.clear(); + + _layout->set_text (""); + + if (_left_layout) { + _left_layout->set_text (""); + _right_layout->set_text (""); + } + switch (_mode) { case Timecode: - clock_base.add (timecode_packer_hbox); - break; - + mode_based_info_ratio = 0.5; + insert_map.push_back (11); + insert_map.push_back (10); + insert_map.push_back (8); + insert_map.push_back (7); + insert_map.push_back (5); + insert_map.push_back (4); + insert_map.push_back (2); + insert_map.push_back (1); + break; + case BBT: - clock_base.add (bbt_packer_hbox); - break; - + mode_based_info_ratio = 0.5; + insert_map.push_back (11); + insert_map.push_back (10); + insert_map.push_back (9); + insert_map.push_back (8); + insert_map.push_back (6); + insert_map.push_back (5); + insert_map.push_back (3); + insert_map.push_back (2); + insert_map.push_back (1); + break; + case MinSec: - clock_base.add (minsec_packer_hbox); - break; - + mode_based_info_ratio = 1.0; + insert_map.push_back (12); + insert_map.push_back (11); + insert_map.push_back (10); + insert_map.push_back (8); + insert_map.push_back (7); + insert_map.push_back (5); + insert_map.push_back (4); + insert_map.push_back (2); + insert_map.push_back (1); + break; + case Frames: - clock_base.add (frames_packer_hbox); - break; - - case Off: - clock_base.add (off_hbox); + mode_based_info_ratio = 0.5; break; } - set_size_requests (); - set (last_when, true); - clock_base.show_all (); - key_entry_state = 0; - if (!is_transient) { - ModeChanged (); /* EMIT SIGNAL */ - mode_changed (); /* EMIT SIGNAL */ + if (!is_transient) { + ModeChanged (); /* EMIT SIGNAL (the static one)*/ + } + + if (!_fixed_width) { + /* display is different, allow us to resize */ + first_width = 0; + first_height = 0; + queue_resize (); } + + mode_changed (); /* EMIT SIGNAL (the member one) */ } void -AudioClock::set_size_requests () +AudioClock::set_bbt_reference (framepos_t pos) { - /* note that in some fonts, "88" is narrower than "00", hence the 2 pixel padding */ - - switch (_mode) { - case Timecode: - Gtkmm2ext::set_size_request_to_display_given_text (hours_label, "-00", 5, 5); - Gtkmm2ext::set_size_request_to_display_given_text (minutes_label, "00", 5, 5); - Gtkmm2ext::set_size_request_to_display_given_text (seconds_label, "00", 5, 5); - Gtkmm2ext::set_size_request_to_display_given_text (frames_label, "00", 5, 5); - break; + bbt_reference_time = pos; +} - case BBT: - Gtkmm2ext::set_size_request_to_display_given_text (bars_label, "-000", 5, 5); - Gtkmm2ext::set_size_request_to_display_given_text (beats_label, "00", 5, 5); - Gtkmm2ext::set_size_request_to_display_given_text (ticks_label, "0000", 5, 5); - break; +void +AudioClock::on_style_changed (const Glib::RefPtr& old_style) +{ + CairoWidget::on_style_changed (old_style); + set_font (); + set_colors (); +} - case MinSec: - Gtkmm2ext::set_size_request_to_display_given_text (ms_hours_label, "00", 5, 5); - Gtkmm2ext::set_size_request_to_display_given_text (ms_minutes_label, "00", 5, 5); - Gtkmm2ext::set_size_request_to_display_given_text (ms_seconds_label, "00.000", 5, 5); - break; +void +AudioClock::set_editable (bool yn) +{ + editable = yn; +} - case Frames: - Gtkmm2ext::set_size_request_to_display_given_text (audio_frames_label, "0000000000", 5, 5); - break; +void +AudioClock::set_is_duration (bool yn) +{ + if (yn == is_duration) { + return; + } - case Off: - Gtkmm2ext::set_size_request_to_display_given_text (off_hbox, "00000", 5, 5); - break; + is_duration = yn; + set (last_when, true); +} +void +AudioClock::set_off (bool yn) +{ + if (_off == yn) { + return; } + + _off = yn; + + /* force a redraw. last_when will be preserved, but the clock text will + * change + */ + + set (last_when, true); } void -AudioClock::set_bbt_reference (nframes64_t pos) +AudioClock::focus () { - bbt_reference_time = pos; + start_edit (Field (0)); +} + +void +AudioClock::set_corner_radius (double r) +{ + corner_radius = r; + queue_draw (); +} + +void +AudioClock::set_fixed_width (bool yn) +{ + _fixed_width = yn; +} + +void +AudioClock::dpi_reset () +{ + /* force recomputation of size even if we are fixed width + */ + first_width = 0; + first_height = 0; + queue_resize (); }