X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Faudio_clock.cc;h=fd8aca8df18466016f9494a761bc093b22b59ef1;hb=611dcdd24932222d676da4d9a4dca643f79db4a4;hp=57abbe325c30bfbc16dfe7569ff34a65772ce9f7;hpb=ae09a5812b665483daa04adb790ab2f2bd32a29b;p=ardour.git diff --git a/gtk2_ardour/audio_clock.cc b/gtk2_ardour/audio_clock.cc index 57abbe325c..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" @@ -62,7 +61,8 @@ const double AudioClock::x_leading_padding = 6.0; 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) @@ -70,7 +70,8 @@ AudioClock::AudioClock (const string& clock_name, bool transient, const string& , _off (false) , _fixed_width (true) , layout_x_offset (0) - , ops_menu (0) + , em_width (0) + , _edit_by_click_field (false) , editing_attr (0) , foreground_attr (0) , first_height (0) @@ -171,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 @@ -332,24 +344,39 @@ AudioClock::render (cairo_t* 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, cursor_r, cursor_g, cursor_b, cursor_a); - if (!_fixed_width) { - cairo_rectangle (cr, - layout_x_offset + cursor.get_x()/PANGO_SCALE + cursor_width, - 0, - 2.0, cursor.get_height()/PANGO_SCALE); + + + 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 { - cairo_rectangle (cr, - layout_x_offset + cursor.get_x()/PANGO_SCALE + cursor_width, - (upper_height - layout_height)/2.0, - 2.0, cursor.get_height()/PANGO_SCALE); + /* we've entered all possible digits, no cursor */ } - cairo_fill (cr); + } else { if (input_string.empty()) { cairo_set_source_rgba (cr, cursor_r, cursor_g, cursor_b, cursor_a); @@ -388,7 +415,6 @@ AudioClock::on_size_allocate (Gtk::Allocation& alloc) /* left justify */ layout_x_offset = 0; } - } void @@ -495,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()) { @@ -507,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) { @@ -530,6 +606,7 @@ AudioClock::end_edit (bool modify) break; case MinSec: + ok = minsec_validate_edit (edit_string); break; case Frames: @@ -718,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; } @@ -726,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; @@ -752,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; @@ -900,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; @@ -945,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; @@ -1011,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; @@ -1070,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); } } @@ -1110,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; @@ -1161,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: @@ -1230,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: @@ -1238,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 @@ -1358,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; @@ -1379,10 +1467,16 @@ AudioClock::on_button_press_event (GdkEventButton *ev) 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)) { + 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; @@ -1408,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 (); + } } } - } } @@ -1537,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 @@ -1616,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; } @@ -1654,7 +1774,11 @@ AudioClock::bbt_validate_edit (const string& str) 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; } @@ -1667,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; @@ -1676,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; } } @@ -1693,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 { @@ -1771,10 +1911,10 @@ AudioClock::frame_duration_from_bbt_string (framepos_t pos, const string& str) c Timecode::BBT_Time bbt; - if (sscanf (str.c_str(), BBT_SCANF_FORMAT, &bbt.bars, &bbt.beats, &bbt.ticks) != 0) { + 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); } @@ -1957,7 +2097,7 @@ AudioClock::set_off (bool yn) void AudioClock::focus () { - start_edit (); + start_edit (Field (0)); } void