Separate selection operations into their own temporary history mechanism.
authornick_m <mainsbridge@gmail.com>
Mon, 22 Dec 2014 13:30:23 +0000 (00:30 +1100)
committerPaul Davis <paul@linuxaudiosystems.com>
Fri, 2 Jan 2015 13:01:12 +0000 (08:01 -0500)
The user can now replay *all* earlier selection operations until the next
session undo/redo command, or the completion of a new operation.
Nothing relating to selection ops is stored, and selection operation history
is begun on first idle.

Selection operation history is fundamentally different from the history of
operations which act on a selection in terms of both their viewport and the
amount of information required to replay them.
WRT undo, the user of a selection op doesn't care about the viewport state
at the beginning of an op, but rather that at the end of the previous one.

gtk2_ardour/ardour.menus.in
gtk2_ardour/editor.cc
gtk2_ardour/editor.h
gtk2_ardour/editor_actions.cc
gtk2_ardour/editor_drag.cc
gtk2_ardour/editor_mouse.cc
gtk2_ardour/editor_ops.cc
gtk2_ardour/editor_selection.cc
gtk2_ardour/selection.cc

index 53eeb745d517467a310f0b9efa44bf7248883c7a..e713f99c6effa058eadba93819346c75eb18efd6 100644 (file)
     <menu name='Edit' action='Edit'>
       <menuitem action='undo'/>
       <menuitem action='redo'/>
+      <separator/>
+      <menuitem action='undo-last-selection-op'/>
+      <menuitem action='redo-last-selection-op'/>
+      <separator/>  
       <menuitem action='editor-cut'/>
       <menuitem action='editor-copy'/>
       <menuitem action='editor-paste'/>
index 93bbbd680575936e8f8685c0f7e011180a8e0eb7..3598642dfd0651424bc89e2531c8867f9888c3ac 100644 (file)
@@ -284,6 +284,8 @@ Editor::Editor ()
        , _tools_tearoff (0)
 
        , _toolbar_viewport (*manage (new Gtk::Adjustment (0, 0, 1e10)), *manage (new Gtk::Adjustment (0, 0, 1e10)))
+       , selection_op_cmd_depth (0)
+       , selection_op_history_it (0)
 
          /* nudge */
 
@@ -309,6 +311,7 @@ Editor::Editor ()
        selection = new Selection (this);
        cut_buffer = new Selection (this);
        _selection_memento = new SelectionMemento ();
+       selection_op_history.clear();
        before.clear();
 
        clicked_regionview = 0;
@@ -3311,6 +3314,95 @@ Editor::map_transport_state ()
 
 /* UNDO/REDO */
 
