X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Faudio_clock.cc;h=fd8aca8df18466016f9494a761bc093b22b59ef1;hb=611dcdd24932222d676da4d9a4dca643f79db4a4;hp=3dbdfa859811d0c4776d686b19387bc4db6783ee;hpb=965f77aaeb1117bd829690a54dd94c789a9c03dd;p=ardour.git diff --git a/gtk2_ardour/audio_clock.cc b/gtk2_ardour/audio_clock.cc index 3dbdfa8598..fd8aca8df1 100644 --- a/gtk2_ardour/audio_clock.cc +++ b/gtk2_ardour/audio_clock.cc @@ -24,17 +24,16 @@ #include "pbd/enumwriter.h" #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 "ardour/slave.h" -#include #include "ardour_ui.h" #include "audio_clock.h" @@ -57,18 +56,32 @@ 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) - : _name (clock_name) + : ops_menu (0) + , _name (clock_name) , is_transient (transient) , is_duration (duration) , editable (allow_edit) , _follows_playhead (follows_playhead) , _off (false) - , ops_menu (0) + , _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) @@ -99,6 +112,7 @@ AudioClock::AudioClock (const string& clock_name, bool transient, const string& } ColorsChanged.connect (sigc::mem_fun (*this, &AudioClock::set_colors)); + DPIReset.connect (sigc::mem_fun (*this, &AudioClock::dpi_reset)); } AudioClock::~AudioClock () @@ -158,6 +172,17 @@ AudioClock::set_font () info_attributes.change (*font_attr); delete font_attr; + + /* 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" + */ + + Glib::RefPtr tmp = Pango::Layout::create (get_pango_context()); + int ignore_height; + + tmp->set_text ("8"); + tmp->get_pixel_size (em_width, ignore_height); } void @@ -175,38 +200,52 @@ AudioClock::set_colors () 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())); } - /* store for bg in render() */ + /* store for bg and cursor in render() */ UINT_TO_RGBA (bg_color, &r, &g, &b, &a); - r = lrint ((r/256.0) * 65535.0); - g = lrint ((g/256.0) * 65535.0); - b = lrint ((b/256.0) * 65535.0); - bg_r = r/256.0; - bg_g = g/256.0; - bg_b = b/256.0; - bg_a = a/256.0; + + bg_r = r/255.0; + bg_g = g/255.0; + bg_b = b/255.0; + bg_a = a/255.0; + + UINT_TO_RGBA (cursor_color, &r, &g, &b, &a); + + cursor_r = r/255.0; + cursor_g = g/255.0; + cursor_b = b/255.0; + cursor_a = a/255.0; + + /* rescale for Pango colors ... sigh */ + + r = lrint (r * 65535.0); + g = lrint (g * 65535.0); + b = lrint (b * 65535.0); UINT_TO_RGBA (text_color, &r, &g, &b, &a); - r = lrint ((r/256.0) * 65535.0); - g = lrint ((g/256.0) * 65535.0); - b = lrint ((b/256.0) * 65535.0); + 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)); UINT_TO_RGBA (editing_color, &r, &g, &b, &a); - r = lrint ((r/256.0) * 65535.0); - g = lrint ((g/256.0) * 65535.0); - b = lrint ((b/256.0) * 65535.0); + 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); @@ -235,11 +274,20 @@ AudioClock::render (cairo_t* cr) if (_need_bg) { cairo_set_source_rgba (cr, bg_r, bg_g, bg_b, bg_a); - Gtkmm2ext::rounded_rectangle (cr, 0, 0, get_width(), upper_height, 9); + 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); } - cairo_move_to (cr, x_leading_padding, (upper_height - layout_height) / 2.0); + if (!_fixed_width) { + cairo_move_to (cr, layout_x_offset, 0); + } else { + cairo_move_to (cr, layout_x_offset, (upper_height - layout_height) / 2.0); + } + pango_cairo_show_layout (cr, _layout->gobj()); if (_left_layout) { @@ -255,7 +303,11 @@ AudioClock::render (cairo_t* cr) double left_rect_width = round (((get_width() - separator_height) * mode_based_info_ratio) + 0.5); if (_need_bg) { - Gtkmm2ext::rounded_rectangle (cr, 0, upper_height + separator_height, left_rect_width, h, 9); + 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); } @@ -263,8 +315,14 @@ AudioClock::render (cairo_t* cr) pango_cairo_show_layout (cr, _left_layout->gobj()); if (_need_bg) { - Gtkmm2ext::rounded_rectangle (cr, left_rect_width + separator_height, upper_height + separator_height, - get_width() - separator_height - left_rect_width, h, 9); + 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); } @@ -275,31 +333,64 @@ AudioClock::render (cairo_t* cr) /* no info to display, or just one */ if (_need_bg) { - Gtkmm2ext::rounded_rectangle (cr, 0, upper_height + separator_height, get_width(), h, 9); + 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 (editing) { - const double cursor_width = 12; /* need em width here, not 16 */ - if (!insert_map.empty()) { - Pango::Rectangle cursor = _layout->get_cursor_strong_pos (insert_map[input_string.length()]); - - cairo_set_source_rgba (cr, 0.9, 0.1, 0.1, 0.8); - cairo_rectangle (cr, - x_leading_padding + cursor.get_x()/PANGO_SCALE + cursor_width, - (upper_height - layout_height)/2.0, - 2.0, cursor.get_height()/PANGO_SCALE); - cairo_fill (cr); + + + 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 { + /* we've entered all possible digits, no cursor */ + } + } else { if (input_string.empty()) { - cairo_set_source_rgba (cr, 0.9, 0.1, 0.1, 0.8); - cairo_rectangle (cr, - (get_width()/2.0), - (upper_height - layout_height)/2.0, - 2.0, upper_height); + 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); } } @@ -316,11 +407,30 @@ AudioClock::on_size_allocate (Gtk::Allocation& alloc) } else { upper_height = get_height(); } + + if (_fixed_width) { + /* center display in available space */ + layout_x_offset = (get_width() - layout_width)/2.0; + } else { + /* left justify */ + layout_x_offset = 0; + } } 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; + } + Glib::RefPtr tmp; Glib::RefPtr style = get_style (); Pango::FontDescription font; @@ -335,13 +445,30 @@ AudioClock::on_size_request (Gtk::Requisition* req) tmp->set_font_description (font); - /* this string is the longest thing we will ever display, - and also includes the BBT "|" 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 (" 88888888888,|"); + 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; + } + } tmp->get_pixel_size (req->width, req->height); @@ -376,6 +503,9 @@ AudioClock::on_size_request (Gtk::Requisition* req) req->height += info_height; req->height += separator_height; } + + first_height = req->height; + first_width = req->width; } void @@ -391,7 +521,7 @@ AudioClock::show_edit_status (int length) } void -AudioClock::start_edit () +AudioClock::start_edit (Field f) { pre_edit_string = _layout->get_text (); if (!insert_map.empty()) { @@ -403,12 +533,62 @@ AudioClock::start_edit () input_string.clear (); editing = true; + if (f) { + input_string = get_field (f); + show_edit_status (merge_input_and_edit_string ()); + _layout->set_text (edit_string); + } + queue_draw (); Keyboard::magic_widget_grab_focus (); grab_focus (); } +string +AudioClock::get_field (Field f) +{ + switch (f) { + case Timecode_Hours: + return edit_string.substr (1, 2); + break; + case Timecode_Minutes: + return edit_string.substr (4, 2); + break; + case Timecode_Seconds: + return edit_string.substr (7, 2); + break; + case Timecode_Frames: + return edit_string.substr (10, 2); + break; + case MS_Hours: + return edit_string.substr (1, 2); + break; + case MS_Minutes: + return edit_string.substr (4, 2); + break; + case MS_Seconds: + return edit_string.substr (7, 2); + break; + case MS_Milliseconds: + return edit_string.substr (10, 3); + break; + case Bars: + return edit_string.substr (1, 3); + break; + case Beats: + return edit_string.substr (5, 2); + break; + case Ticks: + return edit_string.substr (8, 4); + break; + case AudioFrames: + return edit_string; + break; + } + return ""; +} + void AudioClock::end_edit (bool modify) { @@ -426,6 +606,7 @@ AudioClock::end_edit (bool modify) break; case MinSec: + ok = minsec_validate_edit (edit_string); break; case Frames: @@ -614,7 +795,7 @@ AudioClock::parse_as_timecode_distance (const std::string& str) } framecnt_t -AudioClock::parse_as_bbt_distance (const std::string& str) +AudioClock::parse_as_bbt_distance (const std::string&) { return 0; } @@ -622,24 +803,18 @@ AudioClock::parse_as_bbt_distance (const std::string& str) framecnt_t AudioClock::parse_as_distance (const std::string& instr) { - string str = instr; - - /* the input string is in reverse order */ - - std::reverse (str.begin(), str.end()); - switch (_mode) { case Timecode: - return parse_as_timecode_distance (str); + return parse_as_timecode_distance (instr); break; case Frames: - return parse_as_frames_distance (str); + return parse_as_frames_distance (instr); break; case BBT: - return parse_as_bbt_distance (str); + return parse_as_bbt_distance (instr); break; case MinSec: - return parse_as_minsec_distance (str); + return parse_as_minsec_distance (instr); break; } return 0; @@ -648,6 +823,35 @@ AudioClock::parse_as_distance (const std::string& instr) 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; + } + framecnt_t frames = parse_as_distance (input_string); editing = false; @@ -796,7 +1000,7 @@ AudioClock::set_frames (framepos_t when, bool /*force*/) } void -AudioClock::set_minsec (framepos_t when, bool force) +AudioClock::set_minsec (framepos_t when, bool /*force*/) { char buf[32]; framecnt_t left; @@ -841,7 +1045,7 @@ AudioClock::set_minsec (framepos_t when, bool force) } void -AudioClock::set_timecode (framepos_t when, bool force) +AudioClock::set_timecode (framepos_t when, bool /*force*/) { char buf[32]; Timecode::Time TC; @@ -907,7 +1111,7 @@ AudioClock::set_timecode (framepos_t when, bool force) } void -AudioClock::set_bbt (framepos_t when, bool force) +AudioClock::set_bbt (framepos_t when, bool /*force*/) { char buf[16]; Timecode::BBT_Time BBT; @@ -943,9 +1147,11 @@ AudioClock::set_bbt (framepos_t when, bool force) } if (negative) { - snprintf (buf, sizeof (buf), "-%03" PRIu32 "|%02" PRIu32 "|%04" PRIu32, BBT.bars, BBT.beats, BBT.ticks); + 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 "|%02" PRIu32 "|%04" PRIu32, BBT.bars, BBT.beats, BBT.ticks); + 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); @@ -964,7 +1170,7 @@ AudioClock::set_bbt (framepos_t when, bool force) sprintf (buf, "%-5.2f", m.tempo().beats_per_minute()); _left_layout->set_text (buf); - sprintf (buf, "%g|%g", m.meter().beats_per_bar(), m.meter().note_divisor()); + sprintf (buf, "%g/%g", m.meter().divisions_per_bar(), m.meter().note_divisor()); _right_layout->set_text (buf); } } @@ -1004,50 +1210,6 @@ AudioClock::set_session (Session *s) bool AudioClock::on_key_press_event (GdkEventKey* ev) -{ - if (!editing) { - return false; - } - - /* return true for keys that we MIGHT use - at release - */ - 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: - return true; - default: - return false; - } -} - -bool -AudioClock::on_key_release_event (GdkEventKey *ev) { if (!editing) { return false; @@ -1055,6 +1217,8 @@ AudioClock::on_key_release_event (GdkEventKey *ev) string new_text; char new_char = 0; + int highlight_length; + framepos_t pos; switch (ev->keyval) { case GDK_0: @@ -1124,7 +1288,11 @@ AudioClock::on_key_release_event (GdkEventKey *ev) case GDK_Delete: case GDK_BackSpace: - input_string = input_string.substr (1, input_string.length() - 1); + if (!input_string.empty()) { + /* delete the last key entered + */ + input_string = input_string.substr (0, input_string.length() - 1); + } goto use_input_string; default: @@ -1132,75 +1300,110 @@ AudioClock::on_key_release_event (GdkEventKey *ev) } if (!insert_map.empty() && (input_string.length() >= insert_map.size())) { - /* eat the key event, but do no nothing with it */ + /* too many digits: eat the key event, but do nothing with it */ return true; } - input_string.insert (input_string.begin(), new_char); + input_string.push_back (new_char); use_input_string: - string::reverse_iterator ri; - vector insert_at; - int highlight_length = 0; - char buf[32]; - framepos_t pos; - - /* merge with pre-edit-string into edit 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.push_back (new_char); - } else { + 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: - edit_string = pre_edit_string; - - /* backup through the original string, till we have - * enough digits locations to put all the digits from - * the input string. - */ - - for (ri = edit_string.rbegin(); ri != edit_string.rend(); ++ri) { - if (isdigit (*ri)) { - insert_at.push_back (edit_string.length() - (ri - edit_string.rbegin()) - 1); - if (insert_at.size() == input_string.length()) { - break; - } - } - } - - if (insert_at.size() != input_string.length()) { - error << "something went wrong " << endmsg; - } else { - for (int i = input_string.length() - 1; i >= 0; --i) { - edit_string[insert_at[i]] = input_string[i]; - } - - highlight_length = edit_string.length() - insert_at.back(); - } - - break; + highlight_length = merge_input_and_edit_string (); } + + show_edit_status (highlight_length); + _layout->set_text (edit_string); + queue_draw (); + + return true; +} + +int +AudioClock::merge_input_and_edit_string () +{ + /* merge with pre-edit-string into edit string */ - if (edit_string != _layout->get_text()) { - show_edit_status (highlight_length); - _layout->set_text (edit_string); - queue_draw (); + edit_string = pre_edit_string; + + if (input_string.empty()) { + return 0; } - return true; + 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]; +} + + +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; + } } AudioClock::Field @@ -1252,15 +1455,6 @@ AudioClock::on_button_press_event (GdkEventButton *ev) switch (ev->button) { case 1: if (editable && !_off) { - 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; - int index; int trailing; int y; @@ -1271,12 +1465,18 @@ AudioClock::on_button_press_event (GdkEventButton *ev) */ y = ev->y - ((upper_height - layout_height)/2); - x = ev->x - x_leading_padding; + x = ev->x - layout_x_offset; - if (_layout->xy_to_index (x * PANGO_SCALE, y * PANGO_SCALE, index, trailing)) { + if (_layout->xy_to_index (x * PANGO_SCALE, y * PANGO_SCALE, index, trailing)) { drag_field = index_to_field (index); - } else { - drag_field = Field (0); + 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; @@ -1302,10 +1502,36 @@ AudioClock::on_button_release_event (GdkEventButton *ev) return true; } else { if (ev->button == 1) { - start_edit (); + + 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 (); + } } } - } } @@ -1350,7 +1576,7 @@ AudioClock::on_scroll_event (GdkEventScroll *ev) */ y = ev->y - ((upper_height - layout_height)/2); - x = ev->x - x_leading_padding; + x = ev->x - layout_x_offset; if (!_layout->xy_to_index (x * PANGO_SCALE, y * PANGO_SCALE, index, trailing)) { /* not in the main layout */ @@ -1431,7 +1657,7 @@ AudioClock::on_motion_notify_event (GdkEventMotion *ev) int dir; dir = (drag_accum < 0 ? 1:-1); pos = current_time(); - frames = get_frame_step (drag_field,pos,dir); + frames = get_frame_step (drag_field, pos, dir); if (frames != 0 && frames * drag_accum < current_time()) { set ((framepos_t) floor (pos - drag_accum * frames), false); // minus because up is negative in GTK @@ -1510,7 +1736,7 @@ AudioClock::get_frame_step (Field field, framepos_t pos, int dir) } framepos_t -AudioClock::current_time (framepos_t pos) const +AudioClock::current_time (framepos_t) const { return last_when; } @@ -1545,8 +1771,14 @@ AudioClock::bbt_validate_edit (const string& str) { AnyTime any; - sscanf (str.c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, &any.bbt.bars, &any.bbt.beats, &any.bbt.ticks); - + if (sscanf (str.c_str(), BBT_SCANF_FORMAT, &any.bbt.bars, &any.bbt.beats, &any.bbt.ticks) != 3) { + return false; + } + + if (any.bbt.ticks > Timecode::BBT_Time::ticks_per_beat) { + return false; + } + if (!is_duration && any.bbt.bars == 0) { return false; } @@ -1559,7 +1791,7 @@ AudioClock::bbt_validate_edit (const string& str) } bool -AudioClock::timecode_validate_edit (const string& str) +AudioClock::timecode_validate_edit (const string&) { Timecode::Time TC; @@ -1568,16 +1800,16 @@ AudioClock::timecode_validate_edit (const string& str) return false; } - if (TC.minutes > 59 || TC.seconds > 59) { + if (TC.hours > 23U || TC.minutes > 59U || TC.seconds > 59U) { return false; } - if (TC.frames > (long)rint(_session->timecode_frames_per_second()) - 1) { + if (TC.frames > (uint32_t) rint (_session->timecode_frames_per_second()) - 1) { return false; } if (_session->timecode_drop_frames()) { - if (TC.minutes % 10 && TC.seconds == 0 && TC.frames < 2) { + if (TC.minutes % 10 && TC.seconds == 0U && TC.frames < 2U) { return false; } } @@ -1585,6 +1817,22 @@ AudioClock::timecode_validate_edit (const string& str) return true; } +bool +AudioClock::minsec_validate_edit (const string& str) +{ + int hrs, mins, secs, millisecs; + + 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; + } + + return true; +} + framepos_t AudioClock::frames_from_timecode_string (const string& str) const { @@ -1639,7 +1887,9 @@ AudioClock::frames_from_bbt_string (framepos_t pos, const string& str) const AnyTime any; any.type = AnyTime::BBT; - sscanf (str.c_str(), "%" PRId32 "|%" PRId32 "|%" PRId32, &any.bbt.bars, &any.bbt.beats, &any.bbt.ticks); + 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++; @@ -1661,8 +1911,10 @@ AudioClock::frame_duration_from_bbt_string (framepos_t pos, const string& str) c Timecode::BBT_Time bbt; - sscanf (str.c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, &bbt.bars, &bbt.beats, &bbt.ticks); - + 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); } @@ -1785,6 +2037,13 @@ AudioClock::set_mode (Mode m) 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) */ } @@ -1838,7 +2097,28 @@ AudioClock::set_off (bool yn) void AudioClock::focus () { - start_edit (); + 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 (); +}