/* timecode-widget.cpp - Implementation of the timecode widget Copyright (C) Lumiera.org 1999, Paul Davis 2010, Stefan Kangas #include #include // for sprintf #include #include #include #include #include "timecode-widget.hpp" #include "../util/convert.hpp" using namespace sigc; using namespace Gtk; using namespace std; using gui::util::atof; using gui::util::atoi; namespace gui { namespace widgets { // TODO: frame rate should not be a constant, but instead be per sequence const float framerate = 25; sigc::signal TimeCode::ModeChanged; const unsigned int TimeCode::field_length[(int) TimeCode::VFrames+1] = { 2, /* SMPTE_Hours */ 2, /* SMPTE_Minutes */ 2, /* SMPTE_Seconds */ 2, /* SMPTE_Frames */ 2, /* MS_Hours */ 2, /* MS_Minutes */ 5, /* MS_Seconds */ 10 /* VFrames */ }; TimeCode::TimeCode(std::string clock_name, std::string widget_name, bool allow_edit) /*, bool duration,*/ : _name(clock_name), // is_duration(duration), editable(allow_edit), colon1(":"), colon2(":"), colon3(":"), colon4(":"), colon5(":") { last_when = Time(0); last_pdelta = 0; last_sdelta = 0; key_entry_state = 0; ops_menu = 0; dragging = false; 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); 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); ms_hours_ebox.add(ms_hours_label); ms_minutes_ebox.add(ms_minutes_label); ms_seconds_ebox.add(ms_seconds_label); smpte_packer.set_homogeneous(false); smpte_packer.set_border_width(2); smpte_packer.pack_start(hours_ebox, false, false); smpte_packer.pack_start(colon1, false, false); smpte_packer.pack_start(minutes_ebox, false, false); smpte_packer.pack_start(colon2, false, false); smpte_packer.pack_start(seconds_ebox, false, false); smpte_packer.pack_start(colon3, false, false); smpte_packer.pack_start(frames_ebox, false, false); smpte_packer_hbox.pack_start(smpte_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); // Set mode to force update _mode = Off; set_mode(SMPTE); 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(bind(mem_fun( *this, &TimeCode::field_button_release_event), SMPTE_Hours)); // Session::SMPTEOffsetChanged.connect(mem_fun(*this, &TimeCode::smpte_offset_changed)); if (editable) { setup_events(); } set(last_when, true); } void TimeCode::set_widget_name(string name) { 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); 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); 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); queue_draw(); } void TimeCode::setup_events() { clock_base.set_can_focus(true); const Gdk::EventMask eventMask = 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.add_events(eventMask); minutes_ebox.add_events(eventMask); seconds_ebox.add_events(eventMask); frames_ebox.add_events(eventMask); ms_hours_ebox.add_events(eventMask); ms_minutes_ebox.add_events(eventMask); ms_seconds_ebox.add_events(eventMask); audio_frames_ebox.add_events(eventMask); hours_ebox.set_can_focus(true); minutes_ebox.set_can_focus(true); seconds_ebox.set_can_focus(true); frames_ebox.set_can_focus(true); audio_frames_ebox.set_can_focus(true); ms_hours_ebox.set_can_focus(true); ms_minutes_ebox.set_can_focus(true); ms_seconds_ebox.set_can_focus(true); hours_ebox.signal_motion_notify_event().connect(bind(mem_fun( *this, &TimeCode::field_motion_notify_event), SMPTE_Hours)); minutes_ebox.signal_motion_notify_event().connect(bind(mem_fun( *this, &TimeCode::field_motion_notify_event), SMPTE_Minutes)); seconds_ebox.signal_motion_notify_event().connect(bind(mem_fun( *this, &TimeCode::field_motion_notify_event), SMPTE_Seconds)); frames_ebox.signal_motion_notify_event().connect(bind(mem_fun( *this, &TimeCode::field_motion_notify_event), SMPTE_Frames)); audio_frames_ebox.signal_motion_notify_event().connect(bind(mem_fun( *this, &TimeCode::field_motion_notify_event), VFrames)); ms_hours_ebox.signal_motion_notify_event().connect(bind(mem_fun( *this, &TimeCode::field_motion_notify_event), MS_Hours)); ms_minutes_ebox.signal_motion_notify_event().connect(bind(mem_fun( *this, &TimeCode::field_motion_notify_event), MS_Minutes)); ms_seconds_ebox.signal_motion_notify_event().connect(bind(mem_fun( *this, &TimeCode::field_motion_notify_event), MS_Seconds)); hours_ebox.signal_button_press_event().connect(bind(mem_fun( *this, &TimeCode::field_button_press_event), SMPTE_Hours)); minutes_ebox.signal_button_press_event().connect(bind(mem_fun( *this, &TimeCode::field_button_press_event), SMPTE_Minutes)); seconds_ebox.signal_button_press_event().connect(bind(mem_fun( *this, &TimeCode::field_button_press_event), SMPTE_Seconds)); frames_ebox.signal_button_press_event().connect(bind(mem_fun( *this, &TimeCode::field_button_press_event), SMPTE_Frames)); audio_frames_ebox.signal_button_press_event().connect(bind(mem_fun( *this, &TimeCode::field_button_press_event), VFrames)); ms_hours_ebox.signal_button_press_event().connect(bind(mem_fun( *this, &TimeCode::field_button_press_event), MS_Hours)); ms_minutes_ebox.signal_button_press_event().connect(bind(mem_fun( *this, &TimeCode::field_button_press_event), MS_Minutes)); ms_seconds_ebox.signal_button_press_event().connect(bind(mem_fun( *this, &TimeCode::field_button_press_event), MS_Seconds)); hours_ebox.signal_button_release_event().connect(bind(mem_fun( *this, &TimeCode::field_button_release_event), SMPTE_Hours)); minutes_ebox.signal_button_release_event().connect(bind(mem_fun( *this, &TimeCode::field_button_release_event), SMPTE_Minutes)); seconds_ebox.signal_button_release_event().connect(bind(mem_fun( *this, &TimeCode::field_button_release_event), SMPTE_Seconds)); frames_ebox.signal_button_release_event().connect(bind(mem_fun( *this, &TimeCode::field_button_release_event), SMPTE_Frames)); audio_frames_ebox.signal_button_release_event().connect(bind(mem_fun( *this, &TimeCode::field_button_release_event), VFrames)); ms_hours_ebox.signal_button_release_event().connect(bind(mem_fun( *this, &TimeCode::field_button_release_event), MS_Hours)); ms_minutes_ebox.signal_button_release_event().connect(bind(mem_fun( *this, &TimeCode::field_button_release_event), MS_Minutes)); ms_seconds_ebox.signal_button_release_event().connect(bind(mem_fun( *this, &TimeCode::field_button_release_event), MS_Seconds)); hours_ebox.signal_scroll_event().connect(bind(mem_fun( *this, &TimeCode::field_button_scroll_event), SMPTE_Hours)); minutes_ebox.signal_scroll_event().connect(bind(mem_fun( *this, &TimeCode::field_button_scroll_event), SMPTE_Minutes)); seconds_ebox.signal_scroll_event().connect(bind(mem_fun( *this, &TimeCode::field_button_scroll_event), SMPTE_Seconds)); frames_ebox.signal_scroll_event().connect(bind(mem_fun( *this, &TimeCode::field_button_scroll_event), SMPTE_Frames)); audio_frames_ebox.signal_scroll_event().connect(bind(mem_fun( *this, &TimeCode::field_button_scroll_event), VFrames)); ms_hours_ebox.signal_scroll_event().connect(bind(mem_fun( *this, &TimeCode::field_button_scroll_event), MS_Hours)); ms_minutes_ebox.signal_scroll_event().connect(bind(mem_fun( *this, &TimeCode::field_button_scroll_event), MS_Minutes)); ms_seconds_ebox.signal_scroll_event().connect(bind(mem_fun( *this, &TimeCode::field_button_scroll_event), MS_Seconds)); hours_ebox.signal_key_press_event().connect(bind(mem_fun( *this, &TimeCode::field_key_press_event), SMPTE_Hours)); minutes_ebox.signal_key_press_event().connect(bind(mem_fun( *this, &TimeCode::field_key_press_event), SMPTE_Minutes)); seconds_ebox.signal_key_press_event().connect(bind(mem_fun( *this, &TimeCode::field_key_press_event), SMPTE_Seconds)); frames_ebox.signal_key_press_event().connect(bind(mem_fun( *this, &TimeCode::field_key_press_event), SMPTE_Frames)); audio_frames_ebox.signal_key_press_event().connect(bind(mem_fun( *this, &TimeCode::field_key_press_event), VFrames)); ms_hours_ebox.signal_key_press_event().connect(bind(mem_fun( *this, &TimeCode::field_key_press_event), MS_Hours)); ms_minutes_ebox.signal_key_press_event().connect(bind(mem_fun( *this, &TimeCode::field_key_press_event), MS_Minutes)); ms_seconds_ebox.signal_key_press_event().connect(bind(mem_fun( *this, &TimeCode::field_key_press_event), MS_Seconds)); hours_ebox.signal_key_release_event().connect(bind(mem_fun( *this, &TimeCode::field_key_release_event), SMPTE_Hours)); minutes_ebox.signal_key_release_event().connect(bind(mem_fun( *this, &TimeCode::field_key_release_event), SMPTE_Minutes)); seconds_ebox.signal_key_release_event().connect(bind(mem_fun( *this, &TimeCode::field_key_release_event), SMPTE_Seconds)); frames_ebox.signal_key_release_event().connect(bind(mem_fun( *this, &TimeCode::field_key_release_event), SMPTE_Frames)); audio_frames_ebox.signal_key_release_event().connect(bind(mem_fun( *this, &TimeCode::field_key_release_event), VFrames)); ms_hours_ebox.signal_key_release_event().connect(bind(mem_fun( *this, &TimeCode::field_key_release_event), MS_Hours)); ms_minutes_ebox.signal_key_release_event().connect(bind(mem_fun( *this, &TimeCode::field_key_release_event), MS_Minutes)); ms_seconds_ebox.signal_key_release_event().connect(bind(mem_fun( *this, &TimeCode::field_key_release_event), MS_Seconds)); hours_ebox.signal_focus_in_event().connect(bind(mem_fun( *this, &TimeCode::field_focus_in_event), SMPTE_Hours)); minutes_ebox.signal_focus_in_event().connect(bind(mem_fun( *this, &TimeCode::field_focus_in_event), SMPTE_Minutes)); seconds_ebox.signal_focus_in_event().connect(bind(mem_fun( *this, &TimeCode::field_focus_in_event), SMPTE_Seconds)); frames_ebox.signal_focus_in_event().connect(bind(mem_fun( *this, &TimeCode::field_focus_in_event), SMPTE_Frames)); audio_frames_ebox.signal_focus_in_event().connect(bind(mem_fun( *this, &TimeCode::field_focus_in_event), VFrames)); ms_hours_ebox.signal_focus_in_event().connect(bind(mem_fun( *this, &TimeCode::field_focus_in_event), MS_Hours)); ms_minutes_ebox.signal_focus_in_event().connect(bind(mem_fun( *this, &TimeCode::field_focus_in_event), MS_Minutes)); ms_seconds_ebox.signal_focus_in_event().connect(bind(mem_fun( *this, &TimeCode::field_focus_in_event), MS_Seconds)); hours_ebox.signal_focus_out_event().connect(bind(mem_fun( *this, &TimeCode::field_focus_out_event), SMPTE_Hours)); minutes_ebox.signal_focus_out_event().connect(bind(mem_fun( *this, &TimeCode::field_focus_out_event), SMPTE_Minutes)); seconds_ebox.signal_focus_out_event().connect(bind(mem_fun( *this, &TimeCode::field_focus_out_event), SMPTE_Seconds)); frames_ebox.signal_focus_out_event().connect(bind(mem_fun( *this, &TimeCode::field_focus_out_event), SMPTE_Frames)); audio_frames_ebox.signal_focus_out_event().connect(bind(mem_fun( *this, &TimeCode::field_focus_out_event), VFrames)); ms_hours_ebox.signal_focus_out_event().connect(bind(mem_fun( *this, &TimeCode::field_focus_out_event), MS_Hours)); ms_minutes_ebox.signal_focus_out_event().connect(bind(mem_fun( *this, &TimeCode::field_focus_out_event), MS_Minutes)); ms_seconds_ebox.signal_focus_out_event().connect(bind(mem_fun( *this, &TimeCode::field_focus_out_event), MS_Seconds)); clock_base.signal_focus_in_event().connect(mem_fun( *this, &TimeCode::drop_focus_handler)); } bool TimeCode::drop_focus_handler(GdkEventFocus* ignored) { // Keyboard::magic_widget_drop_focus(); return false; } void TimeCode::on_realize() { HBox::on_realize(); /* styles are not available until the widgets are bound to a window */ set_size_requests(); } void TimeCode::set(Time when, bool force) { if (!force && when == last_when) return; switch (_mode) { case SMPTE: set_smpte(when, force); break; case MinSec: set_minsec(when, force); break; case Frames: set_frames(when, force); break; case Off: break; } last_when = when; } // void // TimeCode::smpte_offset_changed() // { // gavl_time_t current; // switch (_mode) { // case SMPTE: // // if(is_duration) { // // current = current_duration(); // // } else { // current = current_time(); // // } // set(current, true); // break; // default: // break; // } // } void TimeCode::set_frames(Time when, bool force) { char buf[32]; snprintf(buf, sizeof(buf), "%u", (unsigned int)when); audio_frames_label.set_text(buf); } void TimeCode::set_minsec(Time when, bool force) { char buf[32]; int hrs = when.getHours(); int mins = when.getMins(); float secs = when.getSecs(); if (force || hrs != ms_last_hrs) { sprintf(buf, "%02d", hrs); ms_hours_label.set_text(buf); ms_last_hrs = hrs; } if (force || mins != ms_last_mins) { sprintf(buf, "%02d", mins); ms_minutes_label.set_text(buf); ms_last_mins = mins; } if (force || secs != ms_last_secs) { sprintf(buf, "%06.3f", secs); ms_seconds_label.set_text(buf); ms_last_secs = secs; } } void TimeCode::set_smpte(Time when, bool force) { char buf[32]; int smpte_negative = 0; // FIXME: when < 0; int smpte_hours = when.getHours(); int smpte_minutes = when.getMins(); int smpte_seconds = when.getSecs(); int smpte_frames = 0; //when.getFrames(framerate); // if (is_duration) { // session->smpte_duration(when, smpte); // } else { // session->smpte_time(when, smpte); // } if (force || smpte_hours != last_hrs || smpte_negative != last_negative) { if (smpte_negative) { sprintf(buf, "-%02d", smpte_hours); } else { sprintf(buf, " %02d", smpte_hours); } hours_label.set_text(buf); last_hrs = smpte_hours; last_negative = smpte_negative; } if (force || smpte_minutes != last_mins) { sprintf(buf, "%02d", smpte_minutes); minutes_label.set_text(buf); last_mins = smpte_minutes; } if (force || smpte_seconds != last_secs) { sprintf(buf, "%02d", smpte_seconds); seconds_label.set_text(buf); last_secs = smpte_seconds; } if (force || smpte_frames != last_frames) { sprintf(buf, "%02d", smpte_frames); frames_label.set_text(buf); last_frames = smpte_frames; } } void TimeCode::focus() { switch (_mode) { case SMPTE: hours_ebox.grab_focus(); break; case MinSec: ms_hours_ebox.grab_focus(); break; case Frames: frames_ebox.grab_focus(); break; case Off: break; } } bool TimeCode::field_key_press_event(GdkEventKey *ev, Field field) { /* all key activity is handled on key release */ return true; } bool TimeCode::field_key_release_event(GdkEventKey *ev, Field field) { Label *label = 0; string new_text; char new_char = 0; bool move_on = false; switch (field) { case SMPTE_Hours: label = &hours_label; break; case SMPTE_Minutes: label = &minutes_label; break; case SMPTE_Seconds: label = &seconds_label; break; case SMPTE_Frames: label = &frames_label; break; case VFrames: label = &audio_frames_label; break; case MS_Hours: label = &ms_hours_label; break; case MS_Minutes: label = &ms_minutes_label; break; case MS_Seconds: label = &ms_seconds_label; break; default: return false; } 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; case GDK_period: case GDK_KP_Decimal: if (_mode == MinSec && field == MS_Seconds) { new_char = '.'; } else { return false; } break; case GDK_Tab: case GDK_Return: case GDK_KP_Enter: move_on = true; break; case GDK_Escape: key_entry_state = 0; clock_base.grab_focus(); ChangeAborted(); /* EMIT SIGNAL */ return true; default: return false; } if (!move_on) { if (key_entry_state == 0) { /* initialize with a fresh new string */ if (field != VFrames) { for (unsigned int xn = 0; xn < field_length[field] - 1; ++xn) { new_text += '0'; } } else { new_text = ""; } } else { string existing = label->get_text(); if (existing.length() >= field_length[field]) { new_text = existing.substr(1, field_length[field] - 1); } else { new_text = existing.substr(0, field_length[field] - 1); } } new_text += new_char; label->set_text(new_text); key_entry_state++; } if (key_entry_state == field_length[field]) { move_on = true; } if (move_on) { if (key_entry_state) { switch (field) { case SMPTE_Hours: case SMPTE_Minutes: case SMPTE_Seconds: case SMPTE_Frames: // Check SMPTE fields for sanity (may also adjust fields) smpte_sanitize_display(); break; default: break; } ValueChanged(); /* EMIT_SIGNAL */ } /* move on to the next field. */ switch (field) { /* SMPTE */ case SMPTE_Hours: minutes_ebox.grab_focus(); break; case SMPTE_Minutes: seconds_ebox.grab_focus(); break; case SMPTE_Seconds: frames_ebox.grab_focus(); break; case SMPTE_Frames: clock_base.grab_focus(); break; /* frames */ case VFrames: clock_base.grab_focus(); break; /* Min:Sec */ 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; default: break; } } //if user hit Enter, lose focus switch (ev->keyval) { case GDK_Return: case GDK_KP_Enter: clock_base.grab_focus(); } return true; } bool TimeCode::field_focus_in_event(GdkEventFocus *ev, Field field) { key_entry_state = 0; // Keyboard::magic_widget_grab_focus(); switch (field) { case SMPTE_Hours: hours_ebox.set_flags(Gtk::HAS_FOCUS); hours_ebox.set_state(Gtk::STATE_ACTIVE); break; case SMPTE_Minutes: minutes_ebox.set_flags(Gtk::HAS_FOCUS); minutes_ebox.set_state(Gtk::STATE_ACTIVE); break; case SMPTE_Seconds: seconds_ebox.set_flags(Gtk::HAS_FOCUS); seconds_ebox.set_state(Gtk::STATE_ACTIVE); break; case SMPTE_Frames: frames_ebox.set_flags(Gtk::HAS_FOCUS); frames_ebox.set_state(Gtk::STATE_ACTIVE); break; case VFrames: audio_frames_ebox.set_flags(Gtk::HAS_FOCUS); audio_frames_ebox.set_state(Gtk::STATE_ACTIVE); break; 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; } return false; } bool TimeCode::field_focus_out_event(GdkEventFocus *ev, Field field) { switch (field) { case SMPTE_Hours: hours_ebox.unset_flags(Gtk::HAS_FOCUS); hours_ebox.set_state(Gtk::STATE_NORMAL); break; case SMPTE_Minutes: minutes_ebox.unset_flags(Gtk::HAS_FOCUS); minutes_ebox.set_state(Gtk::STATE_NORMAL); break; case SMPTE_Seconds: seconds_ebox.unset_flags(Gtk::HAS_FOCUS); seconds_ebox.set_state(Gtk::STATE_NORMAL); break; case SMPTE_Frames: frames_ebox.unset_flags(Gtk::HAS_FOCUS); frames_ebox.set_state(Gtk::STATE_NORMAL); break; case VFrames: audio_frames_ebox.unset_flags(Gtk::HAS_FOCUS); audio_frames_ebox.set_state(Gtk::STATE_NORMAL); break; case MS_Hours: ms_hours_ebox.unset_flags(Gtk::HAS_FOCUS); ms_hours_ebox.set_state(Gtk::STATE_NORMAL); break; case MS_Minutes: ms_minutes_ebox.unset_flags(Gtk::HAS_FOCUS); ms_minutes_ebox.set_state(Gtk::STATE_NORMAL); break; case MS_Seconds: ms_seconds_ebox.unset_flags(Gtk::HAS_FOCUS); ms_seconds_ebox.set_state(Gtk::STATE_NORMAL); break; } // Keyboard::magic_widget_drop_focus(); return false; } bool TimeCode::field_button_release_event (GdkEventButton *ev, Field field) { if (dragging) { gdk_pointer_ungrab(GDK_CURRENT_TIME); dragging = false; if (ev->y > drag_start_y+1 || ev->y < drag_start_y-1 || (ev->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) { // we actually dragged so return without setting editing focus, or we shift clicked return true; } } if (!editable) { if (ops_menu == 0) { build_ops_menu(); } ops_menu->popup(1, ev->time); return true; } switch (ev->button) { case 1: switch (field) { case SMPTE_Hours: hours_ebox.grab_focus(); break; case SMPTE_Minutes: minutes_ebox.grab_focus(); break; case SMPTE_Seconds: seconds_ebox.grab_focus(); break; case SMPTE_Frames: frames_ebox.grab_focus(); break; case VFrames: 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; } break; case 3: if (ops_menu == 0) { build_ops_menu(); } ops_menu->popup(1, ev->time); return true; default: break; } return true; } bool TimeCode::field_button_press_event(GdkEventButton *ev, Field field) { return false; // if (session == 0) return false; // Time frames = 0; switch (ev->button) { case 1: // if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { // set (frames, true); // ValueChanged (); /* EMIT_SIGNAL */ // } /* 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 */ // } break; case 3: /* used for context sensitive menu */ return false; break; default: return false; break; } return true; } bool TimeCode::field_button_scroll_event (GdkEventScroll *ev, Field field) { return false; // if (session == 0) { // return false; // } // Time 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; 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; default: return false; break; } return true; } bool TimeCode::field_motion_notify_event (GdkEventMotion *ev, Field field) { if (!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; } if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) { 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) { int frames; Time pos; int dir; dir = (drag_accum < 0 ? 1:-1); pos = current_time(); frames = get_frames(field,pos,dir); if (frames != 0 && frames * drag_accum < ((gavl_time_t)current_time())) { // minus because up is negative in computer-land set ((Time) floor (pos - drag_accum * frames), false); } else { set (Time(0) , false); } drag_accum = 0; ValueChanged(); /* EMIT_SIGNAL */ } return true; } int TimeCode::get_frames(Field field, Time pos, int dir) { int frames = 0; // switch (field) // { // case SMPTE_Hours: // frames = (Time) floor (3600.0 * session->frame_rate()); // break; // case SMPTE_Minutes: // frames = (Time) floor (60.0 * session->frame_rate()); // break; // case SMPTE_Seconds: // frames = session->frame_rate(); // break; // case SMPTE_Frames: // frames = (Time) floor (session->frame_rate() / session->smpte_frames_per_second()); // break; // case VFrames: // frames = 1; // break; // case MS_Hours: // frames = (Time) floor (3600.0 * session->frame_rate()); // break; // case MS_Minutes: // frames = (Time) floor (60.0 * session->frame_rate()); // break; // case MS_Seconds: // frames = session->frame_rate(); // break; // } return frames; } Time TimeCode::current_time(Time pos) const { Time ret = Time(0); switch (_mode) { case SMPTE: ret = smpte_time_from_display(); break; case MinSec: ret = minsec_time_from_display(); break; case Frames: ret = audio_time_from_display(); break; case Off: break; } return ret; } Time TimeCode::current_duration(Time pos) const { Time ret = Time(0); switch (_mode) { case SMPTE: ret = smpte_time_from_display(); break; case MinSec: ret = minsec_time_from_display(); break; case Frames: ret = audio_time_from_display(); break; case Off: break; } return ret; } void TimeCode::smpte_sanitize_display() { // Check SMPTE fields for sanity, possibly adjusting values if (atoi(minutes_label.get_text()) > 59) { minutes_label.set_text("59"); } if (atoi(seconds_label.get_text()) > 59) { seconds_label.set_text("59"); } if (atoi(frames_label.get_text()) > framerate - 1) { char buf[32]; sprintf(buf, "%02d", int(framerate - 1)); frames_label.set_text(buf); } // TODO: Drop frames // if (session->smpte_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"); // } // } } Time TimeCode::smpte_time_from_display() const { // TODO // SMPTE::Time smpte; // Time sample; // smpte.hours = atoi (hours_label.get_text()); // smpte.minutes = atoi (minutes_label.get_text()); // smpte.seconds = atoi (seconds_label.get_text()); // smpte.frames = atoi (frames_label.get_text()); // smpte.rate = session->smpte_frames_per_second(); // smpte.drop= session->smpte_drop_frames(); // session->smpte_to_sample(smpte, sample, false /* use_offset */, false /* use_subframes */ ); return Time(0); } Time TimeCode::minsec_time_from_display () const { // TODO // int hrs = atoi (ms_hours_label.get_text()); // int mins = atoi (ms_minutes_label.get_text()); // float secs = atof (ms_seconds_label.get_text()); // Time sr = session->frame_rate(); // return (Time) floor ((hrs * 60.0f * 60.0f * sr) + (mins * 60.0f * sr) + (secs * sr)); return Time(0); } Time TimeCode::audio_time_from_display () const { return Time((gavl_time_t) atoi (audio_frames_label.get_text())); } void TimeCode::build_ops_menu () { using namespace Menu_Helpers; ops_menu = new Menu; MenuList& ops_items = ops_menu->items(); ops_menu->set_name ("LumieraContextMenu"); ops_items.push_back (MenuElem ("SMPTE", bind (mem_fun(*this, &TimeCode::set_mode), SMPTE))); ops_items.push_back (MenuElem ("Minutes:Seconds", bind (mem_fun(*this, &TimeCode::set_mode), MinSec))); ops_items.push_back (MenuElem ("Frames", bind (mem_fun(*this, &TimeCode::set_mode), Frames))); ops_items.push_back (MenuElem ("Off", bind (mem_fun(*this, &TimeCode::set_mode), Off))); } void TimeCode::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; switch (_mode) { case SMPTE: clock_base.add(smpte_packer_hbox); break; case MinSec: clock_base.add(minsec_packer_hbox); break; case Frames: clock_base.add(frames_packer_hbox); break; case Off: clock_base.add(off_hbox); break; } set_size_requests(); set(last_when, true); clock_base.show_all(); key_entry_state = 0; ModeChanged(); /* EMIT SIGNAL */ } void TimeCode::set_size_requests() { /* note that in some fonts, "88" is narrower than "00", hence the 2 pixel padding */ switch (_mode) { case SMPTE: set_size_request_to_display_given_text(hours_label, "-00", 5, 5); set_size_request_to_display_given_text(minutes_label, "00", 5, 5); set_size_request_to_display_given_text(seconds_label, "00", 5, 5); set_size_request_to_display_given_text(frames_label, "00", 5, 5); break; case MinSec: set_size_request_to_display_given_text(ms_hours_label, "00", 5, 5); set_size_request_to_display_given_text(ms_minutes_label, "00", 5, 5); set_size_request_to_display_given_text(ms_seconds_label, "00.000", 5, 5); break; case Frames: set_size_request_to_display_given_text(audio_frames_label, "0000000000", 5, 5); break; case Off: set_size_request_to_display_given_text(off_hbox, "00000", 5, 5); break; } } void TimeCode::set_size_request_to_display_given_text(Gtk::Widget &w, const gchar *text, gint hpadding, gint vpadding) { int width, height; w.ensure_style(); get_ink_pixel_size(w.create_pango_layout(text), width, height); w.set_size_request(width + hpadding, height + vpadding); } void TimeCode::get_ink_pixel_size (Glib::RefPtr layout, int& width, int& height) { Pango::Rectangle ink_rect = layout->get_ink_extents (); width = (ink_rect.get_width() + PANGO_SCALE / 2) / PANGO_SCALE; height = (ink_rect.get_height() + PANGO_SCALE / 2) / PANGO_SCALE; } } // namespace widgets } // namespace gui