use syntactic sugar
[ardour.git] / gtk2_ardour / editor.cc
index a8b40087382f6d0e0caa3395ade4727822efd73c..3f78e49ca775802169b9a9398056c12850737952 100644 (file)
@@ -448,7 +448,6 @@ Editor::Editor ()
        , _stepping_axis_view (0)
        , quantize_dialog (0)
        , _main_menu_disabler (0)
-       , myactions (X_("editor"))
 {
        /* we are a singleton */
 
@@ -775,8 +774,8 @@ Editor::Editor ()
 
        /* register actions now so that set_state() can find them and set toggles/checks etc */
 
-       register_actions ();
        load_bindings ();
+       register_actions ();
 
        setup_toolbar ();
 
@@ -861,8 +860,6 @@ Editor::Editor ()
        setup_fade_images ();
 
        set_grid_to (GridTypeNone);
-
-       instant_save ();
 }
 
 Editor::~Editor()
@@ -984,15 +981,11 @@ Editor::set_entered_track (TimeAxisView* tav)
 void
 Editor::instant_save ()
 {
-       if (!constructed || !ARDOUR_UI::instance()->session_loaded || no_save_instant) {
+       if (!constructed || !_session || no_save_instant) {
                return;
        }
 
-       if (_session) {
-               _session->add_instant_xml(get_state());
-       } else {
-               Config->add_instant_xml(get_state());
-       }
+       _session->add_instant_xml(get_state());
 }
 
 void
@@ -2208,41 +2201,15 @@ Editor::snap_mode() const
 }
 
 void