+void
+Editor::begin_selection_op_history ()
+{
+       selection_op_cmd_depth = 0;
+       selection_op_history_it = 0;
+       selection_op_history.clear();
+       selection_undo_action->set_sensitive (false);
+       selection_redo_action->set_sensitive (false);
+       selection_op_history.push_front (&_selection_memento->get_state ());
+}
+
+void
+Editor::begin_reversible_selection_op (string name)
+{
+       if (_session) {
+               //cerr << name << endl;
+               /* begin/commit pairs can be nested */
+               selection_op_cmd_depth++;
+       }
+}
+
+void
+Editor::commit_reversible_selection_op ()
+{
+       if (_session) {
+               if (selection_op_cmd_depth == 1) {
+
+                       if (selection_op_history_it > 0 && selection_op_history_it < selection_op_history.size()) {
+                               list<XMLNode *>::iterator it = selection_op_history.begin();
+                               advance (it, selection_op_history_it);
+                               selection_op_history.erase (selection_op_history.begin(), it);
+                       }
+                       selection_op_history.push_front (&_selection_memento->get_state ());
+                       selection_op_history_it = 0;
+               }
+
+               if (selection_op_cmd_depth > 0) {
+                       selection_op_cmd_depth--;
+               }
+
+               selection_undo_action->set_sensitive (true);
+               selection_redo_action->set_sensitive (false);
+       }
+}
+
+void
+Editor::undo_reversible_selection_op ()
+{
+       if (_session) {
+               selection_op_history_it++;
+               uint32_t n = 0;
+               for (std::list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
+                       if (n == selection_op_history_it) {
+                               _selection_memento->set_state (*(*i), Stateful::current_state_version);
+                               selection_redo_action->set_sensitive (true);
+                       }
+                       ++n;
+
+               }
+               /* is there an earlier entry? */
+               if ((selection_op_history_it + 1) >= selection_op_history.size()) {
+                       selection_undo_action->set_sensitive (false);
+               }
+       }
+}
+
+void
+Editor::redo_reversible_selection_op ()
+{
+       if (_session) {
+               if (selection_op_history_it > 0) {
+                       selection_op_history_it--;
+               }
+               uint32_t n = 0;
+               for (std::list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
+                       if (n == selection_op_history_it) {
+                               _selection_memento->set_state (*(*i), Stateful::current_state_version);
+                               selection_undo_action->set_sensitive (true);
+                       }
+                       ++n;
+
+               }
+
+               if (selection_op_history_it == 0) {
+                       selection_redo_action->set_sensitive (false);
+               }
+       }
+}
+
 void
 Editor::begin_reversible_command (string name)
 {
@@ -3335,9 +3427,12 @@ Editor::commit_reversible_command ()
        if (_session) {
                if (before.size() == 1) {
                        _session->add_command (new MementoCommand<SelectionMemento>(*(_selection_memento), before.front(), &_selection_memento->get_state ()));
+                       begin_selection_op_history ();
                }
 
-               if (!before.empty()) {
+               if (before.empty()) {
+                       cerr << "Please call begin_reversible_command() before commit_reversible_command()." << endl;
+               } else {
                        before.pop_back();
                }
 
@@ -4922,6 +5017,9 @@ Editor::first_idle ()
        _routes->redisplay ();
 
        delete dialog;
+
+       begin_selection_op_history ();
+
        _have_idled = true;
 }
 
index 4addfadb6045e8c1394f8e6936a745ff11d0ff6f..4740fbf2c5bb54de154b91c816bd25f90801dc91 100644 (file)
@@ -440,6 +440,11 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
                      ARDOUR::RoundMode direction = ARDOUR::RoundNearest,
                      bool              for_mark  = false);
 
+       void begin_selection_op_history ();
+       void begin_reversible_selection_op (std::string cmd_name);
+       void commit_reversible_selection_op ();
+       void undo_reversible_selection_op ();
+       void redo_reversible_selection_op ();
        void begin_reversible_command (std::string cmd_name);
        void begin_reversible_command (GQuark);
        void commit_reversible_command ();
@@ -1941,6 +1946,10 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
 
        void write_selection ();
 
+       uint32_t selection_op_cmd_depth;
+       uint32_t selection_op_history_it;
+
+       std::list<XMLNode *> selection_op_history; /* used in *_reversible_selection_op */
        std::list<XMLNode *> before; /* used in *_reversible_command */
 
        void update_title ();
@@ -2066,6 +2075,8 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
 
        Glib::RefPtr<Gtk::Action>              undo_action;
        Glib::RefPtr<Gtk::Action>              redo_action;
+       Glib::RefPtr<Gtk::Action>              selection_undo_action;
+       Glib::RefPtr<Gtk::Action>              selection_redo_action;
 
        void history_changed ();
 
index 8f24a4b91b9d52299a9b100dc830e9104b37a655..c7bda7c25e6e108cccdc168c3ff7931572b06379 100644 (file)
@@ -317,6 +317,9 @@ Editor::register_actions ()
        redo_action = reg_sens (editor_actions, "alternate-redo", _("Redo"), sigc::bind (sigc::mem_fun(*this, &Editor::redo), 1U));
        redo_action = reg_sens (editor_actions, "alternate-alternate-redo", _("Redo"), sigc::bind (sigc::mem_fun(*this, &Editor::redo), 1U));
 
+       selection_undo_action = reg_sens (editor_actions, "undo-last-selection-op", _("Undo Last Selection Op"), sigc::mem_fun(*this, &Editor::undo_reversible_selection_op));
+       selection_redo_action = reg_sens (editor_actions, "redo-last-selection-op", _("Redo Last Selection Op"), sigc::mem_fun(*this, &Editor::redo_reversible_selection_op));
+
        reg_sens (editor_actions, "export-audio", _("Export Audio"), sigc::mem_fun(*this, &Editor::export_audio));
        reg_sens (editor_actions, "export-range", _("Export Range"), sigc::mem_fun(*this, &Editor::export_range));
 
index b86c265610583d8ac28dc0125ac001fbea639eca..965092e65f2ad7da601f72fe81d6a68f56bb3baf 100644 (file)
@@ -4379,6 +4379,7 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
 {
        Session* s = _editor->session();
 
+       _editor->begin_reversible_selection_op (_("Change Time Selection"));
        if (movement_occurred) {
                motion (event, false);
                /* XXX this is not object-oriented programming at all. ick */
@@ -4435,6 +4436,7 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
 
        _editor->stop_canvas_autoscroll ();
        _editor->clicked_selection = 0;
+       _editor->commit_reversible_selection_op ();
 }
 
 void
@@ -5192,11 +5194,11 @@ EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, fram
        
        Selection::Operation op = ArdourKeyboard::selection_type (button_state);
 
-       _editor->begin_reversible_command (_("rubberband selection"));
+       _editor->begin_reversible_selection_op (_("rubberband selection"));
 
        _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
 
-       _editor->commit_reversible_command ();
+       _editor->commit_reversible_selection_op ();
 }
 
 void
index 51b6795b1a8590c6dfa241ec2744489268f604b2..e093eb5426ad54fda5a0f91bfa917d41c9ea7f93 100644 (file)
@@ -1465,7 +1465,11 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                }
 
                 /* do any (de)selection operations that should occur on button release */
