allow resizing of startup wizard window
[ardour.git] / gtk2_ardour / audio_clock.cc
index 47f17e5f4e9d15a6a86d2e3990cf96ef73915c68..fd8aca8df18466016f9494a761bc093b22b59ef1 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)
@@ -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<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
@@ -332,8 +344,6 @@ AudioClock::render (cairo_t* cr)
        }
 
        if (editing) {
-               const double cursor_width = 12; /* need em width here, not 16 */
-
                if (!insert_map.empty()) {
 
 
@@ -352,12 +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, 
-                                                        layout_x_offset + cursor.get_x()/PANGO_SCALE + cursor_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, 
-                                                        layout_x_offset + cursor.get_x()/PANGO_SCALE + cursor_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);
                                }
@@ -404,7 +415,6 @@ AudioClock::on_size_allocate (Gtk::Allocation& alloc)
                /* left justify */
                layout_x_offset = 0;
        }
-
 }
 
 void
@@ -511,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()) {
@@ -523,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)
 {
@@ -546,6 +606,7 @@ AudioClock::end_edit (bool modify)
                        break;
                        
                case MinSec:
+                       ok = minsec_validate_edit (edit_string);
                        break;
                        
                case Frames:
@@ -734,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;
 }
@@ -762,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;
@@ -910,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;
@@ -955,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;
@@ -1021,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;
@@ -1080,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);
        }
 }
@@ -1127,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:
@@ -1216,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];
@@ -1238,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 ();
@@ -1265,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)
@@ -1363,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;
@@ -1384,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;
@@ -1413,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 ();
+                                       }
                                }
                        }
-
                }
        }
 
@@ -1542,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
@@ -1621,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;
 }
@@ -1659,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;
        }
@@ -1672,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;
 
@@ -1681,11 +1800,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;
        }
 
@@ -1698,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
 {
@@ -1776,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);
 }
 
@@ -1962,7 +2097,7 @@ AudioClock::set_off (bool yn)
 void
 AudioClock::focus ()
 {
-       start_edit ();
+       start_edit (Field (0));
 }
 
 void