#include "gui_thread.h"
#include "keyboard.h"
#include "marker.h"
+#include "midi_region_view.h"
#include "midi_time_axis.h"
#include "mixer_strip.h"
#include "mixer_ui.h"
#include "mouse_cursors.h"
+#include "note_base.h"
#include "playlist_selector.h"
#include "public_editor.h"
#include "region_layering_order_editor.h"
#include "sfdb_ui.h"
#include "tempo_lines.h"
#include "time_axis_view.h"
+#include "timers.h"
#include "utils.h"
#include "verbose_cursor.h"
using PBD::atoi;
using Gtkmm2ext::Keyboard;
-const double Editor::timebar_height = 15.0;
+double Editor::timebar_height = 15.0;
static const gchar *_snap_type_strings[] = {
N_("CD Frames"),
, _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 */
selection = new Selection (this);
cut_buffer = new Selection (this);
+ _selection_memento = new SelectionMemento ();
+ selection_op_history.clear();
+ before.clear();
clicked_regionview = 0;
clicked_axisview = 0;
clicked_routeview = 0;
clicked_control_point = 0;
last_update_frame = 0;
- pre_press_cursor = 0;
last_paste_pos = 0;
paste_count = 0;
_drags = new DragManager (this);
sfbrowser = 0;
- location_marker_color = ARDOUR_UI::config()->get_LocationMarker();
- location_range_color = ARDOUR_UI::config()->get_LocationRange();
- location_cd_marker_color = ARDOUR_UI::config()->get_LocationCDMarker();
- location_loop_color = ARDOUR_UI::config()->get_LocationLoop();
- location_punch_color = ARDOUR_UI::config()->get_LocationPunch();
+ location_marker_color = ARDOUR_UI::config()->color ("location marker");
+ location_range_color = ARDOUR_UI::config()->color ("location range");
+ location_cd_marker_color = ARDOUR_UI::config()->color ("location cd marker");
+ location_loop_color = ARDOUR_UI::config()->color ("location loop");
+ location_punch_color = ARDOUR_UI::config()->color ("location punch");
zoom_focus = ZoomFocusLeft;
_edit_point = EditAtMouse;
- _internal_editing = false;
- current_canvas_cursor = 0;
_visible_track_count = -1;
samples_per_pixel = 2048; /* too early to use reset_zoom () */
+ timebar_height = std::max(12., ceil (15. * ARDOUR_UI::config()->get_font_scale() / 102400.));
+ TimeAxisView::setup_sizes ();
+ Marker::setup_sizes (timebar_height);
+
_scroll_callbacks = 0;
bbt_label.set_name ("EditorRulerLabel");
_cursors->set_cursor_set (ARDOUR_UI::config()->get_icon_set());
cerr << "Set cursor set to " << ARDOUR_UI::config()->get_icon_set() << endl;
+ /* Push default cursor to ever-present bottom of cursor stack. */
+ push_canvas_cursor(_cursors->grabber);
+
ArdourCanvas::GtkCanvas* time_pad = manage (new ArdourCanvas::GtkCanvas ());
ArdourCanvas::Line* pad_line_1 = new ArdourCanvas::Line (time_pad->root());
entered_regionview = rv;
if (entered_regionview != 0) {
- entered_regionview->entered (internal_editing ());
+ entered_regionview->entered ();
}
if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
(static_cast<TimeAxisView*>(*i))->set_samples_per_pixel (samples_per_pixel);
}
- super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
+ super_rapid_screen_update_connection = Timers::super_rapid_connect (
sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
);
/* register for undo history */
_session->register_with_memento_command_factory(id(), this);
+ _session->register_with_memento_command_factory(_selection_memento->id(), _selection_memento);
ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
Editor::popup_xfade_in_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType /*item_type*/)
{
using namespace Menu_Helpers;
- AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
- assert(arv);
+ AudioRegionView* arv = dynamic_cast<AudioRegionView*> ((RegionView*)item->get_data ("regionview"));
+ if (!arv) {
+ return;
+ }
MenuList& items (xfade_in_context_menu.items());
items.clear ();
Editor::popup_xfade_out_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType /*item_type*/)
{
using namespace Menu_Helpers;
- AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
- assert(arv);
+ AudioRegionView* arv = dynamic_cast<AudioRegionView*> ((RegionView*)item->get_data ("regionview"));
+ if (!arv) {
+ return;
+ }
MenuList& items (xfade_out_context_menu.items());
items.clear ();
);
edit_items.push_back (SeparatorElem());
- edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
+ edit_items.push_back (MenuElem (_("Separate"), mem_fun(*this, &Editor::separate_region_from_selection)));
edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
edit_items.push_back (SeparatorElem());
edit_items.push_back (SeparatorElem());
edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
+ edit_items.push_back (MenuElem (_("Set Session Start/End from Range"), sigc::mem_fun(*this, &Editor::set_session_extents_from_selection)));
edit_items.push_back (SeparatorElem());
edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
{
string str = snap_mode_strings[(int)mode];
- if (_internal_editing) {
+ if (internal_editing()) {
internal_snap_mode = mode;
} else {
pre_internal_snap_mode = mode;
bool changed = (_edit_point != ep);
_edit_point = ep;
- string str = edit_point_strings[(int)ep];
-
if (Profile->get_mixbus())
if (ep == EditAtSelectedMarker)
ep = EditAtPlayhead;
-
+
+ string str = edit_point_strings[(int)ep];
if (str != edit_point_selector.get_text ()) {
edit_point_selector.set_text (str);
}
- reset_canvas_cursor ();
+ update_all_enter_cursors();
if (!force && !changed) {
return;
pre_internal_snap_type = (SnapType) string_2_enum (prop->value(), pre_internal_snap_type);
}
-
if ((prop = node.property ("pre-internal-snap-mode"))) {
pre_internal_snap_mode = (SnapMode) string_2_enum (prop->value(), pre_internal_snap_mode);
}
reset_y_origin (atof (prop->value ()));
}
- if ((prop = node.property ("internal-edit"))) {
- bool yn = string_is_affirmative (prop->value());
- RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
- if (act) {
- RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
- tact->set_active (!yn);
- tact->set_active (yn);
- }
- }
-
if ((prop = node.property ("join-object-range"))) {
RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object-range"));
bool yn = string_is_affirmative (prop->value());
node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
node->add_property ("mouse-mode", enum2str(mouse_mode));
- node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
node->add_property ("join-object-range", smart_mode_action->get_active () ? "yes" : "no");
Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
mouse_mode_size_group->add_widget (mouse_move_button);
mouse_mode_size_group->add_widget (mouse_cut_button);
mouse_mode_size_group->add_widget (mouse_select_button);
- mouse_mode_size_group->add_widget (mouse_gain_button);
mouse_mode_size_group->add_widget (mouse_timefx_button);
mouse_mode_size_group->add_widget (mouse_audition_button);
mouse_mode_size_group->add_widget (mouse_draw_button);
- mouse_mode_size_group->add_widget (internal_edit_button);
+ mouse_mode_size_group->add_widget (mouse_content_button);
mouse_mode_size_group->add_widget (zoom_in_button);
mouse_mode_size_group->add_widget (zoom_out_button);
}
if (!ARDOUR::Profile->get_trx()) {
- mouse_mode_hbox->pack_start (mouse_gain_button, false, false);
mouse_mode_hbox->pack_start (mouse_timefx_button, false, false);
mouse_mode_hbox->pack_start (mouse_audition_button, false, false);
mouse_mode_hbox->pack_start (mouse_draw_button, false, false);
- mouse_mode_hbox->pack_start (internal_edit_button, false, false, 0);
+ mouse_mode_hbox->pack_start (mouse_content_button, false, false);
}
mouse_mode_vbox->pack_start (*mouse_mode_hbox);
ARDOUR_UI::instance()->set_tip (smart_mode_button, _("Smart Mode (add Range functions to Object mode)"));
ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Object Mode (select/move Objects)"));
ARDOUR_UI::instance()->set_tip (mouse_cut_button, _("Cut Mode (split Regions)"));
- ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Range Mode (select/move Ranges)"));
- ARDOUR_UI::instance()->set_tip (mouse_draw_button, _("Draw/Edit MIDI Notes"));
- ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
+ ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Grab/Select Objects"));
+ ARDOUR_UI::instance()->set_tip (mouse_draw_button, _("Draw/Edit Gain/Notes/Automation"));
ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
- ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Note Level Editing"));
+ ARDOUR_UI::instance()->set_tip (mouse_content_button, _("Edit Contents (notes and automation)"));
ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Later"));
ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Earlier"));
/* 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)
{
if (_session) {
+ before.push_back (&_selection_memento->get_state ());
_session->begin_reversible_command (name);
}
}
Editor::begin_reversible_command (GQuark q)
{
if (_session) {
+ before.push_back (&_selection_memento->get_state ());
_session->begin_reversible_command (q);
}
}
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()) {
+ cerr << "Please call begin_reversible_command() before commit_reversible_command()." << endl;
+ } else {
+ before.pop_back();
+ }
+
_session->commit_reversible_command ();
}
}
int h;
string str;
+ DisplaySuspender ds;
if (_visible_track_count > 0) {
h = trackviews_height() / _visible_track_count;
switch (_snap_type) {
case SnapToBeat:
- return 1.0;
+ return Evoral::MusicalTime(1.0);
break;
case SnapToBeatDiv128:
- return 1.0/128.0;
+ return Evoral::MusicalTime(1.0/128.0);
break;
case SnapToBeatDiv64:
- return 1.0/64.0;
+ return Evoral::MusicalTime(1.0/64.0);
break;
case SnapToBeatDiv32:
- return 1.0/32.0;
+ return Evoral::MusicalTime(1.0/32.0);
break;
case SnapToBeatDiv28:
- return 1.0/28.0;
+ return Evoral::MusicalTime(1.0/28.0);
break;
case SnapToBeatDiv24:
- return 1.0/24.0;
+ return Evoral::MusicalTime(1.0/24.0);
break;
case SnapToBeatDiv20:
- return 1.0/20.0;
+ return Evoral::MusicalTime(1.0/20.0);
break;
case SnapToBeatDiv16:
- return 1.0/16.0;
+ return Evoral::MusicalTime(1.0/16.0);
break;
case SnapToBeatDiv14:
- return 1.0/14.0;
+ return Evoral::MusicalTime(1.0/14.0);
break;
case SnapToBeatDiv12:
- return 1.0/12.0;
+ return Evoral::MusicalTime(1.0/12.0);
break;
case SnapToBeatDiv10:
- return 1.0/10.0;
+ return Evoral::MusicalTime(1.0/10.0);
break;
case SnapToBeatDiv8:
- return 1.0/8.0;
+ return Evoral::MusicalTime(1.0/8.0);
break;
case SnapToBeatDiv7:
- return 1.0/7.0;
+ return Evoral::MusicalTime(1.0/7.0);
break;
case SnapToBeatDiv6:
- return 1.0/6.0;
+ return Evoral::MusicalTime(1.0/6.0);
break;
case SnapToBeatDiv5:
- return 1.0/5.0;
+ return Evoral::MusicalTime(1.0/5.0);
break;
case SnapToBeatDiv4:
- return 1.0/4.0;
+ return Evoral::MusicalTime(1.0/4.0);
break;
case SnapToBeatDiv3:
- return 1.0/3.0;
+ return Evoral::MusicalTime(1.0/3.0);
break;
case SnapToBeatDiv2:
- return 1.0/2.0;
+ return Evoral::MusicalTime(1.0/2.0);
break;
case SnapToBar:
if (_session) {
- return _session->tempo_map().meter_at (position).divisions_per_bar();
+ return Evoral::MusicalTime(_session->tempo_map().meter_at (position).divisions_per_bar());
}
break;
break;
}
- return 0.0;
+ return Evoral::MusicalTime();
}
framecnt_t
void
Editor::update_tearoff_visibility()
{
- bool visible = Config->get_keep_tearoffs();
+ bool visible = ARDOUR_UI::config()->get_keep_tearoffs();
_mouse_mode_tearoff->set_visible (visible);
_tools_tearoff->set_visible (visible);
if (_zoom_tearoff) {
void
Editor::clear_playlists (TimeAxisView* v)
{
- begin_reversible_command (_("clear playlists"));
+ begin_reversible_command (_("clear playlists"));
vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
_session->playlists->get (playlists);
mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::select.property_id);
// return key_press_focus_accelerator_handler (*this, ev);
}
+double
+Editor::get_y_origin () const
+{
+ return vertical_adjustment.get_value ();
+}
+
/** Queue up a change to the viewport x origin.
* @param frame New x origin.
*/
_session->set_auto_punch_location (loc);
XMLNode &after = _session->locations()->get_state();
_session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
- }
- else {
+ } else {
XMLNode &before = tpl->get_state();
tpl->set_hidden (false, this);
tpl->set (start, end);
return regions;
}
+void
+Editor::get_regionviews_by_id (PBD::ID const & id, RegionSelection & regions) const
+{
+ for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
+ RouteTimeAxisView* tatv;
+
+ if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
+ boost::shared_ptr<Playlist> pl;
+ std::vector<boost::shared_ptr<Region> > results;
+ boost::shared_ptr<Track> tr;
+
+ if ((tr = tatv->track()) == 0) {
+ /* bus */
+ continue;
+ }
+
+ if ((pl = (tr->playlist())) != 0) {
+ boost::shared_ptr<Region> r = pl->region_by_id (id);
+ if (r) {
+ RegionView* marv = tatv->view()->find_view (r);
+ if (marv) {
+ regions.push_back (marv);
+ }
+ }
+ }
+ }
+ }
+}
+
void
Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions, bool src_comparison)
{
_routes->redisplay ();
delete dialog;
+
+ begin_selection_op_history ();
+
_have_idled = true;
}
}
void
-Editor::region_view_added (RegionView *)
+Editor::region_view_added (RegionView * rv)
{
+ for (list<PBD::ID>::iterator pr = selection->regions.pending.begin (); pr != selection->regions.pending.end (); ++pr) {
+ if (rv->region ()->id () == (*pr)) {
+ selection->add (rv);
+ selection->regions.pending.erase (pr);
+ break;
+ }
+ }
_summary->set_background_dirty ();
}
rtv->effective_gain_display ();
- if (internal_editing()) {
- rtv->enter_internal_edit_mode ();
- } else {
- rtv->leave_internal_edit_mode ();
- }
-
rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
}
_control_point_context_menu.popup (event->button.button, event->button.time);
}
+void
+Editor::popup_note_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
+{
+ using namespace Menu_Helpers;
+
+ NoteBase* note = reinterpret_cast<NoteBase*>(item->get_data("notebase"));
+ if (!note) {
+ return;
+ }
+
+ /* We need to get the selection here and pass it to the operations, since
+ popping up the menu will cause a region leave event which clears
+ entered_regionview. */
+
+ MidiRegionView& mrv = note->region_view();
+ const RegionSelection rs = get_regions_from_selection_and_entered ();
+
+ MenuList& items = _note_context_menu.items();
+ items.clear();
+
+ items.push_back(MenuElem(_("Delete"),
+ sigc::mem_fun(mrv, &MidiRegionView::delete_selection)));
+ items.push_back(MenuElem(_("Edit..."),
+ sigc::bind(sigc::mem_fun(*this, &Editor::edit_notes), &mrv)));
+ items.push_back(MenuElem(_("Legatize"),
+ sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, false)));
+ items.push_back(MenuElem(_("Quantize..."),
+ sigc::bind(sigc::mem_fun(*this, &Editor::quantize_regions), rs)));
+ items.push_back(MenuElem(_("Remove Overlap"),
+ sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, true)));
+ items.push_back(MenuElem(_("Transform..."),
+ sigc::bind(sigc::mem_fun(*this, &Editor::transform_regions), rs)));
+
+ _note_context_menu.popup (event->button.button, event->button.time);
+}
+
void
Editor::zoom_vertical_modifier_released()
{
{
if (parameter == "icon-set") {
while (!_cursor_stack.empty()) {
- _cursor_stack.pop();
+ _cursor_stack.pop_back();
}
_cursors->set_cursor_set (ARDOUR_UI::config()->get_icon_set());
+ _cursor_stack.push_back(_cursors->grabber);
} else if (parameter == "draggable-playhead") {
if (_verbose_cursor) {
playhead_cursor->set_sensitive (ARDOUR_UI::config()->get_draggable_playhead());