#include "gtkmm2ext/utils.h"
#include "ardour/file_source.h"
-#include "ardour/midi_playlist.h"
+#include "ardour/ladspa_plugin.h"
+#include "ardour/location.h"
#include "ardour/midi_diskstream.h"
#include "ardour/midi_patch_manager.h"
+#include "ardour/midi_playlist.h"
+#include "ardour/midi_region.h"
#include "ardour/midi_source.h"
-#include "ardour/processor.h"
-#include "ardour/ladspa_plugin.h"
-#include "ardour/location.h"
+#include "ardour/operations.h"
#include "ardour/playlist.h"
+#include "ardour/processor.h"
#include "ardour/region_factory.h"
#include "ardour/session.h"
#include "ardour/session_playlist.h"
#include "ardour/tempo.h"
#include "ardour/utils.h"
-#include "ardour/operations.h"
#include "midi++/names.h"
-#include "add_midi_cc_track_dialog.h"
#include "ardour_ui.h"
+#include "ardour_button.h"
#include "automation_line.h"
#include "automation_time_axis.h"
#include "canvas-note-event.h"
#include "canvas_impl.h"
-#include "crossfade_view.h"
#include "editor.h"
#include "enums.h"
#include "ghostregion.h"
static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 162;
static const uint32_t KEYBOARD_MIN_HEIGHT = 140;
-MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess,
- boost::shared_ptr<Route> rt, Canvas& canvas)
+MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
: AxisView(sess) // virtually inherited
- , RouteTimeAxisView(ed, sess, rt, canvas)
+ , RouteTimeAxisView(ed, sess, canvas)
, _ignore_signals(false)
, _range_scroomer(0)
, _piano_roll_header(0)
, controller_menu (0)
, _step_editor (0)
{
- subplugin_menu.set_name ("ArdourContextMenu");
+}
+void
+MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
+{
+ _route = rt;
+
_view = new MidiStreamView (*this);
+
+ if (is_track ()) {
+ _piano_roll_header = new PianoRollHeader(*midi_view());
+ _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
+ }
- ignore_toggle = false;
+ /* This next call will result in our height being set up, so it must come after
+ the creation of the piano roll / range scroomer as their visibility is set up
+ when our height is.
+ */
+ RouteTimeAxisView::set_route (rt);
+
+ _view->apply_color (_color, StreamView::RegionColor);
- mute_button->set_active (false);
- solo_button->set_active (false);
+ subplugin_menu.set_name ("ArdourContextMenu");
+
+ if (!gui_property ("note-range-min").empty ()) {
+ midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()), atoi (gui_property ("note-range-max").c_str()), true);
+ }
+ midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
+
+ ignore_toggle = false;
if (is_midi_track()) {
controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
processors_changed (RouteProcessorChange ());
- ensure_xml_node ();
-
- set_state (*xml_node, Stateful::loading_state_version);
-
_route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
if (is_track()) {
- _piano_roll_header = new PianoRollHeader(*midi_view());
-
+ _piano_roll_header->SetNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
_piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
_piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
_piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
- _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
-
/* Suspend updates of the StreamView during scroomer drags to speed things up */
_range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
_range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
_channel_selector.mode_changed.connect(
sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
- XMLProperty *prop;
- if ((prop = xml_node->property ("color-mode")) != 0) {
- _color_mode = ColorMode (string_2_enum(prop->value(), _color_mode));
+ string prop = gui_property ("color-mode");
+ if (!prop.empty()) {
+ _color_mode = ColorMode (string_2_enum(prop, _color_mode));
if (_color_mode == ChannelColors) {
_channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
}
}
- if ((prop = xml_node->property ("note-mode")) != 0) {
- _note_mode = NoteMode (string_2_enum(prop->value(), _note_mode));
+ set_color_mode (_color_mode, true, false);
+
+ prop = gui_property ("note-mode");
+ if (!prop.empty()) {
+ _note_mode = NoteMode (string_2_enum (prop, _note_mode));
if (_percussion_mode_item) {
_percussion_mode_item->set_active (_note_mode == Percussive);
}
}
- set_color_mode (_color_mode, true, false);
+ /* Look for any GUI object state nodes that represent automation children that should exist, and create
+ * the children.
+ */
+
+ list<string> gui_ids = gui_object_state().all_ids ();
+ for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
+ PBD::ID route_id;
+ bool has_parameter;
+ Evoral::Parameter parameter (0, 0, 0);
+
+ bool const p = AutomationTimeAxisView::parse_state_id (*i, route_id, has_parameter, parameter);
+ if (p && route_id == _route->id () && has_parameter) {
+ create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (*i, X_("visible"))));
+ }
+ }
}
void
return dynamic_cast<MidiStreamView*>(_view);
}
-guint32
-MidiTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
-{
- ensure_xml_node ();
- xml_node->add_property ("shown-editor", "yes");
-
- guint32 ret = TimeAxisView::show_at (y, nth, parent);
- return ret;
-}
-
-void
-MidiTimeAxisView::hide ()
-{
- ensure_xml_node ();
- xml_node->add_property ("shown-editor", "no");
-
- TimeAxisView::hide ();
-}
-
void
MidiTimeAxisView::set_height (uint32_t h)
{
- RouteTimeAxisView::set_height (h);
-
- if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
+ if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
_midi_controls_box.show_all ();
} else {
_midi_controls_box.hide();
}
-
- if (height >= KEYBOARD_MIN_HEIGHT) {
- if (is_track() && _range_scroomer)
+
+ if (h >= KEYBOARD_MIN_HEIGHT) {
+ if (is_track() && _range_scroomer) {
_range_scroomer->show();
- if (is_track() && _piano_roll_header)
+ }
+ if (is_track() && _piano_roll_header) {
_piano_roll_header->show();
+ }
} else {
- if (is_track() && _range_scroomer)
+ if (is_track() && _range_scroomer) {
_range_scroomer->hide();
- if (is_track() && _piano_roll_header)
+ }
+ if (is_track() && _piano_roll_header) {
_piano_roll_header->hide();
+ }
}
+
+ /* We need to do this after changing visibility of our stuff, as it will
+ eventually trigger a call to Editor::reset_controls_layout_width(),
+ which needs to know if we have just shown or hidden a scroomer /
+ piano roll.
+ */
+ RouteTimeAxisView::set_height (h);
}
void
if (_note_mode != mode || midi_track()->note_mode() != mode) {
_note_mode = mode;
midi_track()->set_note_mode(mode);
- xml_node->add_property ("note-mode", enum_2_string(_note_mode));
+ set_gui_property ("note-mode", enum_2_string(_note_mode));
_view->redisplay_track();
}
}
}
_color_mode = mode;
- xml_node->add_property ("color-mode", enum_2_string(_color_mode));
+ set_gui_property ("color-mode", enum_2_string(_color_mode));
if (redisplay) {
_view->redisplay_track();
}
const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
- create_automation_child(*i, true);
+ create_automation_child (*i, true);
}
}
}
AutomationTracks::iterator existing = _automation_tracks.find (param);
+
if (existing != _automation_tracks.end()) {
+
+ /* automation track created because we had existing data for
+ * the processor, but visibility may need to be controlled
+ * since it will have been set visible by default.
+ */
+
+ cerr << "show existing auto track: " << show << " noredraw " << no_redraw << endl;
+
+ if (existing->second->set_marked_for_display (show) && !no_redraw) {
+ request_redraw ();
+ }
+
return;
}
- if (param.type() == GainAutomation) {
- create_gain_automation_child (param, show);
- } else {
+ boost::shared_ptr<AutomationTimeAxisView> track;
- /* These controllers are region "automation", so we do not create
- * an AutomationList/Line for the track */
-
- boost::shared_ptr<AutomationTimeAxisView> track (
- new AutomationTimeAxisView (
- _session,
- _route,
- boost::shared_ptr<Automatable> (),
- boost::shared_ptr<AutomationControl> (),
- param,
- _editor,
- *this,
- true,
- parent_canvas,
- _route->describe_parameter(param)
- )
- );
+ switch (param.type()) {
+
+ case GainAutomation:
+ create_gain_automation_child (param, show);
+ break;
+
+ case PluginAutomation:
+ /* handled elsewhere */
+ break;
+
+ case MidiCCAutomation:
+ case MidiPgmChangeAutomation:
+ case MidiPitchBenderAutomation:
+ case MidiChannelPressureAutomation:
+ case MidiSystemExclusiveAutomation:
+ /* These controllers are region "automation" - they are owned
+ * by regions (and their MidiModels), not by the track. As a
+ * result we do not create an AutomationList/Line for the track
+ * ... except here we are doing something!! XXX
+ */
+
+ track.reset (new AutomationTimeAxisView (
+ _session,
+ _route,
+ boost::shared_ptr<Automatable> (),
+ boost::shared_ptr<AutomationControl> (),
+ param,
+ _editor,
+ *this,
+ true,
+ parent_canvas,
+ _route->describe_parameter(param)
+ ));
if (_view) {
_view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
}
add_automation_child (param, track, show);
+ break;
+
+ default:
+ error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
}
}
-
void
MidiTimeAxisView::route_active_changed ()
{
}
}
+void
+MidiTimeAxisView::set_note_selection (uint8_t note)
+{
+ if (!_editor.internal_editing()) {
+ return;
+ }
+ uint16_t chn_mask = _channel_selector.get_selected_channels();
+
+ if (_view->num_selected_regionviews() == 0) {
+ _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
+ } else {
+ _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
+ }
+}
void
MidiTimeAxisView::add_note_selection (uint8_t note)
}
void
-MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
+MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
{
dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
}
+void
+MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
+{
+ dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
+}
+
void
MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
{
/* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
which will cause a redraw. We don't want one per channel, so block that with no_redraw.
*/
- changed = track->set_visibility (false) || changed;
+ changed = track->set_marked_for_display (false) || changed;
} else {
- changed = track->set_visibility (true) || changed;
+ changed = track->set_marked_for_display (true) || changed;
}
}
}
controller_menu = 0;
if (changed) {
- _route->gui_changed ("track_height", this);
+ request_redraw ();
}
}
return channel;
}
+
+void
+MidiTimeAxisView::note_range_changed ()
+{
+ set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
+ set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
+}