+
+               begin_reversible_selection_op (_("Button Select"));
                 button_selection (item, event, item_type);
+               commit_reversible_selection_op ();
+
                return true;
                break;
 
index 625579d6a8d81b597ac87ce91762ac42ce4ec413..a99da327db93b90b050bff2a9649283e437d1d5c 100644 (file)
@@ -122,6 +122,8 @@ Editor::undo (uint32_t n)
        
        if (_session) {
                _session->undo (n);
+               redo_action->set_sensitive(true);
+               begin_selection_op_history ();
        }
 }
 
@@ -134,6 +136,11 @@ Editor::redo (uint32_t n)
        
        if (_session) {
                _session->redo (n);
+               cerr << "redo depth is : " << _session->redo_depth() << endl;
+               if (_session->redo_depth() == 0) {
+                       redo_action->set_sensitive(false);
+               }
+               begin_selection_op_history ();
        }
 }
 
index 002785e6dcf5a19f9d3ded2b8316a8b6191f10c4..962d7dc8c9f7a1a62415ae8cc595f80c7b6d33bd 100644 (file)
@@ -270,6 +270,8 @@ Editor::set_selected_track_as_side_effect (Selection::Operation op)
 void
 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
 {
+       begin_reversible_selection_op(_("Set Selected Track"));
+
        switch (op) {
        case Selection::Toggle:
                if (selection->selected (&view)) {
@@ -295,6 +297,8 @@ Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no
                extend_selection_to_track (view);
                break;
        }
+
+       commit_reversible_selection_op ();
 }
 
 void
@@ -892,7 +896,7 @@ Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> regi
                return;
        }
 
-       begin_reversible_command (_("set selected regions"));
+       begin_reversible_selection_op (_("set selected regions"));
 
        switch (op) {
        case Selection::Toggle:
@@ -910,7 +914,7 @@ Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> regi
                break;
        }
 
-       commit_reversible_command () ;
+       commit_reversible_selection_op () ;
 }
 
 bool
@@ -935,11 +939,11 @@ Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView*
                return true;
        }
 
-       begin_reversible_command (_("set selected regions"));
+       begin_reversible_selection_op (_("set selected regions"));
 
        selection->set (rv);
 
-       commit_reversible_command () ;
+       commit_reversible_selection_op () ;
 
        return true;
 }
@@ -1348,6 +1352,8 @@ Editor::select_all_in_track (Selection::Operation op)
                return;
        }
 
+       begin_reversible_selection_op(_("Select All in Track"));
+
        clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
 
        switch (op) {
@@ -1364,6 +1370,8 @@ Editor::select_all_in_track (Selection::Operation op)
                selection->add (touched);
                break;
        }
+
+       commit_reversible_selection_op ();
 }
 
 bool
@@ -1408,7 +1416,7 @@ Editor::select_all_objects (Selection::Operation op)
        }
 
 
-       begin_reversible_command (_("select all"));
+       begin_reversible_selection_op (_("select all"));
        switch (op) {
        case Selection::Add:
                selection->add (touched);
@@ -1423,7 +1431,7 @@ Editor::select_all_objects (Selection::Operation op)
                /* meaningless, because we're selecting everything */
                break;
        }
-       commit_reversible_command ();
+       commit_reversible_selection_op ();
 }
 
 void
@@ -1435,8 +1443,10 @@ Editor::invert_selection_in_track ()
                return;
        }
 
+       begin_reversible_selection_op(_("Invert Selection in Track"));
        clicked_routeview->get_inverted_selectables (*selection, touched);
        selection->set (touched);
+       commit_reversible_selection_op ();
 }
 
 void
@@ -1461,7 +1471,9 @@ Editor::invert_selection ()
                (*iter)->get_inverted_selectables (*selection, touched);
        }
 
+       begin_reversible_selection_op(_("Invert Selection"));
        selection->set (touched);
