Make send automation work (#4734).
[ardour.git] / gtk2_ardour / audio_clock.cc
index eb41ef319d113788e0369999b07f1f2964163890..dbe7f03a1453ecbeca417115694fb28255a75d5a 100644 (file)
 #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"
@@ -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)
@@ -71,7 +71,7 @@ AudioClock::AudioClock (const string& clock_name, bool transient, const string&
        , _fixed_width (true)
        , layout_x_offset (0)
        , em_width (0)
-       , ops_menu (0)
+       , _edit_by_click_field (false)
        , editing_attr (0)
        , foreground_attr (0)
        , first_height (0)
@@ -362,11 +362,13 @@ AudioClock::render (cairo_t* cr)
                                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 + em_width), 0,
+                                                        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),
+                                                        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);
                                }
@@ -519,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()) {
@@ -531,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)
 {
@@ -554,6 +606,7 @@ AudioClock::end_edit (bool modify)
                        break;
                        
                case MinSec:
+                       ok = minsec_validate_edit (edit_string);
                        break;
                        
                case Frames:
@@ -742,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;
 }
@@ -770,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;
@@ -918,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;
@@ -963,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;
@@ -1029,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;
@@ -1088,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);
        }
 }
@@ -1135,6 +1217,8 @@ AudioClock::on_key_press_event (GdkEventKey* ev)
 
        string new_text;
        char new_char = 0;
+       int highlight_length;
+       framepos_t pos;
 
        switch (ev->keyval) {
        case GDK_0:
@@ -1224,18 +1308,13 @@ AudioClock::on_key_press_event (GdkEventKey* ev)
 
   use_input_string:
 
-       int highlight_length = 0;
-       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];
@@ -1246,26 +1325,11 @@ AudioClock::on_key_press_event (GdkEventKey* ev)
                /* highlight the whole thing */
                highlight_length = edit_string.length();
                break;
-               
-       default:
-               edit_string = pre_edit_string;
 
-               if (input_string.empty()) {
-                       highlight_length = 0;
-               } else {
-                       // for (int i = input_string.length() - 1; i >= 0; --i) {
-                       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 */
-                       highlight_length = edit_string.length() - insert_map[input_string.length()-1];
-               }
-               break;
+       default:
+               highlight_length = merge_input_and_edit_string ();
        }
-       
-       
+
        show_edit_status (highlight_length);
        _layout->set_text (edit_string);
        queue_draw ();
@@ -1273,6 +1337,26 @@ AudioClock::on_key_press_event (GdkEventKey* ev)
        return true;
 }
 
+int
+AudioClock::merge_input_and_edit_string ()
+{
+       /* 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];
+}
+
 
 bool
 AudioClock::on_key_release_event (GdkEventKey *ev)
@@ -1421,10 +1505,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 ();
+                                       }
                                }
                        }
-
                }
        }
 
@@ -1629,7 +1739,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;
 }
@@ -1667,7 +1777,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;
        }
@@ -1680,7 +1794,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;
 
@@ -1689,11 +1803,11 @@ AudioClock::timecode_validate_edit (const string& str)
                return false;
        }
 
-       if (TC.minutes > 59U || TC.seconds > 59U) {
+       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;
        }
 
@@ -1706,6 +1820,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
 {
@@ -1784,10 +1914,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);
 }
 
@@ -1970,7 +2100,7 @@ AudioClock::set_off (bool yn)
 void
 AudioClock::focus ()
 {
-       start_edit ();
+       start_edit (Field (0));
 }
 
 void