#include "pbd/enumwriter.h"
#include <gtkmm/style.h>
+#include <sigc++/bind.h>
#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 <sigc++/bind.h>
#include "ardour_ui.h"
#include "audio_clock.h"
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)
, _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)
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<Pango::Layout> tmp = Pango::Layout::create (get_pango_context());
+ int ignore_height;
+
+ tmp->set_text ("8");
+ tmp->get_pixel_size (em_width, ignore_height);
}
void
}
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);
/* left justify */
layout_x_offset = 0;
}
-
}
void
}
void
-AudioClock::start_edit ()
+AudioClock::start_edit (Field f)
{
pre_edit_string = _layout->get_text ();
if (!insert_map.empty()) {
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)
{
break;
case MinSec:
+ ok = minsec_validate_edit (edit_string);
break;
case Frames:
}
framecnt_t
-AudioClock::parse_as_bbt_distance (const std::string& str)
+AudioClock::parse_as_bbt_distance (const std::string&)
{
return 0;
}
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;
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;
}
void
-AudioClock::set_minsec (framepos_t when, bool force)
+AudioClock::set_minsec (framepos_t when, bool /*force*/)
{
char buf[32];
framecnt_t left;
}
void
-AudioClock::set_timecode (framepos_t when, bool force)
+AudioClock::set_timecode (framepos_t when, bool /*force*/)
{
char buf[32];
Timecode::Time TC;
}
void
-AudioClock::set_bbt (framepos_t when, bool force)
+AudioClock::set_bbt (framepos_t when, bool /*force*/)
{
char buf[16];
Timecode::BBT_Time BBT;
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);
}
}
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;
string new_text;
char new_char = 0;
+ int highlight_length;
+ framepos_t pos;
switch (ev->keyval) {
case GDK_0:
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:
}
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<int> 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
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;
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;
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 ();
+ }
}
}
-
}
}
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
}
framepos_t
-AudioClock::current_time (framepos_t pos) const
+AudioClock::current_time (framepos_t) const
{
return last_when;
}
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;
}
}
bool
-AudioClock::timecode_validate_edit (const string& str)
+AudioClock::timecode_validate_edit (const string&)
{
Timecode::Time TC;
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;
}
}
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
{
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);
}
void
AudioClock::focus ()
{
- start_edit ();
+ start_edit (Field (0));
}
void