+       commit_reversible_selection_op ();
 }
 
 /** @param start Start time in session frames.
@@ -1502,7 +1514,7 @@ Editor::select_all_within (framepos_t start, framepos_t end, double top, double
                }
        }
 
-       begin_reversible_command (_("select all within"));
+       begin_reversible_selection_op (_("select all within"));
        switch (op) {
        case Selection::Add:
                selection->add (found);
@@ -1518,7 +1530,7 @@ Editor::select_all_within (framepos_t start, framepos_t end, double top, double
                break;
        }
 
-       commit_reversible_command ();
+       commit_reversible_selection_op ();
 }
 
 void
@@ -1560,9 +1572,9 @@ Editor::set_selection_from_loop()
 void
 Editor::set_selection_from_range (Location& loc)
 {
-       begin_reversible_command (_("set selection from range"));
+       begin_reversible_selection_op (_("set selection from range"));
        selection->set (loc.start(), loc.end());
-       commit_reversible_command ();
+       commit_reversible_selection_op ();
 
        if (!Profile->get_sae()) {
                set_mouse_mode (Editing::MouseRange, false);
@@ -1600,9 +1612,9 @@ Editor::select_all_selectables_using_time_selection ()
                (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
        }
 
-       begin_reversible_command (_("select all from range"));
+       begin_reversible_selection_op (_("select all from range"));
        selection->set (touched);
-       commit_reversible_command ();
+       commit_reversible_selection_op ();
 }
 
 
@@ -1631,9 +1643,9 @@ Editor::select_all_selectables_using_punch()
                }
                (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
        }
-       begin_reversible_command (_("select all from punch"));
+       begin_reversible_selection_op (_("select all from punch"));
        selection->set (touched);
-       commit_reversible_command ();
+       commit_reversible_selection_op ();
 
 }
 
@@ -1662,9 +1674,9 @@ Editor::select_all_selectables_using_loop()
                }
                (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
        }
-       begin_reversible_command (_("select all from loop"));
+       begin_reversible_selection_op (_("select all from loop"));
        selection->set (touched);
-       commit_reversible_command ();
+       commit_reversible_selection_op ();
 
 }
 
@@ -1698,9 +1710,9 @@ Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
        }
 
        if (after) {
-               begin_reversible_command (_("select all after cursor"));
+               begin_reversible_selection_op (_("select all after cursor"));
        } else {
-               begin_reversible_command (_("select all before cursor"));
+               begin_reversible_selection_op (_("select all before cursor"));
        }
 
        TrackViewList* ts;
@@ -1718,7 +1730,7 @@ Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
                (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
        }
        selection->set (touched);
-       commit_reversible_command ();
+       commit_reversible_selection_op ();
 }
 
 void
@@ -1749,9 +1761,9 @@ Editor::select_all_selectables_using_edit (bool after)
        }
 
        if (after) {
-               begin_reversible_command (_("select all after edit"));
+               begin_reversible_selection_op (_("select all after edit"));
        } else {
-               begin_reversible_command (_("select all before edit"));
+               begin_reversible_selection_op (_("select all before edit"));
        }
 
        TrackViewList* ts;
@@ -1769,7 +1781,7 @@ Editor::select_all_selectables_using_edit (bool after)
                (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
        }
        selection->set (touched);
-       commit_reversible_command ();
+       commit_reversible_selection_op ();
 }
 
 void
@@ -1806,7 +1818,9 @@ Editor::select_all_selectables_between (bool /*within*/)
                (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
        }
 
+       begin_reversible_selection_op(_("Select all Selectables Between"));
        selection->set (touched);
+       commit_reversible_selection_op ();
 }
 
 void
@@ -1823,8 +1837,10 @@ Editor::select_range_between ()
                return;
        }
 
+       begin_reversible_selection_op(_("Select Range Between"));
        set_mouse_mode (MouseRange);
        selection->set (start, end);
+       commit_reversible_selection_op ();
 }
 
 bool
@@ -1931,13 +1947,18 @@ Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
 void
 Editor::deselect_all ()
 {
+       begin_reversible_selection_op(_("Clear Selection"));
        selection->clear ();
+       commit_reversible_selection_op ();
 }
 
 long
 Editor::select_range (framepos_t s, framepos_t e)
 {
+       begin_reversible_selection_op(_("Select Range"));
        selection->add (clicked_axisview);
        selection->time.clear ();
-       return selection->set (s, e);
+       long ret = selection->set (s, e);
+       commit_reversible_selection_op ();
+       return ret;
 }
index ae00c4f8c4e7af1c89b387ae07fa29fef1d0beb9..6d07564f2cb276c9b6920af188f8ca0f386ce493 100644 (file)
@@ -1261,6 +1261,7 @@ Selection::set_state (XMLNode const & node, int)
        }
 
        clear_regions ();
+       clear_points ();
        clear_time ();
        clear_tracks ();
        clear_markers ();