-Editor::set_grid_to (GridType gt)
+Editor::show_rulers_for_grid ()
 {
-       if (_grid_type == gt) { // already set
-               return;
-       }
-
-       unsigned int grid_ind = (unsigned int)gt;
-
-       if (internal_editing() && UIConfiguration::instance().get_grid_follows_internal()) {
-               internal_grid_type = gt;
-       } else {
-               pre_internal_grid_type = gt;
-       }
-
-       _grid_type = gt;
-
-       if (grid_ind > grid_type_strings.size() - 1) {
-               grid_ind = 0;
-               _grid_type = (GridType)grid_ind;
-       }
-
-       string str = grid_type_strings[grid_ind];
-
-       if (str != grid_type_selector.get_text()) {
-               grid_type_selector.set_text (str);
-       }
-
-       /* show appropriate rulers for this grid setting.
-        */
+       /* show appropriate rulers for this grid setting. */
        if (grid_musical()) {
                ruler_tempo_action->set_active(true);
                ruler_meter_action->set_active(true);
                ruler_bbt_action->set_active(true);
 
-               if ( UIConfiguration::instance().get_rulers_follow_grid() ) {
+               if (UIConfiguration::instance().get_rulers_follow_grid()) {
                        ruler_timecode_action->set_active(false);
                        ruler_minsec_action->set_active(false);
                        ruler_samples_action->set_active(false);
@@ -2250,7 +2217,7 @@ Editor::set_grid_to (GridType gt)
        } else if (_grid_type == GridTypeTimecode) {
                ruler_timecode_action->set_active(true);
 
-               if ( UIConfiguration::instance().get_rulers_follow_grid() ) {
+               if (UIConfiguration::instance().get_rulers_follow_grid()) {
                        ruler_tempo_action->set_active(false);
                        ruler_meter_action->set_active(false);
                        ruler_bbt_action->set_active(false);
@@ -2260,7 +2227,7 @@ Editor::set_grid_to (GridType gt)
        } else if (_grid_type == GridTypeMinSec) {
                ruler_minsec_action->set_active(true);
 
-               if ( UIConfiguration::instance().get_rulers_follow_grid() ) {
+               if (UIConfiguration::instance().get_rulers_follow_grid()) {
                        ruler_tempo_action->set_active(false);
                        ruler_meter_action->set_active(false);
                        ruler_bbt_action->set_active(false);
@@ -2271,7 +2238,7 @@ Editor::set_grid_to (GridType gt)
                ruler_cd_marker_action->set_active(true);
                ruler_minsec_action->set_active(true);
 
-               if ( UIConfiguration::instance().get_rulers_follow_grid() ) {
+               if (UIConfiguration::instance().get_rulers_follow_grid()) {
                        ruler_tempo_action->set_active(false);
                        ruler_meter_action->set_active(false);
                        ruler_bbt_action->set_active(false);
@@ -2279,6 +2246,39 @@ Editor::set_grid_to (GridType gt)
                        ruler_samples_action->set_active(false);
                }
        }
+}
+
+void
+Editor::set_grid_to (GridType gt)
+{
+       if (_grid_type == gt) { // already set
+               return;
+       }
+
+       unsigned int grid_ind = (unsigned int)gt;
+
+       if (internal_editing() && UIConfiguration::instance().get_grid_follows_internal()) {
+               internal_grid_type = gt;
+       } else {
+               pre_internal_grid_type = gt;
+       }
+
+       _grid_type = gt;
+
+       if (grid_ind > grid_type_strings.size() - 1) {
+               grid_ind = 0;
+               _grid_type = (GridType)grid_ind;
+       }
+
+       string str = grid_type_strings[grid_ind];
+
+       if (str != grid_type_selector.get_text()) {
+               grid_type_selector.set_text (str);
+       }
+
+       if (UIConfiguration::instance().get_show_grids_ruler()) {
+               show_rulers_for_grid ();
+       }
 
        instant_save ();
 
@@ -2571,7 +2571,7 @@ Editor::set_state (const XMLNode& node, int version)
                }
        }
 
-       return LuaInstance::instance()->set_state(node);
+       return 0;
 }
 
 XMLNode&
@@ -2639,8 +2639,6 @@ Editor::get_state ()
 
        node->set_property ("nudge-clock-value", nudge_clock->current_duration());
 
-       node->add_child_nocopy (LuaInstance::instance()->get_action_state());
-       node->add_child_nocopy (LuaInstance::instance()->get_hook_state());
        node->add_child_nocopy (_locations->get_state ());
 
        return *node;
@@ -2692,7 +2690,7 @@ Editor::set_snapped_cursor_position (samplepos_t pos)
  *  @param event Event to get current key modifier information from, or 0.
  */
 void
-Editor::snap_to_with_modifier (MusicSample& start, GdkEvent const * event, RoundMode direction, SnapPref pref, bool for_mark)
+Editor::snap_to_with_modifier (MusicSample& start, GdkEvent const * event, RoundMode direction, SnapPref pref)
 {
        if (!_session || !event) {
                return;
@@ -2700,16 +2698,16 @@ Editor::snap_to_with_modifier (MusicSample& start, GdkEvent const * event, Round
 
        if (ArdourKeyboard::indicates_snap (event->button.state)) {
                if (_snap_mode == SnapOff) {
-                       snap_to_internal (start, direction, pref, for_mark);
+                       snap_to_internal (start, direction, pref);
                } else {
                        start.set (start.sample, 0);
                }
        } else {
                if (_snap_mode != SnapOff) {
-                       snap_to_internal (start, direction, pref, for_mark);
+                       snap_to_internal (start, direction, pref);
                } else if (ArdourKeyboard::indicates_snap_delta (event->button.state)) {
                        /* SnapOff, but we pressed the snap_delta modifier */
-                       snap_to_internal (start, direction, pref, for_mark);
+                       snap_to_internal (start, direction, pref);
                } else {
                        start.set (start.sample, 0);
                }
@@ -2717,14 +2715,14 @@ Editor::snap_to_with_modifier (MusicSample& start, GdkEvent const * event, Round
 }
 
 void
-Editor::snap_to (MusicSample& start, RoundMode direction, SnapPref pref, bool for_mark, bool ensure_snap)
+Editor::snap_to (MusicSample& start, RoundMode direction, SnapPref pref, bool ensure_snap)
 {
        if (!_session || (_snap_mode == SnapOff && !ensure_snap)) {
                start.set (start.sample, 0);
                return;
        }
 
-       snap_to_internal (start, direction, pref, for_mark, ensure_snap);
+       snap_to_internal (start, direction, pref, ensure_snap);
 }
 
 void
@@ -2739,61 +2737,239 @@ check_best_snap (samplepos_t presnap, samplepos_t &test, samplepos_t &dist, samp
        test = max_samplepos; // reset this so it doesn't get accidentally reused
 }
 
-samplepos_t
-Editor::snap_to_grid (vector<ArdourCanvas::Ruler::Mark> marks, samplepos_t presnap, RoundMode direction)
+MusicSample
+Editor::snap_to_timecode (MusicSample presnap, RoundMode direction, SnapPref gpref)
 {
-       if (marks.empty()) return presnap;
+       samplepos_t start = presnap.sample;
+       const samplepos_t one_timecode_second = (samplepos_t)(rint(_session->timecode_frames_per_second()) * _session->samples_per_timecode_frame());
+       samplepos_t one_timecode_minute = (samplepos_t)(rint(_session->timecode_frames_per_second()) * _session->samples_per_timecode_frame() * 60);
 
-       samplepos_t before;
-       samplepos_t after;
-       samplepos_t test = presnap;
+       TimecodeRulerScale scale = (gpref != SnapToGrid_Unscaled) ? timecode_ruler_scale : timecode_show_samples;
 
-       before = after = max_samplepos;
+       switch (scale) {
+       case timecode_show_bits:
+       case timecode_show_samples:
+               if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
+                   fmod((double)start, (double)_session->samples_per_timecode_frame()) == 0) {
+                       /* start is already on a whole timecode frame, do nothing */
+               } else if (((direction == 0) && (fmod((double)start, (double)_session->samples_per_timecode_frame()) > (_session->samples_per_timecode_frame() / 2))) || (direction > 0)) {
+                       start = (samplepos_t) (ceil ((double) start / _session->samples_per_timecode_frame()) * _session->samples_per_timecode_frame());
+               } else {
+                       start = (samplepos_t) (floor ((double) start / _session->samples_per_timecode_frame()) *  _session->samples_per_timecode_frame());
+               }
+               break;
 
-       /* get marks to either side of presnap */
-       vector<ArdourCanvas::Ruler::Mark>::const_iterator m = marks.begin();
-       while (m != marks.end() && (m->position < presnap)) {
-               ++m;
-       }
+       case timecode_show_seconds:
+               if (_session->config.get_timecode_offset_negative()) {
+                       start += _session->config.get_timecode_offset ();
+               } else {
+                       start -= _session->config.get_timecode_offset ();
+               }
+               if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
+                   (start % one_timecode_second == 0)) {
+                       /* start is already on a whole second, do nothing */
+               } else if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
+                       start = (samplepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
+               } else {
+                       start = (samplepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
+               }
+
+               if (_session->config.get_timecode_offset_negative()) {
+                       start -= _session->config.get_timecode_offset ();
+               } else {
+                       start += _session->config.get_timecode_offset ();
+               }
+               break;
 
-       if (m == marks.end ()) {
-               /* ran out of marks */
-               before = marks.back().position;
+       case timecode_show_minutes:
+       case timecode_show_hours:
+       case timecode_show_many_hours:
+               if (_session->config.get_timecode_offset_negative()) {
+                       start += _session->config.get_timecode_offset ();
+               } else {
+                       start -= _session->config.get_timecode_offset ();
+               }
+               if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
+                   (start % one_timecode_minute == 0)) {
+                       /* start is already on a whole minute, do nothing */
+               } else if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
+                       start = (samplepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
+               } else {
+                       start = (samplepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
+               }
+               if (_session->config.get_timecode_offset_negative()) {
+                       start -= _session->config.get_timecode_offset ();
+               } else {
+                       start += _session->config.get_timecode_offset ();
+               }
+               break;
+       default:
+               fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
        }
 
-       after = m->position;
+       MusicSample ret(start,0);
+       return ret;
+}
 
-       if (m != marks.begin ()) {
-               --m;
-               before = m->position;
-       }
+MusicSample
+Editor::snap_to_minsec (MusicSample presnap, RoundMode direction, SnapPref gpref)
+{
+       MusicSample ret(presnap);
+       
+       const samplepos_t one_second = _session->sample_rate();
+       const samplepos_t one_minute = one_second * 60;
+       const samplepos_t one_hour = one_minute * 60;
 
-       if (before == max_samplepos && after == max_samplepos) {
-               /* No grid to snap to, so just don't snap */
-               return presnap;
-       } else if (before == max_samplepos) {
-               test = after;
-       } else if (after == max_samplepos) {
-               test = before;
-       } else  {
-               if ((direction == RoundUpMaybe || direction == RoundUpAlways))
-                       test = after;
-               else if ((direction == RoundDownMaybe || direction == RoundDownAlways))
-                       test = before;
-               else if (direction ==  0) {
-                       if ((presnap - before) < (after - presnap)) {
-                               test = before;
+       MinsecRulerScale scale = (gpref != SnapToGrid_Unscaled) ? minsec_ruler_scale : minsec_show_seconds;
+
+       switch (scale) {
+               case minsec_show_msecs:
+               case minsec_show_seconds: {
+                       if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
+                               presnap.sample % one_second == 0) {
+                               /* start is already on a whole second, do nothing */
+                       } else if (((direction == 0) && (presnap.sample % one_second > one_second / 2)) || (direction > 0)) {
+                               ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_second) * one_second;
                        } else {
-                               test = after;
+                               ret.sample = (samplepos_t) floor ((double) presnap.sample / one_second) * one_second;
+                       }
+               } break;
+
+               case minsec_show_minutes: {
+                       if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
+                               presnap.sample % one_minute == 0) {
+                               /* start is already on a whole minute, do nothing */
+                       } else if (((direction == 0) && (presnap.sample % one_minute > one_minute / 2)) || (direction > 0)) {
+                               ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_minute) * one_minute;
+                       } else {
+                               ret.sample = (samplepos_t) floor ((double) presnap.sample / one_minute) * one_minute;
+                       }
+               } break;
+
+               default: {
+                       if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
+                               presnap.sample % one_hour == 0) {
+                               /* start is already on a whole hour, do nothing */
+                       } else if (((direction == 0) && (presnap.sample % one_hour > one_hour / 2)) || (direction > 0)) {
+                               ret.sample = (samplepos_t) ceil ((double) presnap.sample / one_hour) * one_hour;
+                       } else {
+                               ret.sample = (samplepos_t) floor ((double) presnap.sample / one_hour) * one_hour;
                        }
+               } break;
+       }
+       
+       return ret;
+}
+
+MusicSample
+Editor::snap_to_cd_frames (MusicSample presnap, RoundMode direction, SnapPref gpref)
+{
+       if ((gpref != SnapToGrid_Unscaled) && (minsec_ruler_scale != minsec_show_msecs)) {
+               return snap_to_minsec (presnap, direction, gpref);
+       }       
+       
+       const samplepos_t one_second = _session->sample_rate();
+
+       MusicSample ret(presnap);
+       
+       if ((direction == RoundUpMaybe || direction == RoundDownMaybe) &&
+               presnap.sample % (one_second/75) == 0) {
+               /* start is already on a whole CD sample, do nothing */
+       } else if (((direction == 0) && (presnap.sample % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
+               ret.sample = (samplepos_t) ceil ((double) presnap.sample / (one_second / 75)) * (one_second / 75);
+       } else {
+               ret.sample = (samplepos_t) floor ((double) presnap.sample / (one_second / 75)) * (one_second / 75);
+       }
+
+       return ret;
+}
+
+MusicSample
+Editor::snap_to_bbt (MusicSample presnap, RoundMode direction, SnapPref gpref)
+{
+       MusicSample ret(presnap);
+       
+       if (gpref != SnapToGrid_Unscaled) { // use the visual grid lines which are limited by the zoom scale that the user selected
+
+               int divisor = 2;
+               switch (_grid_type) {
+               case GridTypeBeatDiv3:
+               case GridTypeBeatDiv6:
+               case GridTypeBeatDiv12:
+               case GridTypeBeatDiv24:
+                       divisor = 3;
+                       break;
+               case GridTypeBeatDiv5:
+               case GridTypeBeatDiv10:
+               case GridTypeBeatDiv20:
+                       divisor = 5;
+                       break;
+               case GridTypeBeatDiv7:
+               case GridTypeBeatDiv14:
+               case GridTypeBeatDiv28:
+                       divisor = 7;
+                       break;
+               default:
+                       divisor = 2;
+               };
+
+               BBTRulerScale scale = bbt_ruler_scale;
+               switch (scale) {
+                       case bbt_show_many:
+                       case bbt_show_64:
+                       case bbt_show_16:
+                       case bbt_show_4:
+                       case bbt_show_1:
+                               ret = _session->tempo_map().round_to_bar (presnap.sample, direction);
+                               break;
+                       case bbt_show_quarters:
+                               ret = _session->tempo_map().round_to_beat (presnap.sample, direction);
+                               break;
+                       case bbt_show_eighths:
+                               ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 1 * divisor, direction);
+                               break;
+                       case bbt_show_sixteenths:
+                               ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 2 * divisor, direction);
+                               break;
+                       case bbt_show_thirtyseconds:
+                               ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, 4 * divisor, direction);
+                               break;
                }
+       } else {
+               ret = _session->tempo_map().round_to_quarter_note_subdivision (presnap.sample, get_grid_beat_divisions(_grid_type), direction);
        }
+       
+       return ret;
+}
 
-       return test;
+ARDOUR::MusicSample
+Editor::snap_to_grid (MusicSample presnap, RoundMode direction, SnapPref gpref)
+{
+       MusicSample ret(presnap);
+       
+       if (grid_musical()) {
+               ret = snap_to_bbt (presnap, direction, gpref);
+       }
+       
+       switch (_grid_type) {
+               case GridTypeTimecode:
+                       ret = snap_to_timecode(presnap, direction, gpref);
+                       break;
+               case GridTypeMinSec:
+                       ret = snap_to_minsec(presnap, direction, gpref);
+                       break;
+               case GridTypeCDFrame:
+                       ret = snap_to_cd_frames(presnap, direction, gpref);
+                       break;
+               default:
+                       {}
+       };
+
+       return ret;
 }
 
 samplepos_t
-Editor::marker_snap_to_internal (samplepos_t presnap, RoundMode direction)
+Editor::snap_to_marker (samplepos_t presnap, RoundMode direction)
 {
        samplepos_t before;
        samplepos_t after;
@@ -2826,7 +3002,7 @@ Editor::marker_snap_to_internal (samplepos_t presnap, RoundMode direction)
 }
 
 void
-Editor::snap_to_internal (MusicSample& start, RoundMode direction, SnapPref pref, bool for_mark, bool ensure_snap)
+Editor::snap_to_internal (MusicSample& start, RoundMode direction, SnapPref pref, bool ensure_snap)
 {
        const samplepos_t presnap = start.sample;
 
@@ -2835,18 +3011,14 @@ Editor::snap_to_internal (MusicSample& start, RoundMode direction, SnapPref pref
        samplepos_t best = max_samplepos; // this records the best snap-result we've found so far
 
        /* check snap-to-marker */
-       if ( (pref == SnapToAny) && UIConfiguration::instance().get_snap_to_marks()) {
-               if (for_mark) {
-                       return;
-               }
-
-               test = marker_snap_to_internal (presnap, direction);
+       if ((pref == SnapToAny_Visual) && UIConfiguration::instance().get_snap_to_marks()) {
+               test = snap_to_marker (presnap, direction);
                check_best_snap(presnap, test, dist, best);
        }
 
        /* check snap-to-region-{start/end/sync} */
        if (
-               (pref == SnapToAny) &&
+               (pref == SnapToAny_Visual) &&
                (UIConfiguration::instance().get_snap_to_region_start() || UIConfiguration::instance().get_snap_to_region_end() || UIConfiguration::instance().get_snap_to_region_sync())
                ) {
                if (!region_boundary_cache.empty()) {
@@ -2877,8 +3049,9 @@ Editor::snap_to_internal (MusicSample& start, RoundMode direction, SnapPref pref
 
        /* check Grid */
        if (UIConfiguration::instance().get_snap_to_grid() && (_grid_type != GridTypeNone)) {
-               test = snap_to_grid (grid_marks, presnap, direction);
-               check_best_snap(presnap, test, dist, best);
+               MusicSample pre(presnap, 0);
+               MusicSample post = snap_to_grid (pre, direction, pref);
+               check_best_snap(presnap, post.sample, dist, best);
        }
 
        /* now check "magnetic" state: is the grid within reasonable on-screen distance to trigger a snap?
@@ -3989,6 +4162,8 @@ Editor::get_grid_beat_divisions(samplepos_t position)
        case GridTypeBeatDiv4:   return 4;
        case GridTypeBeatDiv3:   return 3;
        case GridTypeBeatDiv2:   return 2;
+       case GridTypeBeat:       return 1;
+       case GridTypeBar:        return 1;
 
        case GridTypeNone:       return 0;
        case GridTypeTimecode:   return 0;
@@ -5758,7 +5933,7 @@ Editor::super_rapid_screen_update ()
                _last_update_time = 0;
        }
 
-       if (!_session->transport_rolling ()) {
+       if (!_session->transport_rolling () || _session->is_auditioning ()) {
                /* Do not interpolate the playhead position; just set it */
                _last_update_time = 0;
        }