Merge branch 'ripple-mode-cc' into cairocanvas
authorColin Fletcher <colin.m.fletcher@googlemail.com>
Tue, 1 Jul 2014 18:10:47 +0000 (19:10 +0100)
committerColin Fletcher <colin.m.fletcher@googlemail.com>
Tue, 1 Jul 2014 18:10:47 +0000 (19:10 +0100)
Fix up merge conflicts in
gtk2_ardour/editor_mouse.cc
gtk2_ardour/editor_ops.cc

Also fix up compile errors.

546 files changed:
MSVCardour3/Ardour3.vcproj
gtk2_ardour/about.cc
gtk2_ardour/actions.cc
gtk2_ardour/add_route_dialog.cc
gtk2_ardour/add_video_dialog.cc
gtk2_ardour/add_video_dialog.h
gtk2_ardour/ardev_common.sh.in
gtk2_ardour/ardour.menus.in
gtk2_ardour/ardour.sh.in
gtk2_ardour/ardour3_styles.rc.in
gtk2_ardour/ardour3_ui_default.conf [deleted file]
gtk2_ardour/ardour3_ui_default.conf.in [new file with mode: 0644]
gtk2_ardour/ardour3_widget_list.rc
gtk2_ardour/ardour_button.cc
gtk2_ardour/ardour_button.h
gtk2_ardour/ardour_dialog.cc
gtk2_ardour/ardour_ui.cc
gtk2_ardour/ardour_ui.h
gtk2_ardour/ardour_ui2.cc
gtk2_ardour/ardour_ui_dialogs.cc
gtk2_ardour/ardour_ui_ed.cc
gtk2_ardour/ardour_ui_options.cc
gtk2_ardour/ardour_window.cc
gtk2_ardour/audio_clock.cc
gtk2_ardour/audio_region_editor.cc
gtk2_ardour/audio_region_view.cc
gtk2_ardour/audio_region_view.h
gtk2_ardour/audio_streamview.cc
gtk2_ardour/audio_time_axis.cc
gtk2_ardour/automation_controller.cc
gtk2_ardour/automation_line.cc
gtk2_ardour/automation_line.h
gtk2_ardour/automation_region_view.cc
gtk2_ardour/automation_region_view.h
gtk2_ardour/automation_streamview.cc
gtk2_ardour/automation_time_axis.cc
gtk2_ardour/axis_view.cc
gtk2_ardour/axis_view.h
gtk2_ardour/big_clock_window.cc
gtk2_ardour/bundle_env_cocoa.cc
gtk2_ardour/bundle_env_linux.cc
gtk2_ardour/bundle_env_msvc.cc
gtk2_ardour/bundle_manager.cc
gtk2_ardour/canvas_vars.h
gtk2_ardour/control_point.cc
gtk2_ardour/control_point.h
gtk2_ardour/crossfade_edit.cc
gtk2_ardour/crossfade_view.h
gtk2_ardour/editor.cc
gtk2_ardour/editor.h
gtk2_ardour/editor_actions.cc
gtk2_ardour/editor_audio_import.cc
gtk2_ardour/editor_canvas.cc
gtk2_ardour/editor_canvas_events.cc
gtk2_ardour/editor_cursors.cc
gtk2_ardour/editor_cursors.h
gtk2_ardour/editor_drag.cc
gtk2_ardour/editor_drag.h
gtk2_ardour/editor_group_tabs.cc
gtk2_ardour/editor_items.h
gtk2_ardour/editor_markers.cc
gtk2_ardour/editor_mixer.cc
gtk2_ardour/editor_mouse.cc
gtk2_ardour/editor_ops.cc
gtk2_ardour/editor_regions.cc
gtk2_ardour/editor_route_groups.cc
gtk2_ardour/editor_routes.cc
gtk2_ardour/editor_routes.h
gtk2_ardour/editor_rulers.cc
gtk2_ardour/editor_selection.cc
gtk2_ardour/editor_snapshots.cc
gtk2_ardour/editor_summary.cc
gtk2_ardour/editor_summary.h
gtk2_ardour/editor_tempodisplay.cc
gtk2_ardour/editor_timefx.cc
gtk2_ardour/engine_dialog.cc
gtk2_ardour/engine_dialog.h
gtk2_ardour/enums.cc
gtk2_ardour/export_channel_selector.cc
gtk2_ardour/export_channel_selector.h
gtk2_ardour/export_dialog.cc
gtk2_ardour/export_dialog.h
gtk2_ardour/export_file_notebook.cc
gtk2_ardour/export_file_notebook.h
gtk2_ardour/export_format_dialog.cc
gtk2_ardour/export_format_dialog.h
gtk2_ardour/export_range_markers_dialog.cc
gtk2_ardour/export_range_markers_dialog.h
gtk2_ardour/export_timespan_selector.cc
gtk2_ardour/export_timespan_selector.h
gtk2_ardour/export_video_dialog.cc
gtk2_ardour/gain_meter.cc
gtk2_ardour/generic_pluginui.cc
gtk2_ardour/ghostregion.cc
gtk2_ardour/ghostregion.h
gtk2_ardour/global_port_matrix.cc
gtk2_ardour/global_signals.h
gtk2_ardour/group_tabs.cc
gtk2_ardour/group_tabs.h
gtk2_ardour/gtk-custom-hruler.c [deleted file]
gtk2_ardour/gtk-custom-hruler.h [deleted file]
gtk2_ardour/gtk-custom-ruler.c [deleted file]
gtk2_ardour/gtk-custom-ruler.h [deleted file]
gtk2_ardour/hit.cc
gtk2_ardour/hit.h
gtk2_ardour/icons/anchored_trim_left_cursor.png [new file with mode: 0644]
gtk2_ardour/icons/anchored_trim_right_cursor.png [new file with mode: 0644]
gtk2_ardour/icons/cursor_z/expand_left_right_cursor.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/expand_up_down_cursor.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/fade_in_cursor.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/fade_out_cursor.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/grabber.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/grabber_edit_point.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/grabber_note.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/hide.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/i_beam_cursor.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/move_cursor.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/resize_bottom_cursor.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/resize_bottom_left_cursor.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/resize_bottom_right_cursor.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/resize_left_cursor.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/resize_right_cursor.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/resize_top_cursor.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/resize_top_left_cursor.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/resize_top_right_cursor.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/trim_bottom_cursor.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/trim_left_cursor.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/trim_left_cursor_5.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/trim_left_cursor_right_only.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/trim_right_cursor.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/trim_right_cursor_5.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/trim_right_cursor_left_only.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/trim_top_cursor.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/zoom_in_cursor.png [new file with mode: 0755]
gtk2_ardour/icons/cursor_z/zoom_out_cursor.png [new file with mode: 0755]
gtk2_ardour/icons/fadein-constant-power.png
gtk2_ardour/icons/fadein-fast-cut.png
gtk2_ardour/icons/fadein-linear.png
gtk2_ardour/icons/fadein-slow-cut.png
gtk2_ardour/icons/fadein-symmetric.png [new file with mode: 0644]
gtk2_ardour/icons/fadeout-constant-power.png
gtk2_ardour/icons/fadeout-fast-cut.png
gtk2_ardour/icons/fadeout-linear.png
gtk2_ardour/icons/fadeout-slow-cut.png
gtk2_ardour/icons/fadeout-symmetric.png [new file with mode: 0644]
gtk2_ardour/icons/padlock_closed.png [new file with mode: 0644]
gtk2_ardour/icons/padlock_open.png [new file with mode: 0644]
gtk2_ardour/icons/soundcloud.png [new file with mode: 0644]
gtk2_ardour/io_selector.cc
gtk2_ardour/keyboard.cc
gtk2_ardour/keyeditor.cc
gtk2_ardour/level_meter.cc
gtk2_ardour/linux_vst_gui_support.cc
gtk2_ardour/location_ui.cc
gtk2_ardour/main.cc
gtk2_ardour/marker.cc
gtk2_ardour/marker.h
gtk2_ardour/meter_patterns.cc
gtk2_ardour/meter_strip.cc
gtk2_ardour/meter_strip.h
gtk2_ardour/meterbridge.cc
gtk2_ardour/midi_automation_line.cc
gtk2_ardour/midi_automation_line.h
gtk2_ardour/midi_region_view.cc
gtk2_ardour/midi_region_view.h
gtk2_ardour/midi_streamview.cc
gtk2_ardour/midi_streamview.h
gtk2_ardour/midi_time_axis.cc
gtk2_ardour/mixer_actor.cc
gtk2_ardour/mixer_group_tabs.cc
gtk2_ardour/mixer_strip.cc
gtk2_ardour/mixer_strip.h
gtk2_ardour/mixer_ui.cc
gtk2_ardour/mixer_ui.h
gtk2_ardour/mnemonic-us.bindings.in
gtk2_ardour/monitor_section.cc
gtk2_ardour/mono_panner.cc
gtk2_ardour/mouse_cursors.cc
gtk2_ardour/mouse_cursors.h
gtk2_ardour/note.cc
gtk2_ardour/note.h
gtk2_ardour/note_base.cc
gtk2_ardour/option_editor.cc
gtk2_ardour/option_editor.h
gtk2_ardour/opts.cc
gtk2_ardour/panner2d.cc
gtk2_ardour/panner2d.h
gtk2_ardour/panner_interface.cc
gtk2_ardour/panner_interface.h
gtk2_ardour/panner_ui.cc
gtk2_ardour/panner_ui.h
gtk2_ardour/patch_change.cc
gtk2_ardour/patch_change.h
gtk2_ardour/plugin_selector.cc
gtk2_ardour/plugin_ui.cc
gtk2_ardour/port_insert_ui.cc
gtk2_ardour/port_matrix.cc
gtk2_ardour/port_matrix_column_labels.cc
gtk2_ardour/port_matrix_row_labels.cc
gtk2_ardour/processor_box.cc
gtk2_ardour/public_editor.cc
gtk2_ardour/public_editor.h
gtk2_ardour/quantize_dialog.cc
gtk2_ardour/rc_option_editor.cc
gtk2_ardour/region_editor.cc
gtk2_ardour/region_gain_line.cc
gtk2_ardour/region_gain_line.h
gtk2_ardour/region_layering_order_editor.cc
gtk2_ardour/region_view.cc
gtk2_ardour/region_view.h
gtk2_ardour/return_ui.cc
gtk2_ardour/rhythm_ferret.cc
gtk2_ardour/route_group_dialog.cc
gtk2_ardour/route_group_dialog.h
gtk2_ardour/route_params_ui.cc
gtk2_ardour/route_params_ui.h
gtk2_ardour/route_time_axis.cc
gtk2_ardour/route_time_axis.h
gtk2_ardour/route_ui.cc
gtk2_ardour/ruler_dialog.cc [new file with mode: 0644]
gtk2_ardour/ruler_dialog.h [new file with mode: 0644]
gtk2_ardour/send_ui.cc
gtk2_ardour/session_dialog.cc
gtk2_ardour/session_dialog.h
gtk2_ardour/session_option_editor.cc
gtk2_ardour/session_option_editor.h
gtk2_ardour/sfdb_freesound_mootcher.cc
gtk2_ardour/sfdb_ui.cc
gtk2_ardour/soundcloud_export_selector.cc [new file with mode: 0644]
gtk2_ardour/soundcloud_export_selector.h [new file with mode: 0644]
gtk2_ardour/splash.cc
gtk2_ardour/startup.cc
gtk2_ardour/step_entry.cc
gtk2_ardour/stereo_panner.cc
gtk2_ardour/streamview.cc
gtk2_ardour/streamview.h
gtk2_ardour/sys_ex.cc
gtk2_ardour/sys_ex.h
gtk2_ardour/tape_region_view.cc
gtk2_ardour/tape_region_view.h
gtk2_ardour/tempo_dialog.cc
gtk2_ardour/tempo_lines.cc
gtk2_ardour/tempo_lines.h
gtk2_ardour/theme_manager.cc
gtk2_ardour/theme_manager.h
gtk2_ardour/time_axis_view.cc
gtk2_ardour/time_axis_view.h
gtk2_ardour/time_axis_view_item.cc
gtk2_ardour/time_axis_view_item.h
gtk2_ardour/track_selection.h
gtk2_ardour/transcode_ffmpeg.cc
gtk2_ardour/transcode_video_dialog.cc
gtk2_ardour/ui_config.cc
gtk2_ardour/ui_config.h
gtk2_ardour/ui_config_vars.h
gtk2_ardour/utils.cc
gtk2_ardour/utils.h
gtk2_ardour/verbose_cursor.cc
gtk2_ardour/verbose_cursor.h
gtk2_ardour/video_image_frame.cc
gtk2_ardour/video_image_frame.h
gtk2_ardour/video_monitor.cc
gtk2_ardour/video_server_dialog.cc
gtk2_ardour/video_timeline.cc
gtk2_ardour/video_timeline.h
gtk2_ardour/volume_controller.cc
gtk2_ardour/wscript
headless/load_session.cc
headless/wscript
libs/ardour/MSVClibardour/ardour.vcproj
libs/ardour/amp.cc
libs/ardour/ardour/audio_backend.h
libs/ardour/ardour/audio_diskstream.h
libs/ardour/ardour/audio_track.h
libs/ardour/ardour/audioengine.h
libs/ardour/ardour/audiofilesource.h
libs/ardour/ardour/auditioner.h
libs/ardour/ardour/buffer_manager.h
libs/ardour/ardour/debug.h
libs/ardour/ardour/delayline.h [new file with mode: 0644]
libs/ardour/ardour/diskstream.h
libs/ardour/ardour/export_format_manager.h
libs/ardour/ardour/export_format_specification.h
libs/ardour/ardour/export_handler.h
libs/ardour/ardour/file_source.h
libs/ardour/ardour/internal_send.h
libs/ardour/ardour/midi_buffer.h
libs/ardour/ardour/midi_diskstream.h
libs/ardour/ardour/midi_track.h
libs/ardour/ardour/mididm.h [new file with mode: 0644]
libs/ardour/ardour/plugin_insert.h
libs/ardour/ardour/region_sorters.h
libs/ardour/ardour/route.h
libs/ardour/ardour/route_sorters.h [new file with mode: 0644]
libs/ardour/ardour/send.h
libs/ardour/ardour/session.h
libs/ardour/ardour/session_configuration.h
libs/ardour/ardour/session_configuration_vars.h
libs/ardour/ardour/smf_source.h
libs/ardour/ardour/sndfilesource.h
libs/ardour/ardour/soundcloud_upload.h [new file with mode: 0644]
libs/ardour/ardour/source_factory.h
libs/ardour/ardour/system_exec.h
libs/ardour/ardour/thread_buffers.h
libs/ardour/ardour/track.h
libs/ardour/ardour/types.h
libs/ardour/ardour/utils.h
libs/ardour/audio_diskstream.cc
libs/ardour/audio_track.cc
libs/ardour/audio_unit.cc
libs/ardour/audioengine.cc
libs/ardour/audiofilesource.cc
libs/ardour/audioregion.cc
libs/ardour/automation_list.cc
libs/ardour/buffer_manager.cc
libs/ardour/control_protocol_manager.cc
libs/ardour/debug.cc
libs/ardour/delayline.cc [new file with mode: 0644]
libs/ardour/delivery.cc
libs/ardour/diskstream.cc
libs/ardour/enums.cc
libs/ardour/export_channel.cc
libs/ardour/export_format_manager.cc
libs/ardour/export_format_specification.cc
libs/ardour/export_handler.cc
libs/ardour/export_profile_manager.cc
libs/ardour/file_source.cc
libs/ardour/filter.cc
libs/ardour/globals.cc
libs/ardour/import.cc
libs/ardour/internal_return.cc
libs/ardour/internal_send.cc
libs/ardour/linux_vst_support.cc
libs/ardour/ltc_slave.cc
libs/ardour/lv2_plugin.cc
libs/ardour/midi_buffer.cc
libs/ardour/midi_diskstream.cc
libs/ardour/midi_patch_manager.cc
libs/ardour/midi_track.cc
libs/ardour/mididm.cc [new file with mode: 0644]
libs/ardour/panner_manager.cc
libs/ardour/panner_shell.cc
libs/ardour/playlist.cc
libs/ardour/plugin_insert.cc
libs/ardour/plugin_manager.cc
libs/ardour/rc_configuration.cc
libs/ardour/route.cc
libs/ardour/send.cc
libs/ardour/session.cc
libs/ardour/session_configuration.cc
libs/ardour/session_midi.cc
libs/ardour/session_state.cc
libs/ardour/session_state_utils.cc
libs/ardour/session_transport.cc
libs/ardour/smf_source.cc
libs/ardour/sndfilesource.cc
libs/ardour/soundcloud_upload.cc [new file with mode: 0644]
libs/ardour/source_factory.cc
libs/ardour/system_exec.cc
libs/ardour/template_utils.cc
libs/ardour/tempo.cc
libs/ardour/test/resampled_source_test.cc
libs/ardour/thread_buffers.cc
libs/ardour/track.cc
libs/ardour/utils.cc
libs/ardour/vst_info_file.cc
libs/ardour/vst_plugin.cc
libs/ardour/wscript
libs/ardouralsautil/ardouralsautil/devicelist.h [new file with mode: 0644]
libs/ardouralsautil/ardouralsautil/reserve.h [new file with mode: 0644]
libs/ardouralsautil/devicelist.cc [new file with mode: 0644]
libs/ardouralsautil/request_device.c [new file with mode: 0644]
libs/ardouralsautil/reserve.c [new file with mode: 0644]
libs/ardouralsautil/wscript [new file with mode: 0644]
libs/backends/MSVCbackends/dummy_audiobackend.vcproj [new file with mode: 0644]
libs/backends/MSVCbackends/jack_audiobackend.vcproj [new file with mode: 0644]
libs/backends/MSVCbackends/jack_backend.vcproj [deleted file]
libs/backends/MSVCbackends/waves_audiobackend.vcproj [new file with mode: 0644]
libs/backends/MSVCbackends/waves_backend.vcproj [deleted file]
libs/backends/alsa/alsa_audiobackend.cc [new file with mode: 0644]
libs/backends/alsa/alsa_audiobackend.h [new file with mode: 0644]
libs/backends/alsa/alsa_midi.cc [new file with mode: 0644]
libs/backends/alsa/alsa_midi.h [new file with mode: 0644]
libs/backends/alsa/alsa_rawmidi.cc [new file with mode: 0644]
libs/backends/alsa/alsa_rawmidi.h [new file with mode: 0644]
libs/backends/alsa/alsa_sequencer.cc [new file with mode: 0644]
libs/backends/alsa/alsa_sequencer.h [new file with mode: 0644]
libs/backends/alsa/rt_thread.h [new file with mode: 0644]
libs/backends/alsa/select_sleep.h [new file with mode: 0644]
libs/backends/alsa/wscript [new file with mode: 0644]
libs/backends/alsa/zita-alsa-pcmi.cc [new file with mode: 0644]
libs/backends/alsa/zita-alsa-pcmi.h [new file with mode: 0644]
libs/backends/dummy/dummy_audiobackend.cc
libs/backends/dummy/dummy_audiobackend.h
libs/backends/dummy/wscript
libs/backends/jack/jack_audiobackend.cc
libs/backends/jack/jack_audiobackend.h
libs/backends/jack/jack_utils.cc
libs/backends/jack/wscript
libs/backends/wavesaudio/waves_audiobackend.h
libs/backends/wavesaudio/wavesapi/devicemanager/WCMRCoreAudioDeviceManager.cpp
libs/backends/wavesaudio/wavesapi/miscutils/UMicroseconds.cpp
libs/backends/wavesaudio/wscript
libs/backends/wscript
libs/canvas/MSVCcanvas/cairocanvas.vcproj
libs/canvas/arc.cc
libs/canvas/arrow.cc
libs/canvas/canvas.cc
libs/canvas/canvas/arc.h
libs/canvas/canvas/arrow.h
libs/canvas/canvas/canvas.h
libs/canvas/canvas/circle.h
libs/canvas/canvas/container.h [new file with mode: 0644]
libs/canvas/canvas/curve.h
libs/canvas/canvas/drag_handle.h [deleted file]
libs/canvas/canvas/fill.h
libs/canvas/canvas/flag.h
libs/canvas/canvas/fwd.h
libs/canvas/canvas/group.h [deleted file]
libs/canvas/canvas/image.h
libs/canvas/canvas/interpolated_curve.h [new file with mode: 0644]
libs/canvas/canvas/item.h
libs/canvas/canvas/line.h
libs/canvas/canvas/line_set.h
libs/canvas/canvas/lookup_table.h
libs/canvas/canvas/outline.h
libs/canvas/canvas/pixbuf.h
libs/canvas/canvas/poly_item.h
libs/canvas/canvas/poly_line.h
libs/canvas/canvas/polygon.h
libs/canvas/canvas/rectangle.h
libs/canvas/canvas/root_group.h
libs/canvas/canvas/ruler.h [new file with mode: 0644]
libs/canvas/canvas/scroll_group.h [new file with mode: 0644]
libs/canvas/canvas/stateful_image.h
libs/canvas/canvas/text.h
libs/canvas/canvas/tracking_text.h [new file with mode: 0644]
libs/canvas/canvas/types.h
libs/canvas/canvas/utils.h
libs/canvas/canvas/wave_view.h
libs/canvas/canvas/widget.h [new file with mode: 0644]
libs/canvas/canvas/xfade_curve.h [new file with mode: 0644]
libs/canvas/circle.cc
libs/canvas/container.cc [new file with mode: 0644]
libs/canvas/curve.cc
libs/canvas/drag_handle.cc [deleted file]
libs/canvas/fill.cc
libs/canvas/flag.cc
libs/canvas/group.cc [deleted file]
libs/canvas/image.cc
libs/canvas/item.cc
libs/canvas/line.cc
libs/canvas/line_set.cc
libs/canvas/lookup_table.cc
libs/canvas/outline.cc
libs/canvas/pixbuf.cc
libs/canvas/poly_item.cc
libs/canvas/poly_line.cc
libs/canvas/polygon.cc
libs/canvas/rectangle.cc
libs/canvas/root_group.cc
libs/canvas/ruler.cc [new file with mode: 0644]
libs/canvas/scroll_group.cc [new file with mode: 0644]
libs/canvas/stateful_image.cc
libs/canvas/text.cc
libs/canvas/tracking_text.cc [new file with mode: 0644]
libs/canvas/utils.cc
libs/canvas/wave_view.cc
libs/canvas/widget.cc [new file with mode: 0644]
libs/canvas/wscript
libs/canvas/xfade_curve.cc [new file with mode: 0644]
libs/evoral/evoral/ControlList.hpp
libs/evoral/evoral/SMF.hpp
libs/evoral/evoral/types.hpp
libs/evoral/src/ControlList.cpp
libs/evoral/src/Curve.cpp
libs/evoral/src/SMF.cpp
libs/evoral/test/SequenceTest.cpp
libs/fst/scanner.cc
libs/gtkmm2ext/actions.cc
libs/gtkmm2ext/gtk_ui.cc
libs/gtkmm2ext/gtkmm2ext/actions.h
libs/gtkmm2ext/gtkmm2ext/cairo_widget.h
libs/gtkmm2ext/gtkmm2ext/idle_adjustment.h
libs/gtkmm2ext/idle_adjustment.cc
libs/gtkmm2ext/selector.cc
libs/gtkmm2ext/utils.cc
libs/midi++2/mmc.cc
libs/midi++2/test/MidnamTest.cpp
libs/pbd/MSVCpbd/pbd.vcproj
libs/pbd/clear_dir.cc [deleted file]
libs/pbd/file_manager.cc
libs/pbd/file_utils.cc
libs/pbd/pathscanner.cc [deleted file]
libs/pbd/pbd/abstract_ui.cc
libs/pbd/pbd/abstract_ui.h
libs/pbd/pbd/clear_dir.h [deleted file]
libs/pbd/pbd/file_utils.h
libs/pbd/pbd/localtime_r.h
libs/pbd/pbd/pathscanner.h [deleted file]
libs/pbd/pbd/system_exec.h
libs/pbd/search_path.cc
libs/pbd/system_exec.cc
libs/pbd/test/filesystem_test.cc
libs/pbd/test/filesystem_test.h
libs/pbd/test/i18n_test/ardour.tst [new file with mode: 0644]
libs/pbd/test/i18n_test/žar.tst [new file with mode: 0644]
libs/pbd/test/i18n_test/пыл.tst [new file with mode: 0644]
libs/pbd/test/i18n_test/եռանդ.tst [new file with mode: 0644]
libs/pbd/test/i18n_test/ব্যগ্রতা.tst [new file with mode: 0644]
libs/pbd/test/i18n_test/ความกระตือรือร้น.tst [new file with mode: 0644]
libs/pbd/test/i18n_test/情熱.tst [new file with mode: 0644]
libs/pbd/test/i18n_test/热情.tst [new file with mode: 0644]
libs/pbd/test/signals_test.cc
libs/pbd/test/test_common.cc
libs/pbd/test/test_common.h
libs/pbd/test/testrunner.cc
libs/pbd/test/xpath.cc
libs/pbd/wscript
libs/surfaces/frontier/tranzport/tranzport_control_protocol.h
libs/surfaces/generic_midi/generic_midi_control_protocol.cc
libs/surfaces/generic_midi/midicontrollable.cc
libs/surfaces/mackie/device_info.cc
libs/surfaces/mackie/device_profile.cc
libs/surfaces/mackie/timer.h
libs/surfaces/osc/osc.cc
libs/surfaces/tranzport/wheel.cc
libs/timecode/wscript
libs/vfork/exec_wrapper.c
msvc_extra_headers/ardourext/misc.h.input
tools/linux_packaging/build
tools/osx_packaging/osx_build
tools/valgrind.supp
tools/windows_packaging/configure-debug.sh
tools/windows_packaging/configure-distcc-debug.sh
tools/windows_packaging/configure-distcc-release.sh
tools/windows_packaging/configure-release.sh
tools/windows_packaging/copydll-fedora.sh [new file with mode: 0755]
tools/windows_packaging/copydll-waves.sh [new file with mode: 0755]
tools/windows_packaging/mingw-env.sh
tools/windows_packaging/package-f19.sh [new file with mode: 0755]
tools/windows_packaging/package-f20.sh [new file with mode: 0755]
tools/windows_packaging/package.sh
tools/windows_packaging/pango.modules
vst/ardourvst.in
wscript

index 96963a21169dc3d14fdb250723ac8ee7e2fd1af2..c5ad0d9365e2e6aecea0f48d5600e8fde515d3a8 100644 (file)
                                RelativePath="..\gtk2_ardour\group_tabs.cc"
                                >
                        </File>
-                       <File
-                               RelativePath="..\gtk2_ardour\gtk-custom-hruler.c"
-                               >
-                               <FileConfiguration
-                                       Name="Debug 32|Win32"
-                                       >
-                                       <Tool
-                                               Name="VCCLCompilerTool"
-                                               CompileAs="1"
-                                       />
-                               </FileConfiguration>
-                               <FileConfiguration
-                                       Name="Release 32|Win32"
-                                       >
-                                       <Tool
-                                               Name="VCCLCompilerTool"
-                                               CompileAs="1"
-                                       />
-                               </FileConfiguration>
-                               <FileConfiguration
-                                       Name="Release 32 with Debugging Capability|Win32"
-                                       >
-                                       <Tool
-                                               Name="VCCLCompilerTool"
-                                               CompileAs="1"
-                                       />
-                               </FileConfiguration>
-                       </File>
-                       <File
-                               RelativePath="..\gtk2_ardour\gtk-custom-ruler.c"
-                               >
-                               <FileConfiguration
-                                       Name="Debug 32|Win32"
-                                       >
-                                       <Tool
-                                               Name="VCCLCompilerTool"
-                                               CompileAs="1"
-                                       />
-                               </FileConfiguration>
-                               <FileConfiguration
-                                       Name="Release 32|Win32"
-                                       >
-                                       <Tool
-                                               Name="VCCLCompilerTool"
-                                               CompileAs="1"
-                                       />
-                               </FileConfiguration>
-                               <FileConfiguration
-                                       Name="Release 32 with Debugging Capability|Win32"
-                                       >
-                                       <Tool
-                                               Name="VCCLCompilerTool"
-                                               CompileAs="1"
-                                       />
-                               </FileConfiguration>
-                       </File>
                        <File
                                RelativePath="..\gtk2_ardour\gtk_pianokeyboard.c"
                                >
                                RelativePath="..\gtk2_ardour\route_ui.cc"
                                >
                        </File>
+                       <File
+                               RelativePath="..\gtk2_ardour\ruler_dialog.cc"
+                               >
+                       </File>
                        <File
                                RelativePath="..\gtk2_ardour\search_path_option.cc"
                                >
                                RelativePath="..\gtk2_ardour\shuttle_control.cc"
                                >
                        </File>
+                       <File
+                               RelativePath="..\gtk2_ardour\soundcloud_export_selector.cc"
+                               >
+                       </File>
                        <File
                                RelativePath="..\gtk2_ardour\speaker_dialog.cc"
                                >
                                RelativePath="..\gtk2_ardour\group_tabs.h"
                                >
                        </File>
-                       <File
-                               RelativePath="..\gtk2_ardour\gtk-custom-hruler.h"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\gtk2_ardour\gtk-custom-ruler.h"
-                               >
-                       </File>
                        <File
                                RelativePath="..\gtk2_ardour\gtk_pianokeyboard.h"
                                >
                                RelativePath="..\gtk2_ardour\route_ui_selection.h"
                                >
                        </File>
+                       <File
+                               RelativePath="..\gtk2_ardour\ruler_dialog.h"
+                               >
+                       </File>
                        <File
                                RelativePath="..\gtk2_ardour\search_path_option.h"
                                >
                                RelativePath="..\gtk2_ardour\shuttle_control.h"
                                >
                        </File>
+                       <File
+                               RelativePath="..\gtk2_ardour\soundcloud_export_selector.h"
+                               >
+                       </File>
                        <File
                                RelativePath="..\gtk2_ardour\speaker_dialog.h"
                                >
index 3a0b362bed84eae83e80e60d67565b02ba93e5b1..05226c03baa90e02e33f23a1123de9ab2c7da42a 100644 (file)
@@ -30,7 +30,6 @@
 #include "ardour/version.h"
 #include "ardour/filesystem_paths.h"
 
-#include "utils.h"
 #include "version.h"
 
 #include "about.h"
@@ -565,7 +564,7 @@ About::About ()
 
        Searchpath spath(ardour_data_search_path());
 
-       if (find_file_in_search_path (spath, "splash.png", splash_file)) {
+       if (find_file (spath, "splash.png", splash_file)) {
                set_logo (Gdk::Pixbuf::create_from_file (splash_file));
        } else {
                error << "Could not find splash file" << endmsg;
index 4c4117edcbb9fd46da10ee68dec341ed9bb4bb8b..f208c6c8d3516486e431db7d831cb1024e90d266 100644 (file)
@@ -36,7 +36,6 @@
 
 #include "gtkmm2ext/actions.h"
 
-#include "utils.h"
 #include "actions.h"
 #include "i18n.h"
 
@@ -76,7 +75,7 @@ ActionManager::load_menus (const string& menus_file)
 {
        std::string ui_file;
 
-       find_file_in_search_path (ardour_config_search_path(), menus_file, ui_file);
+       find_file (ardour_config_search_path(), menus_file, ui_file);
 
        bool loaded = false;
 
index 8837e9f468f567b6fd3dbea0013f1caf59fc53f4..94e4369aae3432cd97f979a015541eafd23bdddc 100644 (file)
@@ -46,6 +46,7 @@ using namespace Gtkmm2ext;
 using namespace std;
 using namespace PBD;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 
 std::vector<std::string> AddRouteDialog::channel_combo_strings;
 
index 5c58b7abcd4b71da8f98b4c44620c4e77cbe3414..b8ede24ae3590b04d4a709a23c4c8a06e79c7e8c 100644 (file)
@@ -33,7 +33,6 @@
 #include "ardour/session.h"
 #include "ardour_ui.h"
 
-#include "utils.h"
 #include "add_video_dialog.h"
 #include "utils_videotl.h"
 #include "i18n.h"
index 203df3ad3dc122f2575931a5379b3e3b6258ec34..3d5ff540305d9e40db49d672dad6a2ed0fe93406 100644 (file)
 
 #include <string>
 
+#ifdef interface
+#undef interface
+#endif
+
 #include <gtkmm.h>
 
 #include "ardour/types.h"
index d65a287701b3c83fa471417a7d253328c07c40cf..30723722e28422e578c0555120e41bddd4bf2255 100644 (file)
@@ -17,7 +17,8 @@ export ARDOUR_DATA_PATH=$TOP:$TOP/build:$TOP/gtk2_ardour:$TOP/build/gtk2_ardour:
 export ARDOUR_MIDIMAPS_PATH=$TOP/midi_maps:.
 export ARDOUR_MCP_PATH=$TOP/mcp:.
 export ARDOUR_EXPORT_FORMATS_PATH=$TOP/export:.
-export ARDOUR_BACKEND_PATH=$libs/backends/jack:$libs/backends/wavesaudio:$libs/backends/dummy
+export ARDOUR_BACKEND_PATH=$libs/backends/jack:$libs/backends/wavesaudio:$libs/backends/dummy:$libs/backends/alsa
+export ARDOUR_TEST_PATH=$libs/ardour/test/data
 
 #
 # even though we set the above variables, ardour requires that these 
@@ -30,7 +31,7 @@ export ARDOUR_DLL_PATH=$libs
 export GTK_PATH=~/.ardour3:$libs/clearlooks-newer
 export VAMP_PATH=$libs/vamp-plugins${VAMP_PATH:+:$VAMP_PATH}
 
-export LD_LIBRARY_PATH=$libs/qm-dsp:$libs/vamp-sdk:$libs/surfaces:$libs/surfaces/control_protocol:$libs/ardour:$libs/midi++2:$libs/pbd:$libs/rubberband:$libs/soundtouch:$libs/gtkmm2ext:$libs/gnomecanvas:$libs/libsndfile:$libs/appleutility:$libs/taglib:$libs/evoral:$libs/evoral/src/libsmf:$libs/audiographer:$libs/timecode:$libs/libltc:$libs/canvas${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
+export LD_LIBRARY_PATH=$libs/qm-dsp:$libs/vamp-sdk:$libs/surfaces:$libs/surfaces/control_protocol:$libs/ardour:$libs/midi++2:$libs/pbd:$libs/rubberband:$libs/soundtouch:$libs/gtkmm2ext:$libs/gnomecanvas:$libs/libsndfile:$libs/appleutility:$libs/taglib:$libs/evoral:$libs/evoral/src/libsmf:$libs/audiographer:$libs/timecode:$libs/libltc:$libs/canvas:$libs/ardouralsautil${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
 
 # DYLD_LIBRARY_PATH is for darwin.
 export DYLD_FALLBACK_LIBRARY_PATH=$LD_LIBRARY_PATH
index 6d08937cf05547e162e8360fd2425ddb9d693012..239aa372173d00aa3aa4b5e4f0cc4782cb10083b 100644 (file)
@@ -12,6 +12,9 @@
   <accelerator action='track-mute-toggle'/>
   <accelerator action='toggle-edit-mode'/>
   <accelerator action='toggle-midi-input-active'/>
+#ifdef GTKOSX
+  <accelerator action='Quit'/>
+#endif
   
   <menubar name='Main' action='MainMenu'>
     <menu name='Session' action='Session'>
@@ -53,6 +56,8 @@
       <menuitem action='toggle-about'/>
       <menuitem action='toggle-rc-options-editor'/>
 #endif
+      <separator/>
+      <menuitem action='lock'/>
 #ifndef GTKOSX
       <separator/>
       <menuitem action='Quit'/>
          <menuitem action='combine-regions'/>
          <menuitem action='uncombine-regions'/>
          <menuitem action='analyze-region'/>
-         <menuitem action='toggle-opaque-region'/>
-         <menuitem action='toggle-region-mute'/>
          <menuitem action='pitch-shift-region'/>
          <menuitem action='split-region'/>
          <menuitem action='split-multichannel-region'/>
         <menuitem action='show-region-list-editor'/>
       </menu>
       <menu action='RegionMenuGain'>
+        <menuitem action='toggle-opaque-region'/>
+        <menuitem action='toggle-region-mute'/>
         <menuitem action='normalize-region'/>
         <menuitem action='boost-region-gain'/>
         <menuitem action='cut-region-gain'/>                      
        <menuitem action='uncombine-regions'/>
        <menuitem action='split-region'/>
        <menuitem action='split-multichannel-region'/>
-       <menuitem action='toggle-opaque-region'/>
-       <menuitem action='toggle-region-mute'/>
        <menuitem action='pitch-shift-region'/>
        <menuitem action='reverse-region'/>    
        <menuitem action='close-region-gaps'/>
       <menuitem action='set-selection-from-region'/>
     </menu>
     <menu action='RegionMenuGain'>
+      <menuitem action='toggle-opaque-region'/>
+      <menuitem action='toggle-region-mute'/>
       <menuitem action='normalize-region'/>
       <menuitem action='boost-region-gain'/>
       <menuitem action='cut-region-gain'/>                        
index 04a9585b64d35d4729248a359bee5b979c7777e0..16061f5c09bab3e114a2756fdd032915e75f5a26 100644 (file)
@@ -5,9 +5,9 @@
 # and does a few checks before exec'ing the real executable.
 # 
 
-export GTK_PATH=@SYSCONFDIR@/ardour3:@LIBDIR@/ardour3${GTK_PATH:+:$GTK_PATH}
+export GTK_PATH=@CONFDIR@:@LIBDIR@${GTK_PATH:+:$GTK_PATH}
 
-export LD_LIBRARY_PATH=@LIBDIR@/ardour3${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
+export LD_LIBRARY_PATH=@LIBDIR@${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
 
 ## Memlock check
 
@@ -23,7 +23,7 @@ fi
 
 ## Glib atomic test
 
-GLIB=$(ldd @LIBDIR@/ardour3/ardour-@VERSION@ 2> /dev/null | grep glib-2.0 | sed 's/.*=> \([^ ]*\) .*/\1/')
+GLIB=$(ldd @LIBDIR@/ardour-@VERSION@ 2> /dev/null | grep glib-2.0 | sed 's/.*=> \([^ ]*\) .*/\1/')
 
 if [ "$GLIB" = "" ]; then
        echo "WARNING: Could not check your glib-2.0 for mutex locking atomic operations."
@@ -43,15 +43,15 @@ fi
 # Running Ardour requires these 3 variables to be set
 #
 
-export ARDOUR_DATA_PATH=@DATADIR@/ardour3
-export ARDOUR_CONFIG_PATH=@SYSCONFDIR@/ardour3
-export ARDOUR_DLL_PATH=@LIBDIR@/ardour3
+export ARDOUR_DATA_PATH=@DATADIR@
+export ARDOUR_CONFIG_PATH=@CONFDIR@
+export ARDOUR_DLL_PATH=@LIBDIR@
 
 #
 # VAMP has its own lookup path
 # 
 
-export VAMP_PATH=@LIBDIR@/ardour3/vamp
+export VAMP_PATH=@LIBDIR@/vamp
 
 if [ $# -gt 0 ] ; then
     case $1 in
@@ -59,6 +59,6 @@ if [ $# -gt 0 ] ; then
     esac
 fi
 
-exec $GDB @LIBDIR@/ardour3/ardour-@VERSION@ "$@"
+exec $GDB @LIBDIR@/ardour-@VERSION@ "$@"
 
 
index 8314e9dc36cf4cb872270fda6308d2a268673b14..c510a372e8919984a45cf33446bd52708a0f9116 100644 (file)
@@ -151,6 +151,10 @@ style "meterbridge_label" = "small_text"
 {
 }
 
+style "midi_device" = "very_small_text"
+{
+}
+
 style "solo_isolate" = "very_small_text"
 {
 }
@@ -159,6 +163,10 @@ style "solo_safe" = "very_small_text"
 {
 }
 
+style "tracknumber_label" = "medium_monospace_text"
+{
+}
+
 style "solo_button" = "small_button"
 {
   bg[NORMAL] = mix(0.1,@@COLPREFIX@_solo,@@COLPREFIX@_bg)
@@ -672,13 +680,6 @@ style "small_red_on_black_entry"  = "small_bold_text"
        bg[ACTIVE] = @@COLPREFIX@_base
 }
 
-style "editor_time_ruler" = "small_text"
-{
-       fg[NORMAL] = @@COLPREFIX@_fg
-       bg[NORMAL] = @@COLPREFIX@_base
-        ythickness = 0
-}
-
 style "audio_bus_base" = "very_small_text"
 {
   fg[NORMAL] = @@COLPREFIX@_fg
diff --git a/gtk2_ardour/ardour3_ui_default.conf b/gtk2_ardour/ardour3_ui_default.conf
deleted file mode 100644 (file)
index da361ec..0000000
+++ /dev/null
@@ -1,544 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Ardour>
-  <UI>
-    <Option name="ui-rc-file" value="ardour3_ui_dark.rc"/>
-    <Option name="flat-buttons" value="00000000"/>
-    <Option name="waveform-gradient-depth" value="0"/>
-    <Option name="timeline-item-gradient-depth" value="00000.95"/>
-    <Option name="all-floating-windows-are-dialogs" value="00000000"/>
-    <Option name="color-regions-using-track-color" value="00000000"/>
-    <Option name="show-waveform-clipping" value="00000001"/>
-  </UI>
-  <Canvas>
-    <Option name="active crossfade" value="20b2af2e"/>
-    <Option name="audio bus base" value="73829968"/>
-    <Option name="audio master bus base" value="00000000"/>
-    <Option name="audio track base" value="9daac468"/>
-    <Option name="automation line" value="44bc59ff"/>
-    <Option name="automation track fill" value="a0a0ce68"/>
-    <Option name="automation track outline" value="282828ff"/>
-    <Option name="cd marker bar" value="9496a3cc"/>
-    <Option name="crossfade editor base" value="282d49ff"/>
-    <Option name="crossfade editor line" value="000000ff"/>
-    <Option name="crossfade editor line shading" value="00a0d154"/>
-    <Option name="crossfade editor point fill" value="00ff00ff"/>
-    <Option name="crossfade editor point outline" value="0000ffff"/>
-    <Option name="crossfade editor wave" value="ffffff28"/>
-    <Option name="selected crossfade editor wave fill" value="00000000"/>
-    <Option name="crossfade line" value="000000ff"/>
-    <Option name="edit point" value="0000ffff"/>
-    <Option name="entered automation line" value="dd6363ff"/>
-    <Option name="control point fill" value="ffffff66"/>
-    <Option name="control point outline" value="ff0000ee"/>
-    <Option name="control point selected" value="55ccccff"/>
-    <Option name="entered gain line" value="dd6363ff"/>
-    <Option name="entered marker" value="dd6363ff"/>
-    <Option name="frame handle" value="7c00ff96"/>
-    <Option name="gain line" value="00bc20ff"/>
-    <Option name="gain line inactive" value="9fbca4c5"/>
-    <Option name="ghost track base" value="44007c7f"/>
-    <Option name="ghost track midi outline" value="00000000"/>
-    <Option name="ghost track wave" value="02fd004c"/>
-    <Option name="ghost track wave fill" value="00000000"/>
-    <Option name="ghost track wave clip" value="ff000000"/>
-    <Option name="ghost track zero line" value="e500e566"/>
-    <Option name="image track" value="ddddd8ff"/>
-    <Option name="inactive crossfade" value="e8ed3d77"/>
-    <Option name="inactive fade handle" value="bbbbbbaa"/>
-    <Option name="location cd marker" value="1ee8c4ff"/>
-    <Option name="location loop" value="35964fff"/>
-    <Option name="location marker" value="c4f411ff"/>
-    <Option name="location punch" value="7c3a3aff"/>
-    <Option name="location range" value="497a59ff"/>
-    <Option name="marker bar" value="99a1adcc"/>
-    <Option name="marker bar separator" value="555555ff"/>
-    <Option name="marker drag line" value="004f00f9"/>
-    <Option name="marker label" value="000000ff"/>
-    <Option name="marker track" value="ddddd8ff"/>
-    <Option name="measure line bar" value="ffffff9c"/>
-    <Option name="measure line beat" value="a29e9e76"/>
-    <Option name="meter bar" value="626470cc"/>
-    <Option name="meter fill: 0" value="008800ff"/>
-    <Option name="meter fill: 1" value="008800ff"/>
-    <Option name="meter fill: 2" value="00ff00ff"/>
-    <Option name="meter fill: 3" value="00ff00ff"/>
-    <Option name="meter fill: 4" value="fff000ff"/>
-    <Option name="meter fill: 5" value="fff000ff"/>
-    <Option name="meter fill: 6" value="ff8000ff"/>
-    <Option name="meter fill: 7" value="ff8000ff"/>
-    <Option name="meter fill: 8" value="ff0000ff"/>
-    <Option name="meter fill: 9" value="ff0000ff"/>
-    <Option name="meter background: bottom" value="333333ff"/>
-    <Option name="meter background: top" value="444444ff"/>
-    <Option name="midi meter fill: 0" value="effaa1ff"/>
-    <Option name="midi meter fill: 1" value="f2c97dff"/>
-    <Option name="midi meter fill: 2" value="f2c97dff"/>
-    <Option name="midi meter fill: 3" value="f48f52ff"/>
-    <Option name="midi meter fill: 4" value="f48f52ff"/>
-    <Option name="midi meter fill: 5" value="f83913ff"/>
-    <Option name="midi meter fill: 6" value="f83913ff"/>
-    <Option name="midi meter fill: 7" value="8fc78eff"/>
-    <Option name="midi meter fill: 8" value="8fc78eff"/>
-    <Option name="midi meter fill: 9" value="00f45600"/>
-    <Option name="meter background: bottom" value="333333ff"/>
-    <Option name="meter background: top" value="444444ff"/>
-    <Option name="meterbridge peakindicator: fill start" value="444444ff"/>
-    <Option name="meterbridge peakindicator: fill end" value="333333ff"/>
-    <Option name="meterbridge peakindicator on: fill start" value="ff0000ff"/>
-    <Option name="meterbridge peakindicator on: fill end" value="880000ff"/>
-    <Option name="meterbridge label: fill start" value="444444ff"/>
-    <Option name="meterbridge label: fill end" value="333333ff"/>
-    <Option name="meterbridge label: text" value="c7c7d8ff"/>
-    <Option name="meter marker" value="f2425bff"/>
-    <Option name="midi bus base" value="00000000"/>
-    <Option name="midi frame base" value="393d3766"/>
-    <Option name="midi note inactive channel" value="00000000"/>
-    <Option name="midi note color min" value="3f542aff"/>
-    <Option name="midi note color mid" value="7ea854ff"/>
-    <Option name="midi note color max" value="bfff80ff"/>
-    <Option name="selected midi note color min" value="1e1e33ff"/>
-    <Option name="selected midi note color mid" value="51518aff"/>
-    <Option name="selected midi note color max" value="8383deff"/>
-    <Option name="midi note selected" value="b2b2ffff"/>
-    <Option name="midi note velocity text" value="f4f214bc"/>
-    <Option name="midi patch change fill" value="50555aa0"/>
-    <Option name="midi patch change outline" value="c0c5caff"/>
-    <Option name="midi patch change inactive channel fill" value="50555ac0"/>
-    <Option name="midi patch change inactive channel outline" value="20252ac0"/>
-    <Option name="midi sysex fill" value="f1e139a0"/>
-    <Option name="midi sysex outline" value="a7a7d4ff"/>
-    <Option name="midi select rect fill" value="8888ff88"/>
-    <Option name="midi select rect outline" value="5555ffff"/>
-    <Option name="midi track base" value="b3cca35f"/>
-    <Option name="name highlight fill" value="0000ffff"/>
-    <Option name="name highlight outline" value="7c00ff96"/>
-    <Option name="piano roll black outline" value="f4f4f476"/>
-    <Option name="piano roll black" value="6c6e6a6b"/>
-    <Option name="piano roll white" value="979b9565"/>
-    <Option name="play head" value="ff0000ff"/>
-    <Option name="processor automation line" value="7aa3f9ff"/>
-    <Option name="punch line" value="a80000ff"/>
-    <Option name="range drag bar rect" value="969696c6"/>
-    <Option name="range drag rect" value="82c696c6"/>
-    <Option name="range marker bar" value="7d7f8ccc"/>
-    <Option name="recording rect" value="cc2828ff"/>
-    <Option name="recorded waveform fill" value="ffffffff"/>
-    <Option name="recorded waveform outline" value="0f0f1fff"/>
-    <Option name="rubber band rect" value="c6c6c659"/>
-    <Option name="selected crossfade editor line" value="00dbdbff"/>
-    <Option name="selected crossfade editor wave" value="f9ea14a0"/>
-    <Option name="selected region base" value="51518a97"/>
-    <Option name="selected waveform fill" value="25e2e9c8"/>
-    <Option name="selected waveform outline" value="0f0f0fcc"/>
-    <Option name="selection rect" value="e8f4d377"/>
-    <Option name="selection" value="636363b2"/>
-    <Option name="shuttle" value="6bb620ff"/>
-    <Option name="silence" value="9efffd7a"/>
-    <Option name="silence text" value="0e066cff"/>
-    <Option name="mono panner outline" value="33445eff"/>
-    <Option name="mono panner fill" value="7a9bccc9"/>
-    <Option name="mono panner text" value="000000ff"/>
-    <Option name="mono panner bg" value="2e2929ff"/>
-    <Option name="mono panner position fill" value="7a89b3ff"/>
-    <Option name="mono panner position outline" value="33445eff"/>
-    <Option name="stereo panner outline" value="33445eff"/>
-    <Option name="stereo panner fill" value="7a9accc9"/>
-    <Option name="stereo panner text" value="000000ff"/>
-    <Option name="stereo panner bg" value="2e2929ff"/>
-    <Option name="stereo panner rule" value="455c7fff"/>
-    <Option name="stereo panner mono outline" value="a05600ff"/>
-    <Option name="stereo panner mono fill" value="e99668ca"/>
-    <Option name="stereo panner mono text" value="000000ff"/>
-    <Option name="stereo panner mono bg" value="2e2929ff"/>
-    <Option name="stereo panner inverted outline" value="bf0a00ff"/>
-    <Option name="stereo panner inverted fill" value="e4a19cc9"/>
-    <Option name="stereo panner inverted text" value="000000ff"/>
-    <Option name="stereo panner inverted bg" value="2e2929ff"/>
-    <Option name="tempo bar" value="70727fcc"/>
-    <Option name="tempo marker" value="f2425bff"/>
-    <Option name="time axis frame" value="000000ff"/>
-    <Option name="selected time axis frame" value="000000ff"/>
-    <Option name="time stretch fill" value="e2b5b596"/>
-    <Option name="time stretch outline" value="63636396"/>
-    <Option name="transport drag rect" value="969696c6"/>
-    <Option name="transport loop rect" value="1e7728f9"/>
-    <Option name="transport marker bar" value="8c8e98cc"/>
-    <Option name="transport punch rect" value="6d2828e5"/>
-    <Option name="trim handle locked" value="ea0f0f28"/>
-    <Option name="trim handle" value="1900ff44"/>
-    <Option name="verbose canvas cursor" value="fffd2ebc"/>
-    <Option name="vestigial frame" value="0000000f"/>
-    <Option name="video timeline bar" value="303030ff"/>
-    <Option name="region base" value="99a7b5a0"/>
-    <Option name="region area covered by another region" value="505050b0"/>
-    <Option name="waveform outline" value="000000ff"/>
-    <Option name="clipped waveform" value="ff0000e5"/>
-    <Option name="waveform fill" value="ffffffff"/>
-    <Option name="zero line" value="7f7f7f58"/>
-    <Option name="zoom rect" value="c6d1b26d"/>
-    <Option name="monitor knob" value="329edfff"/>
-    <Option name="button border" value="000000f0"/>
-    <Option name="border color" value="00000000"/>
-    <Option name="processor prefader: fill start" value="873c3cff"/>
-    <Option name="processor prefader: fill end" value="542525ff"/>
-    <Option name="processor prefader: fill start active" value="774c4cff"/>
-    <Option name="processor prefader: fill end active" value="603535ff"/>
-    <Option name="processor prefader: led" value="26550eff"/>
-    <Option name="processor prefader: led active" value="78cb4eff"/>
-    <Option name="processor prefader: text" value="aaaaa3ff"/>
-    <Option name="processor prefader: text active" value="eeeeecff"/>
-    <Option name="processor fader: fill start" value="5d90b0ff"/>
-    <Option name="processor fader: fill end" value="154c6eff"/>
-    <Option name="processor fader: fill start active" value="5d90b0ff"/>
-    <Option name="processor fader: fill end active" value="256d8fff"/>
-    <Option name="processor fader: led" value="26550eff"/>
-    <Option name="processor fader: led active" value="78cb4eff"/>
-    <Option name="processor fader: text" value="aaaaa3ff"/>
-    <Option name="processor fader: text active" value="eeeeecff"/>
-    <Option name="processor postfader: fill start" value="354537ff"/>
-    <Option name="processor postfader: fill end" value="202823ff"/>
-    <Option name="processor postfader: fill start active" value="466452ff"/>
-    <Option name="processor postfader: fill end active" value="254528ff"/>
-    <Option name="processor postfader: led" value="26550eff"/>
-    <Option name="processor postfader: led active" value="78cb4eff"/>
-    <Option name="processor postfader: text" value="aaaaa3ff"/>
-    <Option name="processor postfader: text active" value="eeeeecff"/>
-    <Option name="processor control button: fill start" value="222222ff"/>
-    <Option name="processor control button: fill end" value="333333ff"/>
-    <Option name="processor control button: fill start active" value="444444ff"/>
-    <Option name="processor control button: fill end active" value="333333ff"/>
-    <Option name="processor control button: led" value="224400ff"/>
-    <Option name="processor control button: led active" value="99cc00ff"/>
-    <Option name="processor control button: text" value="ffffffff"/>
-    <Option name="processor control button: text active" value="ffffffff"/>
-    <Option name="monitor button: fill start" value="5f5a58ff"/>
-    <Option name="monitor button: fill end" value="4f4a48ff"/>
-    <Option name="monitor button: fill start active" value="553500ff"/>
-    <Option name="monitor button: fill end active" value="e58505ff"/>
-    <Option name="monitor button: led" value="660000ff"/>
-    <Option name="monitor button: led active" value="ff0000ff"/>
-    <Option name="monitor button: text" value="aaaaa3ff"/>
-    <Option name="monitor button: text active" value="1a1a1aff"/>
-    <Option name="meterbridge label: fill start" value="444444ff"/>
-    <Option name="meterbridge label: fill end" value="333333ff"/>
-    <Option name="meterbridge label: text" value="c7c7d8ff"/>
-    <Option name="solo isolate: fill start" value="5f5a58ff"/>
-    <Option name="solo isolate: fill end" value="504442ff"/>
-    <Option name="solo isolate: fill start active" value="5d5856ff"/>
-    <Option name="solo isolate: fill end active" value="564d48ff"/>
-    <Option name="solo isolate: led" value="660000ff"/>
-    <Option name="solo isolate: led active" value="ff0000ff"/>
-    <Option name="solo isolate: text" value="c7c7d8ff"/>
-    <Option name="solo isolate: text active" value="c8c8d9ff"/>
-    <Option name="solo safe: fill start" value="5f5a58ff"/>
-    <Option name="solo safe: fill end" value="504442ff"/>
-    <Option name="solo safe: fill start active" value="5d5856ff"/>
-    <Option name="solo safe: fill end active" value="564d48ff"/>
-    <Option name="solo safe: led" value="660000ff"/>
-    <Option name="solo safe: led active" value="ff0000ff"/>
-    <Option name="solo safe: text" value="c7c7d8ff"/>
-    <Option name="solo safe: text active" value="c8c8d9ff"/>
-    <Option name="meterbridge peaklabel" value="ff1111ff"/>
-    <Option name="meter color BBC" value="ffa500ff"/>
-    <Option name="meterbridge peakindicator: fill start" value="444444ff"/>
-    <Option name="meterbridge peakindicator: fill end" value="333333ff"/>
-    <Option name="meterbridge peakindicator on: fill start" value="ff0000ff"/>
-    <Option name="meterbridge peakindicator on: fill end" value="880000ff"/>
-    <Option name="monitor section cut: fill start" value="5f5a58ff"/>
-    <Option name="monitor section cut: fill end" value="4f4a48ff"/>
-    <Option name="monitor section cut: fill start active" value="5f4943ff"/>
-    <Option name="monitor section cut: fill end active" value="ffa500ff"/>
-    <Option name="monitor section cut: led" value="473812ff"/>
-    <Option name="monitor section cut: led active" value="78cb4eff"/>
-    <Option name="monitor section cut: text" value="c7c7d8ff"/>
-    <Option name="monitor section cut: text active" value="000000ff"/>
-    <Option name="monitor section dim: fill start" value="5f5a58ff"/>
-    <Option name="monitor section dim: fill end" value="4f4a48ff"/>
-    <Option name="monitor section dim: fill start active" value="553500ff"/>
-    <Option name="monitor section dim: fill end active" value="e58505ff"/>
-    <Option name="monitor section dim: led" value="00000000"/>
-    <Option name="monitor section dim: led active" value="78cb4eff"/>
-    <Option name="monitor section dim: text" value="c8c8d9ff"/>
-    <Option name="monitor section dim: text active" value="c8c8d9ff"/>
-    <Option name="monitor section solo: fill start" value="5f5a58ff"/>
-    <Option name="monitor section solo: fill end" value="4f4a48ff"/>
-    <Option name="monitor section solo: fill start active" value="104506ff"/>
-    <Option name="monitor section solo: fill end active" value="4dbb00ff"/>
-    <Option name="monitor section solo: led" value="473812ff"/>
-    <Option name="monitor section solo: led active" value="ffa500ff"/>
-    <Option name="monitor section solo: text" value="00000000"/>
-    <Option name="monitor section solo: text active" value="00000000"/>
-    <Option name="monitor section invert: fill start" value="5f5a58ff"/>
-    <Option name="monitor section invert: fill end" value="4f4a48ff"/>
-    <Option name="monitor section invert: fill start active" value="222260ff"/>
-    <Option name="monitor section invert: fill end active" value="4242d0ff"/>
-    <Option name="monitor section invert: led" value="473812ff"/>
-    <Option name="monitor section invert: led active" value="78cb4eff"/>
-    <Option name="monitor section invert: text" value="00000000"/>
-    <Option name="monitor section invert: text active" value="00000000"/>
-    <Option name="monitor section mono: fill start" value="5f5a58ff"/>
-    <Option name="monitor section mono: fill end" value="4f4a48ff"/>
-    <Option name="monitor section mono: fill start active" value="222260ff"/>
-    <Option name="monitor section mono: fill end active" value="3232c0ff"/>
-    <Option name="monitor section mono: led" value="473812ff"/>
-    <Option name="monitor section mono: led active" value="78cb4eff"/>
-    <Option name="monitor section mono: text" value="c7c7d8ff"/>
-    <Option name="monitor section mono: text active" value="c8c8d9ff"/>
-    <Option name="monitor section solo model: fill start" value="5d5856ff"/>
-    <Option name="monitor section solo model: fill end" value="564d48ff"/>
-    <Option name="monitor section solo model: fill start active" value="5d5856ff"/>
-    <Option name="monitor section solo model: fill end active" value="564d48ff"/>
-    <Option name="monitor section solo model: led" value="4f3300ff"/>
-    <Option name="monitor section solo model: led active" value="ffa500ff"/>
-    <Option name="monitor section solo model: text" value="c7c7d8ff"/>
-    <Option name="monitor section solo model: text active" value="c8c8d9ff"/>
-    <Option name="monitor solo override: fill start" value="5d5856ff"/>
-    <Option name="monitor solo override: fill end" value="564d48ff"/>
-    <Option name="monitor solo override: fill start active" value="5d5856ff"/>
-    <Option name="monitor solo override: fill end active" value="564d48ff"/>
-    <Option name="monitor solo override: led" value="4f3300ff"/>
-    <Option name="monitor solo override: led active" value="ffa500ff"/>
-    <Option name="monitor solo override: text" value="c7c7d8ff"/>
-    <Option name="monitor solo override: text active" value="c8c8d9ff"/>
-    <Option name="monitor solo exclusive: fill start" value="5d5856ff"/>
-    <Option name="monitor solo exclusive: fill end" value="564d48ff"/>
-    <Option name="monitor solo exclusive: fill start active" value="5d5856ff"/>
-    <Option name="monitor solo exclusive: fill end active" value="564c47ff"/>
-    <Option name="monitor solo exclusive: led" value="4f3300ff"/>
-    <Option name="monitor solo exclusive: led active" value="ffa500ff"/>
-    <Option name="monitor solo exclusive: text" value="c7c7d8ff"/>
-    <Option name="monitor solo exclusive: text active" value="c8c8d9ff"/>
-    <Option name="rude solo: fill start" value="684d4dff"/>
-    <Option name="rude solo: fill end" value="513c3cff"/>
-    <Option name="rude solo: fill start active" value="ff1f1fff"/>
-    <Option name="rude solo: fill end active" value="e21b1bff"/>
-    <Option name="rude solo: led" value="00000000"/>
-    <Option name="rude solo: led active" value="00000000"/>
-    <Option name="rude solo: text" value="969696ff"/>
-    <Option name="rude solo: text active" value="e5e5e5ff"/>
-    <Option name="rude isolate: fill start" value="21414fff"/>
-    <Option name="rude isolate: fill end" value="192930ff"/>
-    <Option name="rude isolate: fill start active" value="e5f7ffff"/>
-    <Option name="rude isolate: fill end active" value="b6e5fdff"/>
-    <Option name="rude isolate: led" value="00000000"/>
-    <Option name="rude isolate: led active" value="000000ff"/>
-    <Option name="rude isolate: text" value="979797ff"/>
-    <Option name="rude isolate: text active" value="000000ff"/>
-    <Option name="rude audition: fill start" value="684d4dff"/>
-    <Option name="rude audition: fill end" value="513c3cff"/>
-    <Option name="rude audition: fill start active" value="ff1f1fff"/>
-    <Option name="rude audition: fill end active" value="e21b1bff"/>
-    <Option name="rude audition: led" value="00000000"/>
-    <Option name="rude audition: led active" value="00000000"/>
-    <Option name="rude audition: text" value="979797ff"/>
-    <Option name="rude audition: text active" value="ffffffff"/>
-    <Option name="feedback alert: fill start" value="684d4dff"/>
-    <Option name="feedback alert: fill end" value="513c3cff"/>
-    <Option name="feedback alert: fill start active" value="ff1f1fff"/>
-    <Option name="feedback alert: fill end active" value="e21b1bff"/>
-    <Option name="feedback alert: led" value="00000000"/>
-    <Option name="feedback alert: led active" value="00000000"/>
-    <Option name="feedback alert: text" value="969696ff"/>
-    <Option name="feedback alert: text active" value="e5e5e5ff"/>
-    <Option name="mute button: fill start" value="565659ff"/>
-    <Option name="mute button: fill end" value="484853ff"/>
-    <Option name="mute button: fill start active" value="5f4943ff"/>
-    <Option name="mute button: fill end active" value="ffff00ff"/>
-    <Option name="mute button: led" value="00000000"/>
-    <Option name="mute button: led active" value="00000000"/>
-    <Option name="mute button: text" value="bfbfafff"/>
-    <Option name="mute button: text active" value="191919ff"/>
-    <Option name="solo button: fill start" value="565659ff"/>
-    <Option name="solo button: fill end" value="484853ff"/>
-    <Option name="solo button: fill start active" value="1d7a05ff"/>
-    <Option name="solo button: fill end active" value="4dbb00ff"/>
-    <Option name="solo button: led" value="00000000"/>
-    <Option name="solo button: led active" value="00000000"/>
-    <Option name="solo button: text" value="afbfafff"/>
-    <Option name="solo button: text active" value="191919ff"/>
-    <Option name="invert button: fill start" value="565659ff"/>
-    <Option name="invert button: fill end" value="484853ff"/>
-    <Option name="invert button: fill start active" value="222260ff"/>
-    <Option name="invert button: fill end active" value="4242d0ff"/>
-    <Option name="invert button: led" value="473812ff"/>
-    <Option name="invert button: led active" value="78cb4eff"/>
-    <Option name="invert button: text" value="bfbfbfff"/>
-    <Option name="invert button: text active" value="bfbfbfff"/>
-    <Option name="record enable button: fill start" value="3e312fff"/>
-    <Option name="record enable button: fill end" value="3f312fff"/>
-    <Option name="record enable button: fill start active" value="c10b0bff"/>
-    <Option name="record enable button: fill end active" value="fd0000ff"/>
-    <Option name="record enable button: led" value="7b3541ff"/>
-    <Option name="record enable button: led active" value="ffa3b3ff"/>
-    <Option name="record enable button: text" value="a5a5a5ff"/>
-    <Option name="record enable button: text active" value="000000ff"/>
-    <Option name="generic button: fill start" value="3e312fff"/>
-    <Option name="generic button: fill end" value="3f312fff"/>
-    <Option name="generic button: fill start active" value="c10b0bff"/>
-    <Option name="generic button: fill end active" value="fd0000ff"/>
-    <Option name="generic button: led" value="7b3541ff"/>
-    <Option name="generic button: led active" value="ffa3b3ff"/>
-    <Option name="generic button: text" value="ff0000ff"/>
-    <Option name="generic button: text active" value="000000ff"/>
-    <Option name="send alert button: fill start" value="4e5647ff"/>
-    <Option name="send alert button: fill end" value="43493cff"/>
-    <Option name="send alert button: fill start active" value="91f928ff"/>
-    <Option name="send alert button: fill end active" value="85e524ff"/>
-    <Option name="send alert button: led" value="00000000"/>
-    <Option name="send alert button: led active" value="00000000"/>
-    <Option name="send alert button: text" value="ccccccff"/>
-    <Option name="send alert button: text active" value="000000ff"/>
-    <Option name="transport button: fill start" value="616268ff"/>
-    <Option name="transport button: fill end" value="505159ff"/>
-    <Option name="transport button: fill start active" value="1d7a05ff"/>
-    <Option name="transport button: fill end active" value="00a300ff"/>
-    <Option name="transport button: led" value="00000000"/>
-    <Option name="transport button: led active" value="00000000"/>
-    <Option name="transport button: text" value="00000000"/>
-    <Option name="transport button: text active" value="00000000"/>
-    <Option name="transport recenable button: fill start" value="5f3f3fff"/>
-    <Option name="transport recenable button: fill end" value="3d2828ff"/>
-    <Option name="transport recenable button: fill start active" value="6a0404ff"/>
-    <Option name="transport recenable button: fill end active" value="b50e0eff"/>
-    <Option name="transport recenable button: led" value="00000000"/>
-    <Option name="transport recenable button: led active" value="00000000"/>
-    <Option name="transport recenable button: text" value="00000000"/>
-    <Option name="transport recenable button: text active" value="00000000"/>
-    <Option name="transport option button: fill start" value="636470ff"/>
-    <Option name="transport option button: fill end" value="54555dff"/>
-    <Option name="transport option button: fill start active" value="636470ff"/>
-    <Option name="transport option button: fill end active" value="4a4b51ff"/>
-    <Option name="transport option button: led" value="4f3300ff"/>
-    <Option name="transport option button: led active" value="ffa500ff"/>
-    <Option name="transport option button: text" value="c7c7d8ff"/>
-    <Option name="transport option button: text active" value="c8c8d9ff"/>
-    <Option name="transport active option button: fill start" value="606b60ff"/>
-    <Option name="transport active option button: fill end" value="495348ff"/>
-    <Option name="transport active option button: fill start active" value="154515ff"/>
-    <Option name="transport active option button: fill end active" value="20a320ff"/>
-    <Option name="transport active option button: led" value="4f3300ff"/>
-    <Option name="transport active option button: led active" value="ffa500ff"/>
-    <Option name="transport active option button: text" value="c7c7d8ff"/>
-    <Option name="transport active option button: text active" value="000000ff"/>
-    <Option name="plugin bypass button: fill start" value="5d5856ff"/>
-    <Option name="plugin bypass button: fill end" value="564d48ff"/>
-    <Option name="plugin bypass button: fill start active" value="5d5856ff"/>
-    <Option name="plugin bypass button: fill end active" value="564d48ff"/>
-    <Option name="plugin bypass button: led" value="660000ff"/>
-    <Option name="plugin bypass button: led active" value="ff0000ff"/>
-    <Option name="plugin bypass button: text" value="c7c7d8ff"/>
-    <Option name="plugin bypass button: text active" value="c8c8d9ff"/>
-    <Option name="punch button: fill start" value="603f3fff"/>
-    <Option name="punch button: fill end" value="3d2828ff"/>
-    <Option name="punch button: fill start active" value="503010ff"/>
-    <Option name="punch button: fill end active" value="f03020ff"/>
-    <Option name="punch button: led" value="00000000"/>
-    <Option name="punch button: led active" value="00000000"/>
-    <Option name="punch button: text" value="a5a5a5ff"/>
-    <Option name="punch button: text active" value="d8d8d8ff"/>
-    <Option name="mouse mode button: fill start" value="6d7ab460"/>
-    <Option name="mouse mode button: fill end" value="54555dff"/>
-    <Option name="mouse mode button: fill start active" value="1d7a05ff"/>
-    <Option name="mouse mode button: fill end active" value="14ae08ff"/>
-    <Option name="mouse mode button: led" value="4f3300ff"/>
-    <Option name="mouse mode button: led active" value="ffa500ff"/>
-    <Option name="mouse mode button: text" value="f2f2f2ff"/>
-    <Option name="mouse mode button: text active" value="000000ff"/>
-    <Option name="nudge button: fill start" value="785754dd"/>
-    <Option name="nudge button: fill end" value="564242dd"/>
-    <Option name="nudge button: fill start active" value="202025ff"/>
-    <Option name="nudge button: fill end active" value="404045ff"/>
-    <Option name="nudge button: led" value="4f3300ff"/>
-    <Option name="nudge button: led active" value="ffa500ff"/>
-    <Option name="nudge button: text" value="c7c7d8ff"/>
-    <Option name="nudge button: text active" value="c8c8d9ff"/>
-    <Option name="zoom menu: fill start" value="99997950"/>
-    <Option name="zoom menu: fill end" value="99996999"/>
-    <Option name="zoom menu: fill start active" value="202025ff"/>
-    <Option name="zoom menu: fill end active" value="404045ff"/>
-    <Option name="zoom menu: led" value="4f3300ff"/>
-    <Option name="zoom menu: led active" value="ffa500ff"/>
-    <Option name="zoom menu: text" value="c7c7d8ff"/>
-    <Option name="zoom menu: text active" value="c8c8d9ff"/>
-    <Option name="zoom button: fill start" value="d4d0a090"/>
-    <Option name="zoom button: fill end" value="a4a07090"/>
-    <Option name="zoom button: fill start active" value="202025ff"/>
-    <Option name="zoom button: fill end active" value="404045ff"/>
-    <Option name="zoom button: led" value="4f3300ff"/>
-    <Option name="zoom button: led active" value="ffa500ff"/>
-    <Option name="zoom button: text" value="c7c7d8ff"/>
-    <Option name="zoom button: text active" value="c8c8d9ff"/>
-    <Option name="route button: fill start" value="565659ff"/>
-    <Option name="route button: fill end" value="484853ff"/>
-    <Option name="route button: fill start active" value="4d4d4dff"/>
-    <Option name="route button: fill end active" value="121212ff"/>
-    <Option name="route button: led" value="4f3300ff"/>
-    <Option name="route button: led active" value="ffa500ff"/>
-    <Option name="route button: text" value="bfbfbfff"/>
-    <Option name="route button: text active" value="191919ff"/>
-    <Option name="mixer strip button: fill start" value="565659ff"/>
-    <Option name="mixer strip button: fill end" value="484853ff"/>
-    <Option name="mixer strip button: fill start active" value="5f4943ff"/>
-    <Option name="mixer strip button: fill end active" value="ffa500ff"/>
-    <Option name="mixer strip button: led" value="4f3300ff"/>
-    <Option name="mixer strip button: led active" value="ffa500ff"/>
-    <Option name="mixer strip button: text" value="c7c7d8ff"/>
-    <Option name="mixer strip button: text active" value="000000ff"/>
-    <Option name="mixer strip name button: fill start" value="565659ff"/>
-    <Option name="mixer strip name button: fill end" value="484853ff"/>
-    <Option name="mixer strip name button: fill start active" value="4d4d4dff"/>
-    <Option name="mixer strip name button: fill end active" value="121212ff"/>
-    <Option name="mixer strip name button: led" value="4f3300ff"/>
-    <Option name="mixer strip name button: led active" value="ffa500ff"/>
-    <Option name="mixer strip name button: text" value="c7c7d8ff"/>
-    <Option name="mixer strip name button: text active" value="c8c8d9ff"/>
-    <Option name="midi input button: fill start" value="656867ff"/>
-    <Option name="midi input button: fill end" value="333333ff"/>
-    <Option name="midi input button: fill start active" value="a1ff43ff"/>
-    <Option name="midi input button: fill end active" value="00a300ff"/>
-    <Option name="midi input button: led" value="00000000"/>
-    <Option name="midi input button: led active" value="00000000"/>
-    <Option name="midi input button: text" value="00000000"/>
-    <Option name="midi input button: text active" value="00000000"/>
-    <Option name="transport clock: background" value="262626ff"/>
-    <Option name="transport clock: text" value="8df823ff"/>
-    <Option name="transport clock: edited text" value="ffa500ff"/>
-    <Option name="transport clock: cursor" value="ffa500ff"/>
-    <Option name="secondary clock: background" value="262626ff"/>
-    <Option name="secondary clock: text" value="8df823ff"/>
-    <Option name="secondary clock: edited text" value="ffa500ff"/>
-    <Option name="secondary clock: cursor" value="ffa500ff"/>
-    <Option name="transport delta clock: background" value="000000ff"/>
-    <Option name="transport delta clock: edited text" value="ff0000ff"/>
-    <Option name="transport delta clock: cursor" value="f11000ff"/>
-    <Option name="transport delta clock: text" value="8ce1f8ff"/>
-    <Option name="secondary delta clock: edited text" value="ff0000ff"/>
-    <Option name="secondary delta clock: cursor" value="f11000ff"/>
-    <Option name="secondary delta clock: background" value="000000ff"/>
-    <Option name="secondary delta clock: text" value="8ce1f8ff"/>
-    <Option name="big clock: background" value="020202ff"/>
-    <Option name="big clock: text" value="f0f0f0ff"/>
-    <Option name="big clock: edited text" value="ffa500ff"/>
-    <Option name="big clock: cursor" value="ffa500ff"/>
-    <Option name="big clock active: background" value="020202ff"/>
-    <Option name="big clock active: text" value="f11000ff"/>
-    <Option name="big clock active: edited text" value="ffa500ff"/>
-    <Option name="big clock active: cursor" value="ffa500ff"/>
-    <Option name="punch clock: background" value="000000ff"/>
-    <Option name="punch clock: text" value="6bb620ff"/>
-    <Option name="punch clock: edited text" value="ff0000ff"/>
-    <Option name="punch clock: cursor" value="f11000ff"/>
-    <Option name="selection clock: background" value="000000ff"/>
-    <Option name="selection clock: text" value="6bb620ff"/>
-    <Option name="selection clock: edited text" value="ff0000ff"/>
-    <Option name="selection clock: cursor" value="f11000ff"/>
-    <Option name="nudge clock: background" value="262626ff"/>
-    <Option name="nudge clock: text" value="6bb620ff"/>
-    <Option name="nudge clock: edited text" value="ffa500ff"/>
-    <Option name="nudge clock: cursor" value="ffa500ff"/>
-    <Option name="clock: background" value="000000ff"/>
-    <Option name="clock: text" value="6bb620ff"/>
-    <Option name="clock: edited text" value="ffa500ff"/>
-    <Option name="clock: cursor" value="ffa500ff"/>
-  </Canvas>
-</Ardour>
diff --git a/gtk2_ardour/ardour3_ui_default.conf.in b/gtk2_ardour/ardour3_ui_default.conf.in
new file mode 100644 (file)
index 0000000..a91aec3
--- /dev/null
@@ -0,0 +1,605 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Ardour>
+  <UI>
+    <Option name="ui-rc-file" value="ardour3_ui_dark.rc"/>
+    <Option name="flat-buttons" value="00000000"/>
+    <Option name="waveform-gradient-depth" value="0"/>
+    <Option name="timeline-item-gradient-depth" value="00000.5"/>
+    <Option name="all-floating-windows-are-dialogs" value="00000000"/>
+    <Option name="color-regions-using-track-color" value="00000000"/>
+    <Option name="show-waveform-clipping" value="00000001"/>
+  </UI>
+  <Canvas>
+    <Option name="active crossfade" value="20b2af2e"/>
+    <Option name="audio bus base" value="73829968"/>
+    <Option name="audio master bus base" value="00000000"/>
+    <Option name="audio track base" value="9daac468"/>
+    <Option name="automation line" value="44bc59ff"/>
+    <Option name="automation track fill" value="a0a0ce68"/>
+    <Option name="automation track outline" value="282828ff"/>
+    <Option name="cd marker bar" value="9496a3cc"/>
+    <Option name="crossfade editor base" value="282d49ff"/>
+    <Option name="crossfade editor line" value="000000ff"/>
+    <Option name="crossfade editor line shading" value="00a0d154"/>
+    <Option name="crossfade editor point fill" value="00ff00ff"/>
+    <Option name="crossfade editor point outline" value="0000ffff"/>
+    <Option name="crossfade editor wave" value="ffffff28"/>
+    <Option name="selected crossfade editor wave fill" value="00000000"/>
+    <Option name="crossfade line" value="000000ff"/>
+    <Option name="edit point" value="0000ffff"/>
+    <Option name="entered automation line" value="dd6363ff"/>
+    <Option name="control point fill" value="ffffff66"/>
+    <Option name="control point outline" value="ff0000ee"/>
+    <Option name="control point selected" value="55ccccff"/>
+    <Option name="entered gain line" value="dd6363ff"/>
+    <Option name="entered marker" value="dd6363ff"/>
+    <Option name="frame handle" value="7c00ff96"/>
+    <Option name="gain line" value="00bc20ff"/>
+    <Option name="gain line inactive" value="9fbca4c5"/>
+    <Option name="ghost track base" value="44007c7f"/>
+    <Option name="ghost track midi outline" value="00000000"/>
+    <Option name="ghost track wave" value="02fd004c"/>
+    <Option name="ghost track wave fill" value="00000000"/>
+    <Option name="ghost track wave clip" value="ff000000"/>
+    <Option name="ghost track zero line" value="e500e566"/>
+    <Option name="image track" value="ddddd8ff"/>
+    <Option name="inactive crossfade" value="e8ed3d77"/>
+    <Option name="inactive fade handle" value="bbbbbbaa"/>
+    <Option name="inactive group tab" value="434343ff"/>
+    <Option name="location cd marker" value="1ee8c4ff"/>
+    <Option name="location loop" value="35964fff"/>
+    <Option name="location marker" value="c4f411ff"/>
+    <Option name="location punch" value="7c3a3aff"/>
+    <Option name="location range" value="497a59ff"/>
+    <Option name="marker bar" value="99a1adcc"/>
+    <Option name="marker bar separator" value="555555ff"/>
+    <Option name="marker drag line" value="004f00f9"/>
+    <Option name="marker label" value="000000ff"/>
+    <Option name="marker track" value="ddddd8ff"/>
+    <Option name="measure line bar" value="ffffff9c"/>
+    <Option name="measure line beat" value="a29e9e76"/>
+    <Option name="meter bar" value="626470cc"/>
+    <Option name="meter fill: 0" value="008800ff"/>
+    <Option name="meter fill: 1" value="008800ff"/>
+    <Option name="meter fill: 2" value="00ff00ff"/>
+    <Option name="meter fill: 3" value="00ff00ff"/>
+    <Option name="meter fill: 4" value="fff000ff"/>
+    <Option name="meter fill: 5" value="fff000ff"/>
+    <Option name="meter fill: 6" value="ff8000ff"/>
+    <Option name="meter fill: 7" value="ff8000ff"/>
+    <Option name="meter fill: 8" value="ff0000ff"/>
+    <Option name="meter fill: 9" value="ff0000ff"/>
+    <Option name="meter background: bottom" value="333333ff"/>
+    <Option name="meter background: top" value="444444ff"/>
+    <Option name="midi meter fill: 0" value="effaa1ff"/>
+    <Option name="midi meter fill: 1" value="f2c97dff"/>
+    <Option name="midi meter fill: 2" value="f2c97dff"/>
+    <Option name="midi meter fill: 3" value="f48f52ff"/>
+    <Option name="midi meter fill: 4" value="f48f52ff"/>
+    <Option name="midi meter fill: 5" value="f83913ff"/>
+    <Option name="midi meter fill: 6" value="f83913ff"/>
+    <Option name="midi meter fill: 7" value="8fc78eff"/>
+    <Option name="midi meter fill: 8" value="8fc78eff"/>
+    <Option name="midi meter fill: 9" value="00f45600"/>
+    <Option name="meterbridge peakindicator: fill start" value="444444ff"/>
+    <Option name="meterbridge peakindicator: fill end" value="333333ff"/>
+    <Option name="meterbridge peakindicator on: fill start" value="ff0000ff"/>
+    <Option name="meterbridge peakindicator on: fill end" value="880000ff"/>
+    <Option name="meterbridge label: fill start" value="444444ff"/>
+    <Option name="meterbridge label: fill end" value="333333ff"/>
+    <Option name="meterbridge label: text" value="c7c7d8ff"/>
+    <Option name="meter marker" value="f2425bff"/>
+    <Option name="midi bus base" value="00000000"/>
+    <Option name="midi frame base" value="393d3766"/>
+    <Option name="midi note inactive channel" value="00000000"/>
+    <Option name="midi note color min" value="3f542aff"/>
+    <Option name="midi note color mid" value="7ea854ff"/>
+    <Option name="midi note color max" value="bfff80ff"/>
+    <Option name="selected midi note color min" value="1e1e33ff"/>
+    <Option name="selected midi note color mid" value="51518aff"/>
+    <Option name="selected midi note color max" value="8383deff"/>
+    <Option name="midi note selected" value="b2b2ffff"/>
+    <Option name="midi note velocity text" value="f4f214bc"/>
+    <Option name="midi patch change fill" value="50555aa0"/>
+    <Option name="midi patch change outline" value="c0c5caff"/>
+    <Option name="midi patch change inactive channel fill" value="50555ac0"/>
+    <Option name="midi patch change inactive channel outline" value="20252ac0"/>
+    <Option name="midi sysex fill" value="f1e139a0"/>
+    <Option name="midi sysex outline" value="a7a7d4ff"/>
+    <Option name="midi select rect fill" value="8888ff88"/>
+    <Option name="midi select rect outline" value="5555ffff"/>
+    <Option name="midi track base" value="b3cca35f"/>
+    <Option name="name highlight fill" value="0000ffff"/>
+    <Option name="name highlight outline" value="7c00ff96"/>
+    <Option name="piano roll black outline" value="f4f4f476"/>
+    <Option name="piano roll black" value="6c6e6a6b"/>
+    <Option name="piano roll white" value="979b9565"/>
+    <Option name="play head" value="ff0000ff"/>
+    <Option name="processor automation line" value="7aa3f9ff"/>
+    <Option name="punch line" value="a80000ff"/>
+    <Option name="range drag bar rect" value="969696c6"/>
+    <Option name="range drag rect" value="82c696c6"/>
+    <Option name="range marker bar" value="7d7f8ccc"/>
+    <Option name="recording rect" value="cc2828ff"/>
+    <Option name="recorded waveform fill" value="ffffffff"/>
+    <Option name="recorded waveform outline" value="0f0f1fff"/>
+    <Option name="rubber band rect" value="c6c6c659"/>
+    <Option name="ruler base" value="2c2121ff"/>
+    <Option name="ruler text" value="e5e5e5ff"/>
+    <Option name="selected crossfade editor line" value="00dbdbff"/>
+    <Option name="selected crossfade editor wave" value="f9ea14a0"/>
+    <Option name="selected region base" value="596559ff"/>
+    <Option name="selected waveform fill" value="ffa500ff"/>
+    <option name="selected waveform outline" value="0f0f0fcc"/>
+    <Option name="selection rect" value="e8f4d377"/>
+    <Option name="selection" value="636363b2"/>
+    <Option name="shuttle" value="6bb620ff"/>
+    <Option name="silence" value="9efffd7a"/>
+    <Option name="silence text" value="0e066cff"/>
+    <Option name="mono panner outline" value="33445eff"/>
+    <Option name="mono panner fill" value="7a9bccc9"/>
+    <Option name="mono panner text" value="000000ff"/>
+    <Option name="mono panner bg" value="2e2929ff"/>
+    <Option name="mono panner position fill" value="7a89b3ff"/>
+    <Option name="mono panner position outline" value="33445eff"/>
+    <Option name="stereo panner outline" value="33445eff"/>
+    <Option name="stereo panner fill" value="7a9accc9"/>
+    <Option name="stereo panner text" value="000000ff"/>
+    <Option name="stereo panner bg" value="2e2929ff"/>
+    <Option name="stereo panner rule" value="455c7fff"/>
+    <Option name="stereo panner mono outline" value="a05600ff"/>
+    <Option name="stereo panner mono fill" value="e99668ca"/>
+    <Option name="stereo panner mono text" value="000000ff"/>
+    <Option name="stereo panner mono bg" value="2e2929ff"/>
+    <Option name="stereo panner inverted outline" value="bf0a00ff"/>
+    <Option name="stereo panner inverted fill" value="e4a19cc9"/>
+    <Option name="stereo panner inverted text" value="000000ff"/>
+    <Option name="stereo panner inverted bg" value="2e2929ff"/>
+    <Option name="tempo bar" value="70727fcc"/>
+    <Option name="tempo marker" value="f2425bff"/>
+    <Option name="time axis frame" value="000000ff"/>
+    <Option name="selected time axis frame" value="000000ff"/>
+    <Option name="time stretch fill" value="e2b5b596"/>
+    <Option name="time stretch outline" value="63636396"/>
+    <Option name="tracknumber label: fill start" value="444444ff"/>
+    <Option name="tracknumber label: fill end" value="333333ff"/>
+    <Option name="tracknumber label: text" value="c7c7d8ff"/>
+    <Option name="transport drag rect" value="969696c6"/>
+    <Option name="transport loop rect" value="1e7728f9"/>
+    <Option name="transport marker bar" value="8c8e98cc"/>
+    <Option name="transport punch rect" value="6d2828e5"/>
+    <Option name="trim handle locked" value="ea0f0f28"/>
+    <Option name="trim handle" value="1900ff44"/>
+    <Option name="verbose canvas cursor" value="fffd2ebc"/>
+    <Option name="vestigial frame" value="0000000f"/>
+    <Option name="video timeline bar" value="303030ff"/>
+    <Option name="region base" value="b1c9b1ff"/>
+    <Option name="region area covered by another region" value="505050b0"/>
+    <Option name="waveform outline" value="000000ff"/>
+    <Option name="clipped waveform" value="ff0000e5"/>
+    <Option name="waveform fill" value="ffffffff"/>
+    <Option name="zero line" value="7f7f7fe0"/>
+    <Option name="zoom rect" value="c6d1b26d"/>
+    <Option name="monitor knob" value="329edfff"/>
+    <Option name="button border" value="000000f0"/>
+    <Option name="border color" value="00000000"/>
+    <Option name="processor prefader: fill start" value="873c3cff"/>
+    <Option name="processor prefader: fill end" value="542525ff"/>
+    <Option name="processor prefader: fill start active" value="774c4cff"/>
+    <Option name="processor prefader: fill end active" value="603535ff"/>
+    <Option name="processor prefader: led" value="26550eff"/>
+    <Option name="processor prefader: led active" value="78cb4eff"/>
+    <Option name="processor prefader: text" value="aaaaa3ff"/>
+    <Option name="processor prefader: text active" value="eeeeecff"/>
+    <Option name="processor fader: fill start" value="5d90b0ff"/>
+    <Option name="processor fader: fill end" value="154c6eff"/>
+    <Option name="processor fader: fill start active" value="5d90b0ff"/>
+    <Option name="processor fader: fill end active" value="256d8fff"/>
+    <Option name="processor fader: led" value="26550eff"/>
+    <Option name="processor fader: led active" value="78cb4eff"/>
+    <Option name="processor fader: text" value="aaaaa3ff"/>
+    <Option name="processor fader: text active" value="eeeeecff"/>
+    <Option name="processor postfader: fill start" value="354537ff"/>
+    <Option name="processor postfader: fill end" value="202823ff"/>
+    <Option name="processor postfader: fill start active" value="466452ff"/>
+    <Option name="processor postfader: fill end active" value="254528ff"/>
+    <Option name="processor postfader: led" value="26550eff"/>
+    <Option name="processor postfader: led active" value="78cb4eff"/>
+    <Option name="processor postfader: text" value="aaaaa3ff"/>
+    <Option name="processor postfader: text active" value="eeeeecff"/>
+    <Option name="processor control button: fill start" value="222222ff"/>
+    <Option name="processor control button: fill end" value="333333ff"/>
+    <Option name="processor control button: fill start active" value="444444ff"/>
+    <Option name="processor control button: fill end active" value="333333ff"/>
+    <Option name="processor control button: led" value="224400ff"/>
+    <Option name="processor control button: led active" value="99cc00ff"/>
+    <Option name="processor control button: text" value="ffffffff"/>
+    <Option name="processor control button: text active" value="ffffffff"/>
+    <Option name="midi device: fill start" value="54555dff"/>
+    <Option name="midi device: fill end" value="54555dff"/>
+    <Option name="midi device: fill start active" value="3a3a40ff"/>
+    <Option name="midi device: fill end active" value="45464cff"/>
+    <Option name="midi device: led" value="006600ff"/>
+    <Option name="midi device: led active" value="00ff00ff"/>
+    <Option name="midi device: text" value="c7c7d8ff"/>
+    <Option name="midi device: text active" value="eeeeecff"/>
+    <Option name="monitor button: fill start" value="5f5a58ff"/>
+    <Option name="monitor button: fill end" value="4f4a48ff"/>
+    <Option name="monitor button: fill start active" value="553500ff"/>
+    <Option name="monitor button: fill end active" value="e58505ff"/>
+    <Option name="monitor button: led" value="660000ff"/>
+    <Option name="monitor button: led active" value="ff0000ff"/>
+    <Option name="monitor button: text" value="aaaaa3ff"/>
+    <Option name="monitor button: text active" value="1a1a1aff"/>
+    <Option name="meterbridge label: fill start" value="444444ff"/>
+    <Option name="meterbridge label: fill end" value="333333ff"/>
+    <Option name="meterbridge label: text" value="c7c7d8ff"/>
+    <Option name="solo isolate: fill start" value="5f5a58ff"/>
+    <Option name="solo isolate: fill end" value="504442ff"/>
+    <Option name="solo isolate: fill start active" value="5d5856ff"/>
+    <Option name="solo isolate: fill end active" value="564d48ff"/>
+    <Option name="solo isolate: led" value="660000ff"/>
+    <Option name="solo isolate: led active" value="ff0000ff"/>
+    <Option name="solo isolate: text" value="c7c7d8ff"/>
+    <Option name="solo isolate: text active" value="c8c8d9ff"/>
+    <Option name="solo safe: fill start" value="5f5a58ff"/>
+    <Option name="solo safe: fill end" value="504442ff"/>
+    <Option name="solo safe: fill start active" value="5d5856ff"/>
+    <Option name="solo safe: fill end active" value="564d48ff"/>
+    <Option name="solo safe: led" value="660000ff"/>
+    <Option name="solo safe: led active" value="ff0000ff"/>
+    <Option name="solo safe: text" value="c7c7d8ff"/>
+    <Option name="solo safe: text active" value="c8c8d9ff"/>
+    <Option name="meterbridge peaklabel" value="ff1111ff"/>
+    <Option name="meter color BBC" value="ffa500ff"/>
+    <Option name="meterbridge peakindicator: fill start" value="444444ff"/>
+    <Option name="meterbridge peakindicator: fill end" value="333333ff"/>
+    <Option name="meterbridge peakindicator on: fill start" value="ff0000ff"/>
+    <Option name="meterbridge peakindicator on: fill end" value="880000ff"/>
+    <Option name="monitor section cut: fill start" value="5f5a58ff"/>
+    <Option name="monitor section cut: fill end" value="4f4a48ff"/>
+    <Option name="monitor section cut: fill start active" value="5f4943ff"/>
+    <Option name="monitor section cut: fill end active" value="ffa500ff"/>
+    <Option name="monitor section cut: led" value="473812ff"/>
+    <Option name="monitor section cut: led active" value="78cb4eff"/>
+    <Option name="monitor section cut: text" value="c7c7d8ff"/>
+    <Option name="monitor section cut: text active" value="000000ff"/>
+    <Option name="monitor section dim: fill start" value="5f5a58ff"/>
+    <Option name="monitor section dim: fill end" value="4f4a48ff"/>
+    <Option name="monitor section dim: fill start active" value="553500ff"/>
+    <Option name="monitor section dim: fill end active" value="e58505ff"/>
+    <Option name="monitor section dim: led" value="00000000"/>
+    <Option name="monitor section dim: led active" value="78cb4eff"/>
+    <Option name="monitor section dim: text" value="c8c8d9ff"/>
+    <Option name="monitor section dim: text active" value="c8c8d9ff"/>
+    <Option name="monitor section solo: fill start" value="5f5a58ff"/>
+    <Option name="monitor section solo: fill end" value="4f4a48ff"/>
+    <Option name="monitor section solo: fill start active" value="104506ff"/>
+    <Option name="monitor section solo: fill end active" value="4dbb00ff"/>
+    <Option name="monitor section solo: led" value="473812ff"/>
+    <Option name="monitor section solo: led active" value="ffa500ff"/>
+    <Option name="monitor section solo: text" value="00000000"/>
+    <Option name="monitor section solo: text active" value="00000000"/>
+    <Option name="monitor section invert: fill start" value="5f5a58ff"/>
+    <Option name="monitor section invert: fill end" value="4f4a48ff"/>
+    <Option name="monitor section invert: fill start active" value="222260ff"/>
+    <Option name="monitor section invert: fill end active" value="4242d0ff"/>
+    <Option name="monitor section invert: led" value="473812ff"/>
+    <Option name="monitor section invert: led active" value="78cb4eff"/>
+    <Option name="monitor section invert: text" value="00000000"/>
+    <Option name="monitor section invert: text active" value="00000000"/>
+    <Option name="monitor section mono: fill start" value="5f5a58ff"/>
+    <Option name="monitor section mono: fill end" value="4f4a48ff"/>
+    <Option name="monitor section mono: fill start active" value="222260ff"/>
+    <Option name="monitor section mono: fill end active" value="3232c0ff"/>
+    <Option name="monitor section mono: led" value="473812ff"/>
+    <Option name="monitor section mono: led active" value="78cb4eff"/>
+    <Option name="monitor section mono: text" value="c7c7d8ff"/>
+    <Option name="monitor section mono: text active" value="c8c8d9ff"/>
+    <Option name="monitor section solo model: fill start" value="5d5856ff"/>
+    <Option name="monitor section solo model: fill end" value="564d48ff"/>
+    <Option name="monitor section solo model: fill start active" value="5d5856ff"/>
+    <Option name="monitor section solo model: fill end active" value="564d48ff"/>
+    <Option name="monitor section solo model: led" value="4f3300ff"/>
+    <Option name="monitor section solo model: led active" value="ffa500ff"/>
+    <Option name="monitor section solo model: text" value="c7c7d8ff"/>
+    <Option name="monitor section solo model: text active" value="c8c8d9ff"/>
+    <Option name="monitor solo override: fill start" value="5d5856ff"/>
+    <Option name="monitor solo override: fill end" value="564d48ff"/>
+    <Option name="monitor solo override: fill start active" value="5d5856ff"/>
+    <Option name="monitor solo override: fill end active" value="564d48ff"/>
+    <Option name="monitor solo override: led" value="4f3300ff"/>
+    <Option name="monitor solo override: led active" value="ffa500ff"/>
+    <Option name="monitor solo override: text" value="c7c7d8ff"/>
+    <Option name="monitor solo override: text active" value="c8c8d9ff"/>
+    <Option name="monitor solo exclusive: fill start" value="5d5856ff"/>
+    <Option name="monitor solo exclusive: fill end" value="564d48ff"/>
+    <Option name="monitor solo exclusive: fill start active" value="5d5856ff"/>
+    <Option name="monitor solo exclusive: fill end active" value="564c47ff"/>
+    <Option name="monitor solo exclusive: led" value="4f3300ff"/>
+    <Option name="monitor solo exclusive: led active" value="ffa500ff"/>
+    <Option name="monitor solo exclusive: text" value="c7c7d8ff"/>
+    <Option name="monitor solo exclusive: text active" value="c8c8d9ff"/>
+    <Option name="rude solo: fill start" value="684d4dff"/>
+    <Option name="rude solo: fill end" value="513c3cff"/>
+    <Option name="rude solo: fill start active" value="ff1f1fff"/>
+    <Option name="rude solo: fill end active" value="e21b1bff"/>
+    <Option name="rude solo: led" value="00000000"/>
+    <Option name="rude solo: led active" value="00000000"/>
+    <Option name="rude solo: text" value="969696ff"/>
+    <Option name="rude solo: text active" value="e5e5e5ff"/>
+    <Option name="rude isolate: fill start" value="21414fff"/>
+    <Option name="rude isolate: fill end" value="192930ff"/>
+    <Option name="rude isolate: fill start active" value="e5f7ffff"/>
+    <Option name="rude isolate: fill end active" value="b6e5fdff"/>
+    <Option name="rude isolate: led" value="00000000"/>
+    <Option name="rude isolate: led active" value="000000ff"/>
+    <Option name="rude isolate: text" value="979797ff"/>
+    <Option name="rude isolate: text active" value="000000ff"/>
+    <Option name="rude audition: fill start" value="684d4dff"/>
+    <Option name="rude audition: fill end" value="513c3cff"/>
+    <Option name="rude audition: fill start active" value="ff1f1fff"/>
+    <Option name="rude audition: fill end active" value="e21b1bff"/>
+    <Option name="rude audition: led" value="00000000"/>
+    <Option name="rude audition: led active" value="00000000"/>
+    <Option name="rude audition: text" value="979797ff"/>
+    <Option name="rude audition: text active" value="ffffffff"/>
+    <Option name="feedback alert: fill start" value="684d4dff"/>
+    <Option name="feedback alert: fill end" value="513c3cff"/>
+    <Option name="feedback alert: fill start active" value="ff1f1fff"/>
+    <Option name="feedback alert: fill end active" value="e21b1bff"/>
+    <Option name="feedback alert: led" value="00000000"/>
+    <Option name="feedback alert: led active" value="00000000"/>
+    <Option name="feedback alert: text" value="969696ff"/>
+    <Option name="feedback alert: text active" value="e5e5e5ff"/>
+    <Option name="mute button: fill start" value="565659ff"/>
+    <Option name="mute button: fill end" value="484853ff"/>
+    <Option name="mute button: fill start active" value="5f4943ff"/>
+    <Option name="mute button: fill end active" value="ffff00ff"/>
+    <Option name="mute button: led" value="00000000"/>
+    <Option name="mute button: led active" value="00000000"/>
+    <Option name="mute button: text" value="bfbfafff"/>
+    <Option name="mute button: text active" value="191919ff"/>
+    <Option name="solo button: fill start" value="565659ff"/>
+    <Option name="solo button: fill end" value="484853ff"/>
+    <Option name="solo button: fill start active" value="1d7a05ff"/>
+    <Option name="solo button: fill end active" value="4dbb00ff"/>
+    <Option name="solo button: led" value="00000000"/>
+    <Option name="solo button: led active" value="00000000"/>
+    <Option name="solo button: text" value="afbfafff"/>
+    <Option name="solo button: text active" value="191919ff"/>
+    <Option name="invert button: fill start" value="565659ff"/>
+    <Option name="invert button: fill end" value="484853ff"/>
+    <Option name="invert button: fill start active" value="222260ff"/>
+    <Option name="invert button: fill end active" value="4242d0ff"/>
+    <Option name="invert button: led" value="473812ff"/>
+    <Option name="invert button: led active" value="78cb4eff"/>
+    <Option name="invert button: text" value="bfbfbfff"/>
+    <Option name="invert button: text active" value="bfbfbfff"/>
+    <Option name="record enable button: fill start" value="3e312fff"/>
+    <Option name="record enable button: fill end" value="3f312fff"/>
+    <Option name="record enable button: fill start active" value="c10b0bff"/>
+    <Option name="record enable button: fill end active" value="fd0000ff"/>
+    <Option name="record enable button: led" value="7b3541ff"/>
+    <Option name="record enable button: led active" value="ffa3b3ff"/>
+    <Option name="record enable button: text" value="a5a5a5ff"/>
+    <Option name="record enable button: text active" value="000000ff"/>
+    <Option name="generic button: fill start" value="3e312fff"/>
+    <Option name="generic button: fill end" value="3f312fff"/>
+    <Option name="generic button: fill start active" value="c10b0bff"/>
+    <Option name="generic button: fill end active" value="fd0000ff"/>
+    <Option name="generic button: led" value="7b3541ff"/>
+    <Option name="generic button: led active" value="ffa3b3ff"/>
+    <Option name="generic button: text" value="ff0000ff"/>
+    <Option name="generic button: text active" value="000000ff"/>
+    <Option name="send alert button: fill start" value="4e5647ff"/>
+    <Option name="send alert button: fill end" value="43493cff"/>
+    <Option name="send alert button: fill start active" value="91f928ff"/>
+    <Option name="send alert button: fill end active" value="85e524ff"/>
+    <Option name="send alert button: led" value="00000000"/>
+    <Option name="send alert button: led active" value="00000000"/>
+    <Option name="send alert button: text" value="ccccccff"/>
+    <Option name="send alert button: text active" value="000000ff"/>
+    <Option name="transport button: fill start" value="616268ff"/>
+    <Option name="transport button: fill end" value="505159ff"/>
+    <Option name="transport button: fill start active" value="1d7a05ff"/>
+    <Option name="transport button: fill end active" value="00a300ff"/>
+    <Option name="transport button: led" value="00000000"/>
+    <Option name="transport button: led active" value="00000000"/>
+    <Option name="transport button: text" value="00000000"/>
+    <Option name="transport button: text active" value="00000000"/>
+    <Option name="transport recenable button: fill start" value="5f3f3fff"/>
+    <Option name="transport recenable button: fill end" value="3d2828ff"/>
+    <Option name="transport recenable button: fill start active" value="6a0404ff"/>
+    <Option name="transport recenable button: fill end active" value="b50e0eff"/>
+    <Option name="transport recenable button: led" value="00000000"/>
+    <Option name="transport recenable button: led active" value="00000000"/>
+    <Option name="transport recenable button: text" value="00000000"/>
+    <Option name="transport recenable button: text active" value="00000000"/>
+    <Option name="transport option button: fill start" value="636470ff"/>
+    <Option name="transport option button: fill end" value="54555dff"/>
+    <Option name="transport option button: fill start active" value="636470ff"/>
+    <Option name="transport option button: fill end active" value="4a4b51ff"/>
+    <Option name="transport option button: led" value="4f3300ff"/>
+    <Option name="transport option button: led active" value="ffa500ff"/>
+    <Option name="transport option button: text" value="c7c7d8ff"/>
+    <Option name="transport option button: text active" value="c8c8d9ff"/>
+    <Option name="transport active option button: fill start" value="606b60ff"/>
+    <Option name="transport active option button: fill end" value="495348ff"/>
+    <Option name="transport active option button: fill start active" value="154515ff"/>
+    <Option name="transport active option button: fill end active" value="20a320ff"/>
+    <Option name="transport active option button: led" value="4f3300ff"/>
+    <Option name="transport active option button: led active" value="ffa500ff"/>
+    <Option name="transport active option button: text" value="c7c7d8ff"/>
+    <Option name="transport active option button: text active" value="000000ff"/>
+    <Option name="plugin bypass button: fill start" value="5d5856ff"/>
+    <Option name="plugin bypass button: fill end" value="564d48ff"/>
+    <Option name="plugin bypass button: fill start active" value="5d5856ff"/>
+    <Option name="plugin bypass button: fill end active" value="564d48ff"/>
+    <Option name="plugin bypass button: led" value="660000ff"/>
+    <Option name="plugin bypass button: led active" value="ff0000ff"/>
+    <Option name="plugin bypass button: text" value="c7c7d8ff"/>
+    <Option name="plugin bypass button: text active" value="c8c8d9ff"/>
+    <Option name="punch button: fill start" value="603f3fff"/>
+    <Option name="punch button: fill end" value="3d2828ff"/>
+    <Option name="punch button: fill start active" value="503010ff"/>
+    <Option name="punch button: fill end active" value="f03020ff"/>
+    <Option name="punch button: led" value="00000000"/>
+    <Option name="punch button: led active" value="00000000"/>
+    <Option name="punch button: text" value="a5a5a5ff"/>
+    <Option name="punch button: text active" value="d8d8d8ff"/>
+    <Option name="mouse mode button: fill start" value="6d7ab460"/>
+    <Option name="mouse mode button: fill end" value="54555dff"/>
+    <Option name="mouse mode button: fill start active" value="1d7a05ff"/>
+    <Option name="mouse mode button: fill end active" value="14ae08ff"/>
+    <Option name="mouse mode button: led" value="4f3300ff"/>
+    <Option name="mouse mode button: led active" value="ffa500ff"/>
+    <Option name="mouse mode button: text" value="f2f2f2ff"/>
+    <Option name="mouse mode button: text active" value="000000ff"/>
+    <Option name="nudge button: fill start" value="785754dd"/>
+    <Option name="nudge button: fill end" value="564242dd"/>
+    <Option name="nudge button: fill start active" value="202025ff"/>
+    <Option name="nudge button: fill end active" value="404045ff"/>
+    <Option name="nudge button: led" value="4f3300ff"/>
+    <Option name="nudge button: led active" value="ffa500ff"/>
+    <Option name="nudge button: text" value="c7c7d8ff"/>
+    <Option name="nudge button: text active" value="c8c8d9ff"/>
+    <Option name="zoom menu: fill start" value="99997950"/>
+    <Option name="zoom menu: fill end" value="99996999"/>
+    <Option name="zoom menu: fill start active" value="202025ff"/>
+    <Option name="zoom menu: fill end active" value="404045ff"/>
+    <Option name="zoom menu: led" value="4f3300ff"/>
+    <Option name="zoom menu: led active" value="ffa500ff"/>
+    <Option name="zoom menu: text" value="c7c7d8ff"/>
+    <Option name="zoom menu: text active" value="c8c8d9ff"/>
+    <Option name="zoom button: fill start" value="d4d0a090"/>
+    <Option name="zoom button: fill end" value="a4a07090"/>
+    <Option name="zoom button: fill start active" value="202025ff"/>
+    <Option name="zoom button: fill end active" value="404045ff"/>
+    <Option name="zoom button: led" value="4f3300ff"/>
+    <Option name="zoom button: led active" value="ffa500ff"/>
+    <Option name="zoom button: text" value="c7c7d8ff"/>
+    <Option name="zoom button: text active" value="c8c8d9ff"/>
+    <Option name="route button: fill start" value="565659ff"/>
+    <Option name="route button: fill end" value="484853ff"/>
+    <Option name="route button: fill start active" value="4d4d4dff"/>
+    <Option name="route button: fill end active" value="121212ff"/>
+    <Option name="route button: led" value="4f3300ff"/>
+    <Option name="route button: led active" value="ffa500ff"/>
+    <Option name="route button: text" value="bfbfbfff"/>
+    <Option name="route button: text active" value="191919ff"/>
+    <Option name="mixer strip button: fill start" value="565659ff"/>
+    <Option name="mixer strip button: fill end" value="484853ff"/>
+    <Option name="mixer strip button: fill start active" value="5f4943ff"/>
+    <Option name="mixer strip button: fill end active" value="ffa500ff"/>
+    <Option name="mixer strip button: led" value="4f3300ff"/>
+    <Option name="mixer strip button: led active" value="ffa500ff"/>
+    <Option name="mixer strip button: text" value="c7c7d8ff"/>
+    <Option name="mixer strip button: text active" value="000000ff"/>
+    <Option name="mixer strip name button: fill start" value="565659ff"/>
+    <Option name="mixer strip name button: fill end" value="484853ff"/>
+    <Option name="mixer strip name button: fill start active" value="4d4d4dff"/>
+    <Option name="mixer strip name button: fill end active" value="121212ff"/>
+    <Option name="mixer strip name button: led" value="4f3300ff"/>
+    <Option name="mixer strip name button: led active" value="ffa500ff"/>
+    <Option name="mixer strip name button: text" value="c7c7d8ff"/>
+    <Option name="mixer strip name button: text active" value="c8c8d9ff"/>
+    <Option name="midi input button: fill start" value="656867ff"/>
+    <Option name="midi input button: fill end" value="333333ff"/>
+    <Option name="midi input button: fill start active" value="a1ff43ff"/>
+    <Option name="midi input button: fill end active" value="00a300ff"/>
+    <Option name="midi input button: led" value="00000000"/>
+    <Option name="midi input button: led active" value="00000000"/>
+    <Option name="midi input button: text" value="00000000"/>
+    <Option name="midi input button: text active" value="00000000"/>
+    <Option name="transport clock: background" value="262626ff"/>
+    <Option name="transport clock: text" value="8df823ff"/>
+    <Option name="transport clock: edited text" value="ffa500ff"/>
+    <Option name="transport clock: cursor" value="ffa500ff"/>
+    <Option name="secondary clock: background" value="262626ff"/>
+    <Option name="secondary clock: text" value="8df823ff"/>
+    <Option name="secondary clock: edited text" value="ffa500ff"/>
+    <Option name="secondary clock: cursor" value="ffa500ff"/>
+    <Option name="transport delta clock: background" value="000000ff"/>
+    <Option name="transport delta clock: edited text" value="ff0000ff"/>
+    <Option name="transport delta clock: cursor" value="f11000ff"/>
+    <Option name="transport delta clock: text" value="8ce1f8ff"/>
+    <Option name="secondary delta clock: edited text" value="ff0000ff"/>
+    <Option name="secondary delta clock: cursor" value="f11000ff"/>
+    <Option name="secondary delta clock: background" value="000000ff"/>
+    <Option name="secondary delta clock: text" value="8ce1f8ff"/>
+    <Option name="big clock: background" value="020202ff"/>
+    <Option name="big clock: text" value="f0f0f0ff"/>
+    <Option name="big clock: edited text" value="ffa500ff"/>
+    <Option name="big clock: cursor" value="ffa500ff"/>
+    <Option name="big clock active: background" value="020202ff"/>
+    <Option name="big clock active: text" value="f11000ff"/>
+    <Option name="big clock active: edited text" value="ffa500ff"/>
+    <Option name="big clock active: cursor" value="ffa500ff"/>
+    <Option name="punch clock: background" value="000000ff"/>
+    <Option name="punch clock: text" value="6bb620ff"/>
+    <Option name="punch clock: edited text" value="ff0000ff"/>
+    <Option name="punch clock: cursor" value="f11000ff"/>
+    <Option name="selection clock: background" value="000000ff"/>
+    <Option name="selection clock: text" value="6bb620ff"/>
+    <Option name="selection clock: edited text" value="ff0000ff"/>
+    <Option name="selection clock: cursor" value="f11000ff"/>
+    <Option name="nudge clock: background" value="262626ff"/>
+    <Option name="nudge clock: text" value="6bb620ff"/>
+    <Option name="nudge clock: edited text" value="ffa500ff"/>
+    <Option name="nudge clock: cursor" value="ffa500ff"/>
+    <Option name="clock: background" value="000000ff"/>
+    <Option name="clock: text" value="6bb620ff"/>
+    <Option name="clock: edited text" value="ffa500ff"/>
+    <Option name="clock: cursor" value="ffa500ff"/>
+    <Option name="lock button: fill start" value="ff2714dd"/>
+    <Option name="lock button: fill end" value="8b0000ff"/>
+    <Option name="lock button: fill start active" value="202025ff"/>
+    <Option name="lock button: fill end active" value="404045ff"/>
+    <Option name="lock button: led" value="00000000"/>
+    <Option name="lock button: led active" value="00000000"/>
+    <Option name="lock button: text" value="000024ff"/>
+    <Option name="lock button: text active" value="c8c8d9ff"/>
+
+    <Option name="small font" value="@FONT_SMALL@"/>
+    <Option name="smaller font" value="@FONT_SMALLER@"/>
+    <Option name="normal font" value="@FONT_NORMAL@"/>
+    <Option name="big font" value="@FONT_BIG@"/>
+    <Option name="large font" value="@FONT_LARGE@"/>
+    <Option name="larger font" value="@FONT_LARGER@"/>
+    <Option name="huger font" value="@FONT_HUGER@"/>
+    <Option name="massive font" value="@FONT_MASSIVE@"/>
+    <Option name="small bold font" value="bold @FONT_SMALL@"/>
+    <Option name="smaller bold font" value="bold @FONT_SMALLER@"/>
+    <Option name="normal bold font" value="bold @FONT_NORMAL@"/>
+    <Option name="big bold font" value="bold @FONT_BIG@"/>
+    <Option name="large bold font" value="bold @FONT_LARGE@"/>
+    <Option name="larger bold font" value="bold @FONT_LARGER@"/>
+    <Option name="huger bold font" value="bold @FONT_HUGER@"/>
+    <Option name="massive bold font" value="bold @FONT_MASSIVE@"/>
+    <Option name="small italic font" value="italic @FONT_SMALL@"/>
+    <Option name="smaller italic font" value="italic @FONT_SMALLER@"/>
+    <Option name="normal italic font" value="italic @FONT_NORMAL@"/>
+    <Option name="big italic font" value="italic @FONT_BIG@"/>
+    <Option name="large italic font" value="italic @FONT_LARGE@"/>
+    <Option name="larger italic font" value="italic @FONT_LARGER@"/>
+    <Option name="huger italic font" value="italic @FONT_HUGER@"/>
+    <Option name="massive italic font" value="italic @FONT_MASSIVE@"/>
+    <Option name="small monospace font" value="@MONOSPACE@ @FONT_SMALL@"/>
+    <Option name="smaller monospace font" value="@MONOSPACE@ @FONT_SMALLER@"/>
+    <Option name="normal monospace font" value="@MONOSPACE@ @FONT_NORMAL@"/>
+    <Option name="big monospace font" value="@MONOSPACE@ @FONT_BIG@"/>
+    <Option name="large monospace font" value="@MONOSPACE@ @FONT_LARGE@"/>
+    <Option name="larger monospace font" value="@MONOSPACE@ @FONT_LARGER@"/>
+    <Option name="huger monospace font" value="@MONOSPACE@ @FONT_HUGER@"/>
+    <Option name="massive monospace font" value="@MONOSPACE@ @FONT_MASSIVE@"/>
+    <Option name="small bold monospace font" value="bold @MONOSPACE@ @FONT_SMALL@"/>
+    <Option name="smaller bold monospace font" value="bold @MONOSPACE@ @FONT_SMALLER@"/>
+    <Option name="normal bold monospace font" value="bold @MONOSPACE@ @FONT_NORMAL@"/>
+    <Option name="big bold monospace font" value="bold @MONOSPACE@ @FONT_BIG@"/>
+    <Option name="large bold monospace font" value="bold @MONOSPACE@ @FONT_LARGE@"/>
+    <Option name="larger bold monospace font" value="bold @MONOSPACE@ @FONT_LARGER@"/>
+    <Option name="huger bold monospace font" value="bold @MONOSPACE@ @FONT_HUGER@"/>
+    <Option name="massive bold monospace font" value="bold @MONOSPACE@ @FONT_MASSIVE@"/>
+  </Canvas>
+</Ardour>
index 582871d87f95db20167e9a3e1556c781566c7d49..6f8fda6906a98173c6800ede60f73319cfa24a16 100644 (file)
@@ -150,12 +150,6 @@ widget "*HistorySelector" style:highest "medium_entry"
 widget "*LocationSelector" style:highest "medium_entry"
 widget "*TakeSelector" style:highest "medium_entry"
 widget "*RegionSelector" style:highest "medium_entry"
-widget "*TimecodeRuler" style:highest "editor_time_ruler"
-widget "*BBTRuler" style:highest "editor_time_ruler"
-widget "*SamplesRuler" style:highest "editor_time_ruler"
-widget "*TimecodeRuler" style:highest "editor_time_ruler"
-widget "*FramesRuler" style:highest "editor_time_ruler"
-widget "*MinSecRuler" style:highest "editor_time_ruler"
 widget "*BaseFrame" style:highest "base_frame"
 
 widget "*SendStripBase" style:highest "send_strip_base"
@@ -361,6 +355,7 @@ widget "*EditorHScrollbar" style:highest "editor_hscrollbar"
 widget "*MidiListView*" style:highest "treeview_display"
 widget "*ProcessorList*" style:highest "processor_list"
 widget "*PortMatrixLabel*" style:highest "small_text"
+widget "*midi device" style:highest "midi_device"
 widget "*MidiTracerTextView" style:highest "midi_tracer_textview"
 widget "*solo isolate" style:highest "solo_isolate"
 widget "*meterbridge label" style:highest "meterbridge_label"
@@ -373,6 +368,7 @@ widget "*TimeInfoSelectionLabel" style:highest "very_small_text"
 widget "*TimeInfoPunchTitle" style:highest "very_small_text"
 widget "*TimeInfoPunchButton" style:highest "very_small_text"
 widget "*TimeInfoBox" style:highest "time_info_box"
+widget "*tracknumber label" style:highest "tracknumber_label"
 widget "*StatusBarBox" style:highest "status_bar_box"
 widget "*RouteNameEditorEntry" style:highest "text_cell_entry"
 widget "*RegionNameEditorEntry" style:highest "text_cell_entry"
index 47c98710150c1e984acdd9c374c72db2b9e58477..b01d093825977236ec1c7864195e65e5e534b336 100644 (file)
@@ -86,8 +86,9 @@ ArdourButton::ArdourButton (Element e)
        , _fixed_diameter (true)
        , _distinct_led_click (false)
        , _hovering (false)
+       , _focused (false)
 {
-       ColorsChanged.connect (sigc::mem_fun (*this, &ArdourButton::color_handler));
+       ARDOUR_UI_UTILS::ColorsChanged.connect (sigc::mem_fun (*this, &ArdourButton::color_handler));
 }
 
 ArdourButton::ArdourButton (const std::string& str, Element e)
@@ -122,6 +123,7 @@ ArdourButton::ArdourButton (const std::string& str, Element e)
        , _fixed_diameter (true)
        , _distinct_led_click (false)
        , _hovering (false)
+       , _focused (false)
 {
        set_text (str);
 }
@@ -447,6 +449,16 @@ ArdourButton::render (cairo_t* cr, cairo_rectangle_t *)
                        cairo_fill (cr);
                }
        }
+       if (_focused) {
+               rounded_function (cr, 1.5, 1.5, get_width() - 3, get_height() - 3, _corner_radius);
+               cairo_set_source_rgba (cr, 0.905, 0.917, 0.925, 0.8);
+               double dashes = 1;
+               cairo_set_dash (cr, &dashes, 1, 0);
+               cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+               cairo_set_line_width (cr, 1.0);
+               cairo_stroke (cr);
+               cairo_set_dash (cr, 0, 0, 0);
+       }
 }
 
 void
@@ -921,6 +933,37 @@ ArdourButton::set_visual_state (Gtkmm2ext::VisualState s)
        }
 }
        
+
+bool
+ArdourButton::on_focus_in_event (GdkEventFocus* ev)
+{
+       _focused = true;
+       queue_draw ();
+       return CairoWidget::on_focus_in_event (ev);
+}
+
+bool
+ArdourButton::on_focus_out_event (GdkEventFocus* ev)
+{
+       _focused = false;
+       queue_draw ();
+       return CairoWidget::on_focus_out_event (ev);
+}
+
+bool
+ArdourButton::on_key_release_event (GdkEventKey *ev) {
+       if (_focused &&
+                       (ev->keyval == GDK_KEY_space || ev->keyval == GDK_Return))
+       {
+               signal_clicked();
+               if (_action) {
+                       _action->activate ();
+               }
+               return true;
+       }
+       return CairoWidget::on_key_release_event (ev);
+}
+
 bool
 ArdourButton::on_enter_notify_event (GdkEventCrossing* ev)
 {
index 18de7230d2291d6a5604637a12707f743a175f4b..dd6337dcef0f79ed98c4bd040dd583c6b3f1d804 100644 (file)
@@ -108,6 +108,9 @@ class ArdourButton : public CairoWidget , public Gtkmm2ext::Activatable
        void on_name_changed ();
        bool on_enter_notify_event (GdkEventCrossing*);
        bool on_leave_notify_event (GdkEventCrossing*);
+       bool on_focus_in_event (GdkEventFocus*);
+       bool on_focus_out_event (GdkEventFocus*);
+       bool on_key_release_event (GdkEventKey *);
 
        void controllable_changed ();
        PBD::ScopedConnection watch_connection;
@@ -168,6 +171,7 @@ class ArdourButton : public CairoWidget , public Gtkmm2ext::Activatable
        bool _fixed_diameter;
        bool _distinct_led_click;
        bool _hovering;
+       bool _focused;
     
        static bool _flat_buttons;
 
index 3690ee023bbe545fa7cb59016f478f0507ad5adc..34027ca0c3600f3f207e2b460034234a15662389 100644 (file)
@@ -32,6 +32,7 @@
 using namespace std;
 using namespace Gtk;
 using namespace Gtkmm2ext;
+using namespace ARDOUR_UI_UTILS;
 
 ArdourDialog::ArdourDialog (string title, bool modal, bool use_seperator)
        : Dialog (title, modal, use_seperator)
@@ -65,7 +66,10 @@ ArdourDialog::~ArdourDialog ()
 bool
 ArdourDialog::on_key_press_event (GdkEventKey* ev)
 {
-       return relay_key_press (ev, this);
+       if (!relay_key_press (ev, this)) {
+               return Gtk::Window::on_key_press_event(ev);
+       }
+       return true;
 }
 
 bool
index 00ac6fb634306487701f039a7b0960a48cdce266..cde9d0d9eeb1a13754f7a19e0cc03c6d0bb0d9bf 100644 (file)
@@ -50,6 +50,7 @@
 #include "pbd/enumwriter.h"
 #include "pbd/memento_command.h"
 #include "pbd/openuri.h"
+#include "pbd/stl_delete.h"
 #include "pbd/file_utils.h"
 #include "pbd/localtime_r.h"
 
@@ -137,6 +138,7 @@ typedef uint64_t microseconds_t;
 #include "i18n.h"
 
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtkmm2ext;
 using namespace Gtk;
@@ -755,6 +757,7 @@ ARDOUR_UI::starting ()
        try {
                audio_midi_setup.get (true);
        } catch (...) {
+               std::cerr << "audio-midi engine setup failed."<< std::endl;
                return -1;
        }
 
@@ -841,6 +844,7 @@ ARDOUR_UI::starting ()
                const bool new_session_required = (ARDOUR_COMMAND_LINE::new_session || brand_new_user);
 
                if (get_session_parameters (false, new_session_required, ARDOUR_COMMAND_LINE::load_template)) {
+                       std::cerr << "Cannot get session parameters."<< std::endl;
                        return -1;
                }
        }
@@ -1413,7 +1417,7 @@ ARDOUR_UI::redisplay_recent_sessions ()
 
                get_state_files_in_directory (*i, state_file_paths);
 
-               vector<string*>* states;
+               vector<string> states;
                vector<const gchar*> item;
                string fullpath = *i;
 
@@ -1430,8 +1434,9 @@ ARDOUR_UI::redisplay_recent_sessions ()
                }
 
                /* now get available states for this session */
+               states = Session::possible_states (fullpath);
 
-               if ((states = Session::possible_states (fullpath)) == 0) {
+               if (states.empty()) {
                        /* no state file? */
                        continue;
                }
@@ -1440,14 +1445,14 @@ ARDOUR_UI::redisplay_recent_sessions ()
 
                Gtk::TreeModel::Row row = *(recent_session_model->append());
 
-               row[recent_session_columns.visible_name] = Glib::path_get_basename (fullpath);
                row[recent_session_columns.fullpath] = fullpath;
                row[recent_session_columns.tip] = Glib::Markup::escape_text (fullpath);
 
                if (state_file_names.size() > 1) {
+                       // multiple session files in the session directory - show the directory name.
+                       row[recent_session_columns.visible_name] = Glib::path_get_basename (fullpath);
 
                        // add the children
-
                        for (std::vector<std::string>::iterator i2 = state_file_names.begin();
                                        i2 != state_file_names.end(); ++i2)
                        {
@@ -1458,6 +1463,9 @@ ARDOUR_UI::redisplay_recent_sessions ()
                                child_row[recent_session_columns.fullpath] = fullpath;
                                child_row[recent_session_columns.tip] = Glib::Markup::escape_text (fullpath);
                        }
+               } else {
+                       // only a single session file in the directory - show its actual name.
+                       row[recent_session_columns.visible_name] = state_file_names.front ();
                }
        }
 
@@ -3384,9 +3392,8 @@ ARDOUR_UI::add_route (Gtk::Window* float_window)
 
        setup_order_hint();
 
-       PBD::ScopedConnection idle_connection;
-
        string template_path = add_route_dialog->track_template();
+       DisplaySuspender ds;
 
        if (!template_path.empty()) {
                if (add_route_dialog->name_template_is_default())  {
@@ -3427,8 +3434,6 @@ ARDOUR_UI::add_route (Gtk::Window* float_window)
                session_add_audio_bus (input_chan.n_audio(), output_chan.n_audio(), route_group, count, name_template);
                break;
        }
-
-       /* idle connection will end at scope end */
 }
 
 void
index dfb2b72a1ebafee391e21c3a27256faf6ff496c6..1c637a436da091b8f40f785c103425823d1e45ae 100644 (file)
@@ -507,7 +507,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr
 
        struct RecentSessionsSorter {
                bool operator() (std::pair<std::string,std::string> a, std::pair<std::string,std::string> b) const {
-                   return cmp_nocase(a.first, b.first) == -1;
+                   return ARDOUR::cmp_nocase(a.first, b.first) == -1;
            }
        };
 
index e9efb1d03aee94d8d0d61a0112045c3eef31e098..f78d096000a13f5181ef94483bdd9ef91002c587 100644 (file)
@@ -64,6 +64,7 @@ using namespace PBD;
 using namespace Gtkmm2ext;
 using namespace Gtk;
 using namespace Glib;
+using namespace ARDOUR_UI_UTILS;
 
 int
 ARDOUR_UI::setup_windows ()
index 35dbe3cfc9702da1038bfecdd915b22a2c0ef022..6fef5b94e49d6dead1bbcc54141db8c1cf6f8d87 100644 (file)
 #include "ardour/profile.h"
 #include "ardour/session.h"
 
-#ifdef interface
-#undef interface
-#endif
-
 #include "actions.h"
 #include "add_route_dialog.h"
 #include "add_video_dialog.h"
index 454c657e6e90479f86c7342cc210136e14b7818c..6c69f4279345ee5d4f02436dfa9484df658a470a 100644 (file)
@@ -51,7 +51,6 @@
 #include "actions.h"
 #include "mixer_ui.h"
 #include "startup.h"
-#include "utils.h"
 #include "window_manager.h"
 #include "global_port_matrix.h"
 #include "location_ui.h"
index e1435f7a8dae4dc65e479a11ebe6d9237b123bf2..365810df75f0c3b3fc48d337958bdbddfb4bb995 100644 (file)
@@ -417,6 +417,13 @@ ARDOUR_UI::parameter_changed (std::string p)
                                editor_meter_peak_display.show();
                        } 
                }
+       } else if (p == "waveform-scale") {
+               ArdourCanvas::WaveView::set_global_logscaled (Config->get_waveform_scale() == Logarithmic);
+       } else if (p == "waveform-shape") {
+               ArdourCanvas::WaveView::set_global_shape (Config->get_waveform_shape() == Rectified
+                               ? ArdourCanvas::WaveView::Rectified : ArdourCanvas::WaveView::Normal);
+       } else if (p == "show-waveform-clipping") {
+               ArdourCanvas::WaveView::set_global_show_waveform_clipping (ARDOUR_UI::config()->get_show_waveform_clipping());
        }
 }
 
index b50a83c31f673ab5fff2a460ffde2baa4f33a787..6b325aca8534325d9f73fb96c52fdeb53e6a937d 100644 (file)
@@ -30,6 +30,7 @@
 using namespace std;
 using namespace Gtk;
 using namespace Gtkmm2ext;
+using namespace ARDOUR_UI_UTILS;
 
 ArdourWindow::ArdourWindow (string title)
        : Window ()
index 64358eaadffd5a924752fa94695f97a13720d2a7..0c7f41f6ebe699b4b48d43a9a631df8ef0753119 100644 (file)
@@ -45,6 +45,7 @@
 #include "i18n.h"
 
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtk;
 using namespace std;
index 730cd99b37cf2cb8c9f700930bf096a1dd4300fd..2041d20335feb17e289c4d488db29f4817214416 100644 (file)
@@ -32,7 +32,6 @@
 #include "audio_region_editor.h"
 #include "audio_region_view.h"
 #include "ardour_ui.h"
-#include "utils.h"
 #include "gui_thread.h"
 
 #include "i18n.h"
index add985dd6f13f2c2301774373df7e588833ba23e..72bf08e79939a41d7fc8cf62788056918276194d 100644 (file)
@@ -43,7 +43,7 @@
 #include "canvas/poly_line.h"
 #include "canvas/line.h"
 #include "canvas/text.h"
-#include "canvas/curve.h"
+#include "canvas/xfade_curve.h"
 #include "canvas/debug.h"
 #include "canvas/utils.h"
 
@@ -57,7 +57,6 @@
 #include "control_point.h"
 #include "ghostregion.h"
 #include "audio_time_axis.h"
-#include "utils.h"
 #include "rgb_macros.h"
 #include "gui_thread.h"
 #include "ardour_ui.h"
@@ -73,44 +72,46 @@ using namespace Editing;
 using namespace ArdourCanvas;
 
 static const int32_t sync_mark_width = 9;
-static double const handle_size = 15; /* height of fade handles */
+static double const handle_size = 10; /* height of fade handles */
 
-AudioRegionView::AudioRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr<AudioRegion> r, double spu,
-                                 Gdk::Color const & basic_color)
+AudioRegionView::AudioRegionView (ArdourCanvas::Container *parent, RouteTimeAxisView &tv, boost::shared_ptr<AudioRegion> r, double spu,
+                                 uint32_t basic_color)
        : RegionView (parent, tv, r, spu, basic_color)
        , sync_mark(0)
        , fade_in_handle(0)
        , fade_out_handle(0)
-       , start_xfade_in (0)
-       , start_xfade_out (0)
+       , fade_in_trim_handle(0)
+       , fade_out_trim_handle(0)
+       , start_xfade_curve (0)
        , start_xfade_rect (0)
        , _start_xfade_visible (false)
-       , end_xfade_in (0)
-       , end_xfade_out (0)
+       , end_xfade_curve (0)
        , end_xfade_rect (0)
        , _end_xfade_visible (false)
        , _amplitude_above_axis(1.0)
-       , fade_color(0)
+       , trim_fade_in_drag_active(false)
+       , trim_fade_out_drag_active(false)
 {
        Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&AudioRegionView::parameter_changed, this, _1), gui_context());
 }
 
-AudioRegionView::AudioRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr<AudioRegion> r, double spu,
-                                 Gdk::Color const & basic_color, bool recording, TimeAxisViewItem::Visibility visibility)
+AudioRegionView::AudioRegionView (ArdourCanvas::Container *parent, RouteTimeAxisView &tv, boost::shared_ptr<AudioRegion> r, double spu,
+                                 uint32_t basic_color, bool recording, TimeAxisViewItem::Visibility visibility)
        : RegionView (parent, tv, r, spu, basic_color, recording, visibility)
        , sync_mark(0)
        , fade_in_handle(0)
        , fade_out_handle(0)
-       , start_xfade_in (0)
-       , start_xfade_out (0)
+       , fade_in_trim_handle(0)
+       , fade_out_trim_handle(0)
+       , start_xfade_curve (0)
        , start_xfade_rect (0)
        , _start_xfade_visible (false)
-       , end_xfade_in (0)
-       , end_xfade_out (0)
+       , end_xfade_curve (0)
        , end_xfade_rect (0)
        , _end_xfade_visible (false)
        , _amplitude_above_axis(1.0)
-       , fade_color(0)
+       , trim_fade_in_drag_active(false)
+       , trim_fade_out_drag_active(false)
 {
        Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&AudioRegionView::parameter_changed, this, _1), gui_context());
 }
@@ -119,56 +120,63 @@ AudioRegionView::AudioRegionView (const AudioRegionView& other, boost::shared_pt
        : RegionView (other, boost::shared_ptr<Region> (other_region))
        , fade_in_handle(0)
        , fade_out_handle(0)
-       , start_xfade_in (0)
-       , start_xfade_out (0)
+       , fade_in_trim_handle(0)
+       , fade_out_trim_handle(0)
+       , start_xfade_curve (0)
        , start_xfade_rect (0)
        , _start_xfade_visible (false)
-       , end_xfade_in (0)
-       , end_xfade_out (0)
+       , end_xfade_curve (0)
        , end_xfade_rect (0)
        , _end_xfade_visible (false)
        , _amplitude_above_axis (other._amplitude_above_axis)
-       , fade_color(0)
+       , trim_fade_in_drag_active(false)
+       , trim_fade_out_drag_active(false)
 {
-       Gdk::Color c;
-       int r,g,b,a;
-
-       UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
-       c.set_rgb_p (r/255.0, g/255.0, b/255.0);
-
-       init (c, true);
+       init (true);
 
        Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&AudioRegionView::parameter_changed, this, _1), gui_context());
 }
 
 void
-AudioRegionView::init (Gdk::Color const & basic_color, bool wfd)
+AudioRegionView::init (bool wfd)
 {
        // FIXME: Some redundancy here with RegionView::init.  Need to figure out
        // where order is important and where it isn't...
 
-       RegionView::init (basic_color, wfd);
+       RegionView::init (wfd);
 
        _amplitude_above_axis = 1.0;
 
-       compute_colors (basic_color);
-
        create_waves ();
 
        if (!_recregion) {
                fade_in_handle = new ArdourCanvas::Rectangle (group);
                CANVAS_DEBUG_NAME (fade_in_handle, string_compose ("fade in handle for %1", region()->name()));
-               fade_in_handle->set_outline_color (RGBA_TO_UINT (0, 0, 0, 255));
+               fade_in_handle->set_outline_color (ArdourCanvas::rgba_to_color (0, 0, 0, 1.0));
                fade_in_handle->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
                fade_in_handle->set_data ("regionview", this);
                fade_in_handle->hide ();
 
                fade_out_handle = new ArdourCanvas::Rectangle (group);
                CANVAS_DEBUG_NAME (fade_out_handle, string_compose ("fade out handle for %1", region()->name()));
-               fade_out_handle->set_outline_color (RGBA_TO_UINT (0, 0, 0, 255));
+               fade_out_handle->set_outline_color (ArdourCanvas::rgba_to_color (0, 0, 0, 1.0));
                fade_out_handle->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
                fade_out_handle->set_data ("regionview", this);
                fade_out_handle->hide ();
+
+               fade_in_trim_handle = new ArdourCanvas::Rectangle (group);
+               CANVAS_DEBUG_NAME (fade_in_handle, string_compose ("fade in trim handle for %1", region()->name()));
+               fade_in_trim_handle->set_outline_color (ArdourCanvas::rgba_to_color (0, 0, 0, 1.0));
+               fade_in_trim_handle->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
+               fade_in_trim_handle->set_data ("regionview", this);
+               fade_in_trim_handle->hide ();
+
+               fade_out_trim_handle = new ArdourCanvas::Rectangle (group);
+               CANVAS_DEBUG_NAME (fade_out_handle, string_compose ("fade out trim handle for %1", region()->name()));
+               fade_out_trim_handle->set_outline_color (ArdourCanvas::rgba_to_color (0, 0, 0, 1.0));
+               fade_out_trim_handle->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
+               fade_out_trim_handle->set_data ("regionview", this);
+               fade_out_trim_handle->hide ();
        }
 
        setup_fade_handle_positions ();
@@ -205,17 +213,24 @@ AudioRegionView::init (Gdk::Color const & basic_color, bool wfd)
        reset_width_dependent_items (_pixel_width);
 
        if (fade_in_handle) {
-               fade_in_handle->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_fade_in_handle_event), fade_in_handle, this));
+               fade_in_handle->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_fade_in_handle_event), fade_in_handle, this, false));
        }
 
        if (fade_out_handle) {
-               fade_out_handle->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_fade_out_handle_event), fade_out_handle, this));
+               fade_out_handle->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_fade_out_handle_event), fade_out_handle, this, false));
+       }
+
+       if (fade_in_trim_handle) {
+               fade_in_trim_handle->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_fade_in_handle_event), fade_in_trim_handle, this, true));
+       }
+
+       if (fade_out_trim_handle) {
+               fade_out_trim_handle->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_fade_out_handle_event), fade_out_trim_handle, this, true));
        }
 
        set_colors ();
 
        setup_waveform_visibility ();
-       setup_waveform_shape ();
 
        if (frame_handle_start) {
                frame_handle_start->raise_to_top ();
@@ -298,9 +313,10 @@ AudioRegionView::fade_in_active_changed ()
 {
        if (start_xfade_rect) {
                if (audio_region()->fade_in_active()) {
-                       start_xfade_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ActiveCrossfade());
+                       start_xfade_rect->set_fill (false);
                } else {
                        start_xfade_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveCrossfade());
+                       start_xfade_rect->set_fill (true);
                }
        }
 }
@@ -310,9 +326,10 @@ AudioRegionView::fade_out_active_changed ()
 {
        if (end_xfade_rect) {
                if (audio_region()->fade_out_active()) {
-                       end_xfade_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ActiveCrossfade());
+                       end_xfade_rect->set_fill (false);
                } else {        
                        end_xfade_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveCrossfade());
+                       end_xfade_rect->set_fill (true);
                }
        }
 }
@@ -389,14 +406,13 @@ AudioRegionView::reset_width_dependent_items (double pixel_width)
        RegionView::reset_width_dependent_items(pixel_width);
        assert(_pixel_width == pixel_width);
 
-       if (fade_in_handle) {
-               if (pixel_width <= 6.0 || _height < 5.0 || !trackview.session()->config.get_show_region_fades()) {
-                       fade_in_handle->hide();
-                       fade_out_handle->hide();
-               } else {
-                       //fade_in_handle->show();
-                       //fade_out_handle->show();
-               }
+       if (pixel_width <= 20.0 || _height < 5.0 || !trackview.session()->config.get_show_region_fades()) {
+               if (fade_in_handle)       { fade_in_handle->hide(); }
+               if (fade_out_handle)      { fade_out_handle->hide(); }
+               if (fade_in_trim_handle)  { fade_in_trim_handle->hide(); }
+               if (fade_out_trim_handle) { fade_out_trim_handle->hide(); }
+               if (start_xfade_rect)     { start_xfade_rect->set_outline (false); }
+               if (end_xfade_rect)       { end_xfade_rect->set_outline (false); }
        }
 
        AnalysisFeatureList analysis_features = _region->transients();
@@ -442,6 +458,16 @@ AudioRegionView::setup_fade_handle_positions()
                fade_out_handle->set_y0 (handle_pos);
                fade_out_handle->set_y1 (handle_pos + handle_size);
        }
+
+       if (fade_in_trim_handle) {
+               fade_in_trim_handle->set_y0 (_height - handle_size);
+               fade_in_trim_handle->set_y1 (_height);
+       }
+
+       if (fade_out_trim_handle) {
+               fade_out_trim_handle->set_y0 (_height - handle_size );
+               fade_out_trim_handle->set_y1 (_height);
+       }
 }
 
 void
@@ -498,13 +524,15 @@ AudioRegionView::set_height (gdouble height)
        if (name_text) {
                name_text->raise_to_top();
        }
+
+       setup_fade_handle_positions();
 }
 
 void
 AudioRegionView::reset_fade_shapes ()
 {
-       reset_fade_in_shape ();
-       reset_fade_out_shape ();
+       if (!trim_fade_in_drag_active) { reset_fade_in_shape (); }
+       if (!trim_fade_out_drag_active) { reset_fade_out_shape (); }
 }
 
 void
@@ -514,8 +542,9 @@ AudioRegionView::reset_fade_in_shape ()
 }
 
 void
-AudioRegionView::reset_fade_in_shape_width (boost::shared_ptr<AudioRegion> ar, framecnt_t width)
+AudioRegionView::reset_fade_in_shape_width (boost::shared_ptr<AudioRegion> ar, framecnt_t width, bool drag_active)
 {
+       trim_fade_in_drag_active = drag_active;
        if (fade_in_handle == 0) {
                return;
        }
@@ -532,6 +561,16 @@ AudioRegionView::reset_fade_in_shape_width (boost::shared_ptr<AudioRegion> ar, f
        fade_in_handle->set_x0 (handle_left);
        fade_in_handle->set_x1 (handle_left + handle_size);
 
+       if (fade_in_trim_handle) {
+               fade_in_trim_handle->set_x0 (0);
+               fade_in_trim_handle->set_x1 (handle_size);
+       }
+
+       if (fade_in_handle->visible()) {
+               //see comment for drag_start
+               entered(false);
+       }
+
        if (pwidth < 5) {
                hide_start_xfade();
                return;
@@ -553,12 +592,10 @@ AudioRegionView::reset_fade_in_shape_width (boost::shared_ptr<AudioRegion> ar, f
        /* points *MUST* be in anti-clockwise order */
 
        Points points;
-       Points::size_type npoints;
        Points::size_type pi;
        boost::shared_ptr<const Evoral::ControlList> list (audio_region()->fade_in());
        Evoral::ControlList::const_iterator x;
        double length = list->length();
-       npoints = list->size();
 
        points.assign (list->size(), Duple());
 
@@ -584,8 +621,9 @@ AudioRegionView::reset_fade_out_shape ()
 }
 
 void
-AudioRegionView::reset_fade_out_shape_width (boost::shared_ptr<AudioRegion> ar, framecnt_t width)
+AudioRegionView::reset_fade_out_shape_width (boost::shared_ptr<AudioRegion> ar, framecnt_t width, bool drag_active)
 {
+       trim_fade_out_drag_active = drag_active;
        if (fade_out_handle == 0) {
                return;
        }
@@ -594,19 +632,28 @@ AudioRegionView::reset_fade_out_shape_width (boost::shared_ptr<AudioRegion> ar,
 
        width = std::max ((framecnt_t) 64, width);
 
-       double const pwidth = trackview.editor().sample_to_pixel (width);
+       double const pwidth = rint(trackview.editor().sample_to_pixel (width));
 
        /* the right edge should be right on the region frame is the pixel
         * width is zero. Hence the additional + 1.0 at the end.
         */
 
-       double const handle_right = trackview.editor().sample_to_pixel (_region->length()) + TimeAxisViewItem::RIGHT_EDGE_SHIFT - pwidth;
+       double const handle_right = rint(trackview.editor().sample_to_pixel (_region->length()) + TimeAxisViewItem::RIGHT_EDGE_SHIFT - pwidth);
+       double const trim_handle_right = rint(trackview.editor().sample_to_pixel (_region->length()) + TimeAxisViewItem::RIGHT_EDGE_SHIFT);
 
        /* Put the fade out handle so that its right side is at the end-of-fade line;
         */
-       fade_out_handle->set_x0 (handle_right - handle_size);
-       fade_out_handle->set_x1 (handle_right);
+       fade_out_handle->set_x0 (1 + handle_right - handle_size);
+       fade_out_handle->set_x1 (1 + handle_right);
+       if (fade_out_trim_handle) {
+               fade_out_trim_handle->set_x0 (1 + trim_handle_right - handle_size);
+               fade_out_trim_handle->set_x1 (1 + trim_handle_right);
+       }
 
+       if (fade_out_handle->visible()) {
+               //see comment for drag_start
+               entered(false);
+       }
        /* don't show shape if its too small */
 
        if (pwidth < 5) {
@@ -630,12 +677,10 @@ AudioRegionView::reset_fade_out_shape_width (boost::shared_ptr<AudioRegion> ar,
        /* points *MUST* be in anti-clockwise order */
        
        Points points;
-       Points::size_type npoints;
        Points::size_type pi;
        boost::shared_ptr<const Evoral::ControlList> list (audio_region()->fade_out());
        Evoral::ControlList::const_iterator x;
        double length = list->length();
-       npoints = list->size();
 
        points.assign (list->size(), Duple());
 
@@ -688,36 +733,26 @@ AudioRegionView::redraw_start_xfade_to (boost::shared_ptr<AudioRegion> ar, frame
                return;
        }
 
-       if (!start_xfade_in) {
-               start_xfade_in = new ArdourCanvas::Curve (group);
-               CANVAS_DEBUG_NAME (start_xfade_in, string_compose ("xfade start in line for %1", region()->name()));
-               start_xfade_in->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
-               start_xfade_in->set_outline_width (1.5);
-               start_xfade_in->set_ignore_events (true);
+       if (!start_xfade_curve) {
+               start_xfade_curve = new ArdourCanvas::XFadeCurve (group, ArdourCanvas::XFadeCurve::Start);
+               CANVAS_DEBUG_NAME (start_xfade_curve, string_compose ("xfade start out line for %1", region()->name()));
+               start_xfade_curve->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ActiveCrossfade());
+               start_xfade_curve->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
+               start_xfade_curve->set_ignore_events (true);
        }
-
-       if (!start_xfade_out) {
-               start_xfade_out = new ArdourCanvas::Curve (group);
-               CANVAS_DEBUG_NAME (start_xfade_out, string_compose ("xfade start out line for %1", region()->name()));
-               uint32_t col = UINT_RGBA_CHANGE_A (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine(), 128);
-               start_xfade_out->set_outline_color (col);
-               start_xfade_out->set_outline_width (2.0);
-               start_xfade_out->set_ignore_events (true);
-       }
-
        if (!start_xfade_rect) {
                start_xfade_rect = new ArdourCanvas::Rectangle (group);
                CANVAS_DEBUG_NAME (start_xfade_rect, string_compose ("xfade start rect for %1", region()->name()));
-               start_xfade_rect->set_fill (true);
-               start_xfade_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ActiveCrossfade());
+               start_xfade_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
+               start_xfade_rect->set_fill (false);
                start_xfade_rect->set_outline (false);
+               start_xfade_rect->set_outline_what (ArdourCanvas::Rectangle::What (ArdourCanvas::Rectangle::RIGHT));
+               start_xfade_rect->set_outline_width (0.5);
                start_xfade_rect->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_start_xfade_event), start_xfade_rect, this));
                start_xfade_rect->set_data ("regionview", this);
        }
 
-       start_xfade_rect->set (ArdourCanvas::Rect (1.0, 0.0, rect_width, effective_height));
-
-       start_xfade_in->set (points);
+       start_xfade_rect->set (ArdourCanvas::Rect (0.0, 0.0, rect_width, effective_height));
 
        /* fade out line */
 
@@ -762,7 +797,7 @@ AudioRegionView::redraw_start_xfade_to (boost::shared_ptr<AudioRegion> ar, frame
                }
        }
 
-       start_xfade_out->set (ipoints);
+       start_xfade_curve->set_inout (points, ipoints);
 
        show_start_xfade();
 }
@@ -789,37 +824,28 @@ AudioRegionView::redraw_end_xfade_to (boost::shared_ptr<AudioRegion> ar, framecn
                return;
        }
 
-       if (!end_xfade_in) {
-               end_xfade_in = new ArdourCanvas::Curve (group);
-               CANVAS_DEBUG_NAME (end_xfade_in, string_compose ("xfade end in line for %1", region()->name()));
-               uint32_t col UINT_RGBA_CHANGE_A (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine(), 128);
-               end_xfade_in->set_outline_color (col);
-               end_xfade_in->set_outline_width (1.5);  
-               end_xfade_in->set_ignore_events (true);
-}
-
-       if (!end_xfade_out) {
-               end_xfade_out = new ArdourCanvas::Curve (group);
-               CANVAS_DEBUG_NAME (end_xfade_out, string_compose ("xfade end out line for %1", region()->name()));
-               end_xfade_out->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
-               end_xfade_out->set_outline_width (2.0);
-               end_xfade_out->set_ignore_events (true);
+       if (!end_xfade_curve) {
+               end_xfade_curve = new ArdourCanvas::XFadeCurve (group, ArdourCanvas::XFadeCurve::End);
+               CANVAS_DEBUG_NAME (end_xfade_curve, string_compose ("xfade end out line for %1", region()->name()));
+               end_xfade_curve->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ActiveCrossfade());
+               end_xfade_curve->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
+               end_xfade_curve->set_ignore_events (true);
        }
 
        if (!end_xfade_rect) {
                end_xfade_rect = new ArdourCanvas::Rectangle (group);
                CANVAS_DEBUG_NAME (end_xfade_rect, string_compose ("xfade end rect for %1", region()->name()));
-               end_xfade_rect->set_fill (true);
-               end_xfade_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ActiveCrossfade());
-               end_xfade_rect->set_outline (0);
+               end_xfade_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
+               end_xfade_rect->set_fill (false);
+               end_xfade_rect->set_outline (false);
+               end_xfade_rect->set_outline_what (ArdourCanvas::Rectangle::What (ArdourCanvas::Rectangle::LEFT));
+               end_xfade_rect->set_outline_width (0.5);
                end_xfade_rect->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_end_xfade_event), end_xfade_rect, this));
                end_xfade_rect->set_data ("regionview", this);
        }
 
        end_xfade_rect->set (ArdourCanvas::Rect (rect_edge, 0.0, rect_edge + rect_width + TimeAxisViewItem::RIGHT_EDGE_SHIFT, effective_height));
 
-       end_xfade_in->set (points);
-
        /* fade in line */
 
        boost::shared_ptr<AutomationList> inverse = ar->inverse_fade_out ();
@@ -867,7 +893,7 @@ AudioRegionView::redraw_end_xfade_to (boost::shared_ptr<AudioRegion> ar, framecn
                }
        }
 
-       end_xfade_out->set (ipoints);
+       end_xfade_curve->set_inout (ipoints, points);
 
        show_end_xfade();
 }
@@ -882,11 +908,8 @@ AudioRegionView::hide_xfades ()
 void
 AudioRegionView::hide_start_xfade ()
 {
-       if (start_xfade_in) {
-               start_xfade_in->hide();
-       }
-       if (start_xfade_out) {
-               start_xfade_out->hide();
+       if (start_xfade_curve) {
+               start_xfade_curve->hide();
        }
        if (start_xfade_rect) {
                start_xfade_rect->hide ();
@@ -898,11 +921,8 @@ AudioRegionView::hide_start_xfade ()
 void
 AudioRegionView::hide_end_xfade ()
 {
-       if (end_xfade_in) {
-               end_xfade_in->hide();
-       }
-       if (end_xfade_out) {
-               end_xfade_out->hide();
+       if (end_xfade_curve) {
+               end_xfade_curve->hide();
        }
        if (end_xfade_rect) {
                end_xfade_rect->hide ();
@@ -914,11 +934,8 @@ AudioRegionView::hide_end_xfade ()
 void
 AudioRegionView::show_start_xfade ()
 {
-       if (start_xfade_in) {
-               start_xfade_in->show();
-       }
-       if (start_xfade_out) {
-               start_xfade_out->show();
+       if (start_xfade_curve) {
+               start_xfade_curve->show();
        }
        if (start_xfade_rect) {
                start_xfade_rect->show ();
@@ -930,11 +947,8 @@ AudioRegionView::show_start_xfade ()
 void
 AudioRegionView::show_end_xfade ()
 {
-       if (end_xfade_in) {
-               end_xfade_in->show();
-       }
-       if (end_xfade_out) {
-               end_xfade_out->show();
+       if (end_xfade_curve) {
+               end_xfade_curve->show();
        }
        if (end_xfade_rect) {
                end_xfade_rect->show ();
@@ -969,16 +983,6 @@ AudioRegionView::set_amplitude_above_axis (gdouble a)
        }
 }
 
-void
-AudioRegionView::compute_colors (Gdk::Color const & basic_color)
-{
-       RegionView::compute_colors (basic_color);
-
-       /* gain color computed in envelope_active_changed() */
-
-       fade_color = UINT_RGBA_CHANGE_A (fill_color, 120);
-}
-
 void
 AudioRegionView::set_colors ()
 {
@@ -992,26 +996,20 @@ AudioRegionView::set_colors ()
 
        set_waveform_colors ();
 
-       if (start_xfade_in) {
-               start_xfade_in->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
-       }
-       if (start_xfade_out) {
-               uint32_t col UINT_RGBA_CHANGE_A (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine(), 128);
-               start_xfade_out->set_outline_color (col);
+       if (start_xfade_curve) {
+               start_xfade_curve->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ActiveCrossfade());
+               start_xfade_curve->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
        }
-       if (end_xfade_in) {
-               end_xfade_in->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
-       }
-       if (end_xfade_out) {
-               uint32_t col UINT_RGBA_CHANGE_A (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine(), 128);
-               end_xfade_out->set_outline_color (col);
+       if (end_xfade_curve) {
+               end_xfade_curve->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ActiveCrossfade());
+               end_xfade_curve->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
        }
 
        if (start_xfade_rect) {
-               start_xfade_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ActiveCrossfade());
+               start_xfade_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
        }
        if (end_xfade_rect) {
-               end_xfade_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ActiveCrossfade());
+               end_xfade_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
        }
 }
 
@@ -1144,6 +1142,9 @@ AudioRegionView::create_one_wave (uint32_t which, bool /*direct*/)
        wave->set_height (ht);
        wave->set_samples_per_pixel (samples_per_pixel);
        wave->set_show_zero_line (true);
+       wave->set_clip_level (Config->get_waveform_clip_level ());
+
+       wave->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_wave_view_event), wave, this));
        
        switch (Config->get_waveform_shape()) {
        case Rectified:
@@ -1155,6 +1156,10 @@ AudioRegionView::create_one_wave (uint32_t which, bool /*direct*/)
                
        wave->set_logscaled (Config->get_waveform_scale() == Logarithmic);
 
+       vector<ArdourCanvas::WaveView*> v;
+       v.push_back (wave);
+       set_some_waveform_colors (v);
+
        if (!Config->get_show_waveforms ()) {
                wave->hide();
        }
@@ -1259,35 +1264,6 @@ AudioRegionView::remove_gain_point_event (ArdourCanvas::Item *item, GdkEvent* /*
        audio_region()->envelope()->erase (cp->model());
 }
 
-void
-AudioRegionView::setup_waveform_shape ()
-{
-       WaveView::Shape shape;
-
-       switch (Config->get_waveform_shape()) {
-       case Rectified:
-               shape = WaveView::Rectified;
-               break;
-       default:
-               shape = WaveView::Normal;
-       }
-       for (vector<WaveView *>::iterator wave = waves.begin(); wave != waves.end() ; ++wave) {
-               (*wave)->set_shape (shape);
-       }
-}
-
-void
-AudioRegionView::setup_waveform_scale ()
-{
-       WaveView::set_global_logscaled (Config->get_waveform_scale() == Logarithmic);
-}
-
-void
-AudioRegionView::setup_waveform_clipping ()
-{
-       WaveView::set_global_show_waveform_clipping (ARDOUR_UI::config()->get_show_waveform_clipping());
-}
-
 GhostRegion*
 AudioRegionView::add_ghost (TimeAxisView& tv)
 {
@@ -1335,11 +1311,39 @@ AudioRegionView::entered (bool internal_editing)
                gain_line->add_visibility (AutomationLine::ControlPoints);
        }
 
-       if (fade_in_handle && !internal_editing) {
-               fade_in_handle->show ();
-               fade_out_handle->show ();
-               fade_out_handle->raise_to_top ();
-               fade_in_handle->raise_to_top ();
+       if (!internal_editing) {
+               if (start_xfade_rect) {
+                       start_xfade_rect->set_outline (true);
+               }
+               if (end_xfade_rect) {
+                       end_xfade_rect->set_outline (true);
+               }
+               if (fade_in_handle) {
+                       fade_in_handle->show ();
+                       fade_in_handle->raise_to_top ();
+               }
+               if (fade_out_handle) {
+                       fade_out_handle->show ();
+                       fade_out_handle->raise_to_top ();
+               }
+               if (fade_in_trim_handle) {
+                       boost::shared_ptr<AudioRegion> ar (audio_region());
+                       if (!ar->locked() && (ar->fade_in()->back()->when > 64 || (ar->can_trim() & Trimmable::FrontTrimEarlier))) {
+                               fade_in_trim_handle->show ();
+                               fade_in_trim_handle->raise_to_top ();
+                       } else {
+                               fade_in_trim_handle->hide ();
+                       }
+               }
+               if (fade_out_trim_handle) {
+                       boost::shared_ptr<AudioRegion> ar (audio_region());
+                       if (!ar->locked() && (ar->fade_out()->back()->when > 64 || (ar->can_trim() & Trimmable::EndTrimLater))) {
+                               fade_out_trim_handle->show ();
+                               fade_out_trim_handle->raise_to_top ();
+                       } else {
+                               fade_out_trim_handle->hide ();
+                       }
+               }
        }
 }
 
@@ -1353,10 +1357,12 @@ AudioRegionView::exited ()
                gain_line->remove_visibility (AutomationLine::ControlPoints);
        }
 
-       if (fade_in_handle) {
-               fade_in_handle->hide ();
-               fade_out_handle->hide ();
-       }
+       if (fade_in_handle)       { fade_in_handle->hide(); }
+       if (fade_out_handle)      { fade_out_handle->hide(); }
+       if (fade_in_trim_handle)  { fade_in_trim_handle->hide(); }
+       if (fade_out_trim_handle) { fade_out_trim_handle->hide(); }
+       if (start_xfade_rect)     { start_xfade_rect->set_outline (false); }
+       if (end_xfade_rect)       { end_xfade_rect->set_outline (false); }
 }
 
 void
@@ -1388,17 +1394,17 @@ AudioRegionView::color_handler ()
 void
 AudioRegionView::set_waveform_colors ()
 {
-        for (vector<ArdourCanvas::WaveView*>::iterator w = waves.begin(); w != waves.end(); ++w) {
-               set_one_waveform_color (*w);
-       }
+       set_some_waveform_colors (waves);
 }
 
 void
-AudioRegionView::set_one_waveform_color (ArdourCanvas::WaveView* wave)
+AudioRegionView::set_some_waveform_colors (vector<ArdourCanvas::WaveView*>& waves_to_color)
 {
        ArdourCanvas::Color fill;
        ArdourCanvas::Color outline;
-       
+       ArdourCanvas::Color clip = ARDOUR_UI::config()->get_canvasvar_WaveFormClip();
+       ArdourCanvas::Color zero = ARDOUR_UI::config()->get_canvasvar_ZeroLine();
+
        if (_selected) {
                if (_region->muted()) {
                        /* hide outline with zero alpha */
@@ -1407,6 +1413,13 @@ AudioRegionView::set_one_waveform_color (ArdourCanvas::WaveView* wave)
                } else {
                        outline = ARDOUR_UI::config()->get_canvasvar_SelectedWaveForm();
                        fill = ARDOUR_UI::config()->get_canvasvar_SelectedWaveFormFill();
+
+                       if (ARDOUR_UI::config()->get_color_regions_using_track_color()) {
+                               /* just use a slightly transparent version of the selected
+                                * color so that some of the track color bleeds through
+                                */
+                               fill = UINT_RGBA_CHANGE_A (fill, 217);
+                       }
                }
        } else {
                if (_recregion) {
@@ -1420,32 +1433,23 @@ AudioRegionView::set_one_waveform_color (ArdourCanvas::WaveView* wave)
                        } else {
                                outline = ARDOUR_UI::config()->get_canvasvar_WaveForm();
                                fill = ARDOUR_UI::config()->get_canvasvar_WaveFormFill();
+
+                               if (ARDOUR_UI::config()->get_color_regions_using_track_color()) {
+                                       /* just use a slightly transparent version of the selected
+                                        * color so that some of the track color bleeds through
+                                        */
+                                       fill = UINT_RGBA_CHANGE_A (fill, 217);
+                               }
                        }
                }
        }
 
-       if (ARDOUR_UI::config()->get_color_regions_using_track_color()) {
-
-               /* just use a slightly transparent version of the selected
-                * color so that some of the track color bleeds through
-                */
-
-               double r, g, b, a;
-               ArdourCanvas::color_to_rgba (fill, r, g, b, a);
-               fill = ArdourCanvas::rgba_to_color (r, g, b, 0.85); /* magic number, not user controllable */
-               outline = ARDOUR_UI::config()->get_canvasvar_WaveForm();
-
-               if (!Config->get_show_name_highlight()) {
-                       /* recolor name text because it needs to contrast with
-                          the waveform background, not the name highlight.
-                       */
-               }
+        for (vector<ArdourCanvas::WaveView*>::iterator w = waves_to_color.begin(); w != waves_to_color.end(); ++w) {
+               (*w)->set_fill_color (fill);
+               (*w)->set_outline_color (outline);
+               (*w)->set_clip_color (clip);
+               (*w)->set_zero_color (zero);
        }
-
-       wave->set_fill_color (fill);
-       wave->set_outline_color (outline);
-       wave->set_clip_color (ARDOUR_UI::config()->get_canvasvar_WaveFormClip());
-       wave->set_zero_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
 }
 
 void
@@ -1455,13 +1459,7 @@ AudioRegionView::set_frame_color ()
                return;
        }
 
-       if (_region->opaque()) {
-               fill_opacity = 130;
-       } else {
-               fill_opacity = 0;
-       }
-
-       TimeAxisViewItem::set_frame_color ();
+       RegionView::set_frame_color ();
 
        set_waveform_colors ();
 }
@@ -1470,19 +1468,21 @@ void
 AudioRegionView::set_fade_visibility (bool yn)
 {
        if (yn) {
-               if (fade_in_handle) {
-                       fade_in_handle->show ();
-               }
-               if (fade_out_handle) {
-                       fade_out_handle->show ();
-               }
-       } else {
-               if (fade_in_handle) {
-                       fade_in_handle->hide ();
-               }
-               if (fade_out_handle) {
-                       fade_out_handle->hide ();
-               }
+               if (start_xfade_curve)    { start_xfade_curve->show (); }
+               if (end_xfade_curve)      { end_xfade_curve->show (); }
+               if (start_xfade_rect)     { start_xfade_rect->show (); }
+               if (end_xfade_rect)       { end_xfade_rect->show (); }
+               } else {
+               if (start_xfade_curve)    { start_xfade_curve->hide(); }
+               if (end_xfade_curve)      { end_xfade_curve->hide(); }
+               if (fade_in_handle)       { fade_in_handle->hide(); }
+               if (fade_out_handle)      { fade_out_handle->hide(); }
+               if (fade_in_trim_handle)  { fade_in_trim_handle->hide(); }
+               if (fade_out_trim_handle) { fade_out_trim_handle->hide(); }
+               if (start_xfade_rect)     { start_xfade_rect->hide (); }
+               if (end_xfade_rect)       { end_xfade_rect->hide (); }
+               if (start_xfade_rect)     { start_xfade_rect->set_outline (false); }
+               if (end_xfade_rect)       { end_xfade_rect->set_outline (false); }
        }
 }
 
@@ -1491,10 +1491,10 @@ AudioRegionView::update_coverage_frames (LayerDisplay d)
 {
        RegionView::update_coverage_frames (d);
 
-       if (fade_in_handle) {
-               fade_in_handle->raise_to_top ();
-               fade_out_handle->raise_to_top ();
-       }
+       if (fade_in_handle)       { fade_in_handle->raise_to_top (); }
+       if (fade_out_handle)      { fade_out_handle->raise_to_top (); }
+       if (fade_in_trim_handle)  { fade_in_trim_handle->raise_to_top (); }
+       if (fade_out_trim_handle) { fade_out_trim_handle->raise_to_top (); }
 }
 
 void
@@ -1624,8 +1624,15 @@ void
 AudioRegionView::drag_end ()
 {
        TimeAxisViewItem::drag_end ();
-
        //see comment for drag_start
+
+       if (fade_in_handle && fade_in_handle->visible()) {
+               // lenght of region or fade changed, re-check
+               // if fade_in_trim_handle or fade_out_trim_handle should
+               // be visible. -- If the fade_in_handle is visible
+               // we have focus and are not in internal edit mode.
+               entered(false);
+       }
 }
 
 void
@@ -1633,11 +1640,5 @@ AudioRegionView::parameter_changed (string const & p)
 {
        if (p == "show-waveforms") {
                setup_waveform_visibility ();
-       } else if (p == "waveform-scale") {
-               setup_waveform_scale ();
-       } else if (p == "waveform-shape") {
-               setup_waveform_shape ();
-       } else if (p == "show-waveform-clipping") {
-               setup_waveform_clipping ();
        }
 }
index 0a737cd6a170652325da67358aaee7ac5bafd03e..9c2c9f7c12aaaccf47d74aef3e1ae5bedd6d4180 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "canvas/fwd.h"
 #include "canvas/wave_view.h"
+#include "canvas/xfade_curve.h"
 
 #include "region_view.h"
 #include "time_axis_view_item.h"
@@ -50,17 +51,17 @@ class RouteTimeAxisView;
 class AudioRegionView : public RegionView
 {
   public:
-       AudioRegionView (ArdourCanvas::Group *,
+       AudioRegionView (ArdourCanvas::Container *,
                         RouteTimeAxisView&,
                         boost::shared_ptr<ARDOUR::AudioRegion>,
                         double initial_samples_per_pixel,
-                        Gdk::Color const & basic_color);
+                        uint32_t base_color);
 
-       AudioRegionView (ArdourCanvas::Group *,
+       AudioRegionView (ArdourCanvas::Container *,
                         RouteTimeAxisView&,
                         boost::shared_ptr<ARDOUR::AudioRegion>,
                         double samples_per_pixel,
-                        Gdk::Color const & basic_color,
+                        uint32_t base_color,
                         bool recording,
                         TimeAxisViewItem::Visibility);
 
@@ -68,7 +69,7 @@ class AudioRegionView : public RegionView
 
        ~AudioRegionView ();
 
-       virtual void init (Gdk::Color const & base_color, bool wait_for_data);
+       void init (bool wait_for_data);
 
        boost::shared_ptr<ARDOUR::AudioRegion> audio_region() const;
 
@@ -94,8 +95,8 @@ class AudioRegionView : public RegionView
 
        GhostRegion* add_ghost (TimeAxisView&);
 
-       void reset_fade_in_shape_width (boost::shared_ptr<ARDOUR::AudioRegion> ar, framecnt_t);
-       void reset_fade_out_shape_width (boost::shared_ptr<ARDOUR::AudioRegion> ar, framecnt_t);
+       void reset_fade_in_shape_width (boost::shared_ptr<ARDOUR::AudioRegion> ar, framecnt_t, bool drag_active = false);
+       void reset_fade_out_shape_width (boost::shared_ptr<ARDOUR::AudioRegion> ar, framecnt_t, bool drag_active = false);
 
        framepos_t get_fade_in_shape_width ();
        framepos_t get_fade_out_shape_width ();
@@ -157,23 +158,21 @@ class AudioRegionView : public RegionView
        ArdourCanvas::Polygon*          sync_mark; ///< polgyon for sync position
        ArdourCanvas::Rectangle*        fade_in_handle; ///< fade in handle, or 0
        ArdourCanvas::Rectangle*        fade_out_handle; ///< fade out handle, or 0
+       ArdourCanvas::Rectangle*        fade_in_trim_handle; ///< fade in trim handle, or 0
+       ArdourCanvas::Rectangle*        fade_out_trim_handle; ///< fade out trim handle, or 0
 
-       ArdourCanvas::Curve*     start_xfade_in;
-       ArdourCanvas::Curve*     start_xfade_out;
-       ArdourCanvas::Rectangle* start_xfade_rect;
+       ArdourCanvas::XFadeCurve* start_xfade_curve;
+       ArdourCanvas::Rectangle*  start_xfade_rect;
        bool _start_xfade_visible;
 
-       ArdourCanvas::Curve *end_xfade_in;
-       ArdourCanvas::Curve *end_xfade_out;
-       ArdourCanvas::Rectangle* end_xfade_rect;
+       ArdourCanvas::XFadeCurve* end_xfade_curve;
+       ArdourCanvas::Rectangle*  end_xfade_rect;
        bool _end_xfade_visible;
 
        boost::shared_ptr<AudioRegionGainLine> gain_line;
 
        double _amplitude_above_axis;
 
-       uint32_t fade_color;
-
        void reset_fade_shapes ();
        void reset_fade_in_shape ();
        void reset_fade_out_shape ();
@@ -192,8 +191,6 @@ class AudioRegionView : public RegionView
 
        void set_colors ();
         void set_waveform_colors ();
-        void set_one_waveform_color (ArdourCanvas::WaveView*);
-       void compute_colors (Gdk::Color const &);
        void reset_width_dependent_items (double pixel_width);
        void set_frame_color ();
 
@@ -208,9 +205,7 @@ private:
 
        void parameter_changed (std::string const &);
        void setup_waveform_visibility ();
-       void setup_waveform_shape ();
-       void setup_waveform_scale ();
-       void setup_waveform_clipping ();
+       void set_some_waveform_colors (std::vector<ArdourCanvas::WaveView*>& waves_to_color);
 
        /** A ScopedConnection for each PeaksReady callback (one per channel).  Each member
         *  may be 0 if no connection exists.
@@ -221,6 +216,9 @@ private:
         *  first list is for start xfades, second list is for end xfades.
         */
        std::pair<std::list<AudioRegionView*>, std::list<AudioRegionView*> > _hidden_xfades;
+
+       bool trim_fade_in_drag_active;
+       bool trim_fade_out_drag_active;
 };
 
 #endif /* __gtk_ardour_audio_region_view_h__ */
index 6880c8be5b9212205dd75b24425699ba081ca9af..bc5052c3907e8ac50e122fda00c16a740954383f 100644 (file)
@@ -46,7 +46,6 @@
 #include "ardour_ui.h"
 #include "rgb_macros.h"
 #include "gui_thread.h"
-#include "utils.h"
 
 #include "i18n.h"
 
@@ -110,7 +109,7 @@ AudioStreamView::create_region_view (boost::shared_ptr<Region> r, bool wait_for_
                break;
        case Destructive:
                region_view = new TapeAudioRegionView (_canvas_group, _trackview, region,
-                               _samples_per_pixel, region_color);
+                                                      _samples_per_pixel, region_color);
                break;
        default:
                fatal << string_compose (_("programming error: %1"), "illegal track mode in ::add_region_view_internal") << endmsg;
@@ -118,7 +117,7 @@ AudioStreamView::create_region_view (boost::shared_ptr<Region> r, bool wait_for_
 
        }
 
-       region_view->init (region_color, wait_for_waves);
+       region_view->init (wait_for_waves);
        region_view->set_amplitude_above_axis(_amplitude_above_axis);
        region_view->set_height (child_height ());
 
index e59c43fc8492e22e33d0f7373dab6c13d2a21e28..8c022abdb74e66cc2cd1f1259a61e7767cca1944 100644 (file)
@@ -60,6 +60,7 @@
 
 using namespace std;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtk;
 using namespace Editing;
@@ -83,7 +84,7 @@ AudioTimeAxisView::set_route (boost::shared_ptr<Route> rt)
 
        RouteTimeAxisView::set_route (rt);
 
-       _view->apply_color (color (), StreamView::RegionColor);
+       _view->apply_color (gdk_color_to_rgba (color()), StreamView::RegionColor);
 
        // Make sure things are sane...
        assert(!is_track() || is_audio_track());
index a80a4742327cf41eadd782287b83b0af455c9af9..a75faf747c2fe3751fb67b633084d74c681cad93 100644 (file)
@@ -28,7 +28,6 @@
 #include "ardour/session.h"
 
 #include "ardour_ui.h"
-#include "utils.h"
 #include "automation_controller.h"
 #include "gui_thread.h"
 
index 7e37c32eb734f3a37d22a0ad5e51f86a2623e7f4..1cfd7eeb48d4a7c657d27d0142d7b35bb9a53bf6 100644 (file)
@@ -39,6 +39,7 @@
 #include "ardour/automation_list.h"
 #include "ardour/dB.h"
 #include "ardour/debug.h"
+#include "ardour/tempo.h"
 
 #include "evoral/Curve.hpp"
 
@@ -50,7 +51,6 @@
 #include "rgb_macros.h"
 #include "ardour_ui.h"
 #include "public_editor.h"
-#include "utils.h"
 #include "selection.h"
 #include "time_axis_view.h"
 #include "point_selection.h"
@@ -69,7 +69,7 @@ using namespace Editing;
 /** @param converter A TimeConverter whose origin_b is the start time of the AutomationList in session frames.
  *  This will not be deleted by AutomationLine.
  */
-AutomationLine::AutomationLine (const string& name, TimeAxisView& tv, ArdourCanvas::Group& parent,
+AutomationLine::AutomationLine (const string& name, TimeAxisView& tv, ArdourCanvas::Item& parent,
                                 boost::shared_ptr<AutomationList> al,
                                 Evoral::TimeConverter<double, framepos_t>* converter)
        : trackview (tv)
@@ -98,7 +98,7 @@ AutomationLine::AutomationLine (const string& name, TimeAxisView& tv, ArdourCanv
        terminal_points_can_slide = true;
        _height = 0;
 
-       group = new ArdourCanvas::Group (&parent);
+       group = new ArdourCanvas::Container (&parent);
        CANVAS_DEBUG_NAME (group, "region gain envelope group");
 
        line = new ArdourCanvas::PolyLine (group);
@@ -520,11 +520,12 @@ AutomationLine::ContiguousControlPoints::ContiguousControlPoints (AutomationLine
 }
 
 void
-AutomationLine::ContiguousControlPoints::compute_x_bounds ()
+AutomationLine::ContiguousControlPoints::compute_x_bounds (PublicEditor& e)
 {
        uint32_t sz = size();
 
        if (sz > 0 && sz < line.npoints()) {
+               const TempoMap& map (e.session()->tempo_map());
 
                /* determine the limits on x-axis motion for this 
                   contiguous range of control points
@@ -532,14 +533,30 @@ AutomationLine::ContiguousControlPoints::compute_x_bounds ()
 
                if (front()->view_index() > 0) {
                        before_x = line.nth (front()->view_index() - 1)->get_x();
+
+                       const framepos_t pos = e.pixel_to_sample(before_x);
+                       const Meter& meter = map.meter_at (pos);
+                       const framecnt_t len = ceil (meter.frames_per_bar (map.tempo_at (pos), e.session()->frame_rate())
+                                       / (Timecode::BBT_Time::ticks_per_beat * meter.divisions_per_bar()) );
+                       const double one_tick_in_pixels = e.sample_to_pixel_unrounded (len);
+
+                       before_x += one_tick_in_pixels;
                }
 
                /* if our last point has a point after it in the line,
                   we have an "after" bound
                */
 
-               if (back()->view_index() < (line.npoints() - 2)) {
+               if (back()->view_index() < (line.npoints() - 1)) {
                        after_x = line.nth (back()->view_index() + 1)->get_x();
+
+                       const framepos_t pos = e.pixel_to_sample(after_x);
+                       const Meter& meter = map.meter_at (pos);
+                       const framecnt_t len = ceil (meter.frames_per_bar (map.tempo_at (pos), e.session()->frame_rate())
+                                       / (Timecode::BBT_Time::ticks_per_beat * meter.divisions_per_bar()));
+                       const double one_tick_in_pixels = e.sample_to_pixel_unrounded (len);
+
+                       after_x -= one_tick_in_pixels;
                }
        }
 }
@@ -643,7 +660,7 @@ AutomationLine::drag_motion (double const x, float fraction, bool ignore_x, bool
                }
 
                for (vector<CCP>::iterator ccp = contiguous_points.begin(); ccp != contiguous_points.end(); ++ccp) {
-                       (*ccp)->compute_x_bounds ();
+                       (*ccp)->compute_x_bounds (trackview.editor());
                }
        }       
 
index dcd81c8b4eb31a10b29d4e22780ed47111f10dad..663310dc46c9d339e6433c219606c2e05c13847f 100644 (file)
@@ -37,7 +37,7 @@
 #include "ardour/types.h"
 
 #include "canvas/types.h"
-#include "canvas/group.h"
+#include "canvas/container.h"
 #include "canvas/poly_line.h"
 
 class AutomationLine;
@@ -47,6 +47,7 @@ class TimeAxisView;
 class AutomationTimeAxisView;
 class Selectable;
 class Selection;
+class PublicEditor;
 
 
 /** A GUI representation of an ARDOUR::AutomationList */
@@ -59,7 +60,7 @@ public:
                SelectedControlPoints = 0x4
        };
        
-       AutomationLine (const std::string& name, TimeAxisView&, ArdourCanvas::Group&,
+       AutomationLine (const std::string& name, TimeAxisView&, ArdourCanvas::Item&,
                        boost::shared_ptr<ARDOUR::AutomationList>,
                        Evoral::TimeConverter<double, ARDOUR::framepos_t>* converter = 0);
        virtual ~AutomationLine ();
@@ -104,7 +105,7 @@ public:
 
        TimeAxisView& trackview;
 
-       ArdourCanvas::Group& canvas_group() const { return *group; }
+       ArdourCanvas::Container& canvas_group() const { return *group; }
        ArdourCanvas::Item&  parent_group() const { return _parent_group; }
        ArdourCanvas::Item&  grab_item() const { return *line; }
 
@@ -172,8 +173,8 @@ protected:
        /** true if we did a push at any point during the current drag */
        bool    did_push;
 
-       ArdourCanvas::Group&        _parent_group;
-       ArdourCanvas::Group*        group;
+       ArdourCanvas::Item&        _parent_group;
+       ArdourCanvas::Container*       group;
        ArdourCanvas::PolyLine*     line; /* line */
        ArdourCanvas::Points        line_points; /* coordinates for canvas line */
        std::vector<ControlPoint*>  control_points; /* visible control points */
@@ -183,7 +184,7 @@ public:
                ContiguousControlPoints (AutomationLine& al);
                double clamp_dx (double dx);
                void move (double dx, double dy);
-               void compute_x_bounds ();
+               void compute_x_bounds (PublicEditor& e);
 private:
                AutomationLine& line;
                double before_x;
index 86e7121677acbba2ef04ccf43ad606c50d4888f5..2da15c17c5085e75017dbddafdfcc7856edc663f 100644 (file)
 
 #include "i18n.h"
 
-AutomationRegionView::AutomationRegionView (ArdourCanvas::Group*                      parent,
+AutomationRegionView::AutomationRegionView (ArdourCanvas::Container*                      parent,
                                            AutomationTimeAxisView&                   time_axis,
                                            boost::shared_ptr<ARDOUR::Region>         region,
                                            const Evoral::Parameter&                  param,
                                            boost::shared_ptr<ARDOUR::AutomationList> list,
                                            double                                    spu,
-                                           Gdk::Color const &                        basic_color)
+                                           uint32_t                                  basic_color)
        : RegionView(parent, time_axis, region, spu, basic_color, true)
        , _parameter(param)
 {
@@ -63,13 +63,11 @@ AutomationRegionView::~AutomationRegionView ()
 }
 
 void
-AutomationRegionView::init (Gdk::Color const & basic_color, bool /*wfd*/)
+AutomationRegionView::init (bool /*wfd*/)
 {
        _enable_display = false;
 
-       RegionView::init(basic_color, false);
-
-       compute_colors (basic_color);
+       RegionView::init (false);
 
        reset_width_dependent_items ((double) _region->length() / samples_per_pixel);
 
index 726ce85d31fa09c782d6fa4f256adc4eba3d3f83..1dec06f0c63bc73ae269edc6ecfaee2cb443fde8 100644 (file)
@@ -37,17 +37,17 @@ class TimeAxisView;
 class AutomationRegionView : public RegionView
 {
 public:
-       AutomationRegionView(ArdourCanvas::Group*,
+       AutomationRegionView(ArdourCanvas::Container*,
                             AutomationTimeAxisView&,
                             boost::shared_ptr<ARDOUR::Region>,
                             const Evoral::Parameter& parameter,
                             boost::shared_ptr<ARDOUR::AutomationList>,
                             double initial_samples_per_pixel,
-                            Gdk::Color const & basic_color);
+                            uint32_t basic_color);
 
        ~AutomationRegionView();
 
-       void init (Gdk::Color const & basic_color, bool wfd);
+       void init (bool wfd);
 
        inline AutomationTimeAxisView* automation_view() const
                { return dynamic_cast<AutomationTimeAxisView*>(&trackview); }
index f54b95cbc5d40f5c09bdcd941749f7190d10542a..53c643c0270ff4990007fccc00f27b855ef64081 100644 (file)
@@ -40,7 +40,6 @@
 #include "ardour_ui.h"
 #include "rgb_macros.h"
 #include "gui_thread.h"
-#include "utils.h"
 
 using namespace std;
 using namespace ARDOUR;
@@ -113,7 +112,7 @@ AutomationStreamView::add_region_view_internal (boost::shared_ptr<Region> region
                _samples_per_pixel, region_color
                );
 
-       region_view->init (region_color, false);
+       region_view->init (false);
        region_views.push_front (region_view);
 
        /* follow global waveform setting */
index c6238e67111c9de0d9795e0d4d71362ab64b896d..3b00fc69b6cdb1e6c9597eb97b34984aa2b1ecac 100644 (file)
@@ -51,6 +51,7 @@
 
 using namespace std;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtk;
 using namespace Gtkmm2ext;
index 7a449843e09055a306e6b9148eb7a9a6e5752c7b..5e36fc43c1e5a7bc04a4dcdeb38b0cf899c04eb1 100644 (file)
@@ -43,6 +43,7 @@ using namespace std;
 using namespace Gtk;
 using namespace Gtkmm2ext;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 
 list<Gdk::Color> AxisView::used_colors;
 
@@ -65,7 +66,14 @@ AxisView::unique_random_color()
 string
 AxisView::gui_property (const string& property_name) const
 {
-       return gui_object_state().get_string (state_id(), property_name);
+       if (property_hashtable.count(property_name)) {
+               return property_hashtable[property_name];
+       } else {
+         string rv = gui_object_state().get_string (state_id(), property_name);
+               property_hashtable.erase(property_name);
+               property_hashtable.emplace(property_name, rv);
+               return rv;
+       }
 }
 
 bool
@@ -83,7 +91,6 @@ AxisView::set_marked_for_display (bool yn)
                set_gui_property ("visible", yn);
                return true; // things changed
        }
-
        return false;
 }
 
index 17f8c09a7bc18ffd00614296581b73351fc1744d..4ce76c92ce52b74c74135614acd40075879011cb 100644 (file)
@@ -21,6 +21,7 @@
 #define __ardour_gtk_axis_view_h__
 
 #include <list>
+#include <boost/unordered_map.hpp>
 
 #include <gtkmm/label.h>
 #include <gdkmm/color.h>
@@ -63,6 +64,10 @@ class AxisView : public virtual Selectable, public PBD::ScopedConnectionList, pu
        std::string gui_property (const std::string& property_name) const;
        
        template<typename T> void set_gui_property (const std::string& property_name, const T& value) {
+               std::stringstream s;
+               s << value;
+               property_hashtable.erase(property_name);
+               property_hashtable.emplace(property_name, s.str());
                gui_object_state().set_property<T> (state_id(), property_name, value);
        }
 
@@ -83,14 +88,13 @@ class AxisView : public virtual Selectable, public PBD::ScopedConnectionList, pu
         */
        static Gdk::Color unique_random_color();
 
-
        Gdk::Color _color;
 
        static std::list<Gdk::Color> used_colors;
 
        Gtk::Label name_label;
 
-       bool _marked_for_display;
+       mutable boost::unordered_map<std::string, std::string> property_hashtable;
        uint32_t _old_order_key;
 }; /* class AxisView */
 
index c67ae38074f9a8ca71e5787393c6f1682bb976ff..28b7b286ab075dedfe478f91c0f11bf72c913cda 100644 (file)
@@ -31,6 +31,7 @@
 
 using std::min;
 using std::string;
+using namespace ARDOUR_UI_UTILS;
 
 BigClockWindow::BigClockWindow (AudioClock& c) 
        : ArdourWindow (_("Big Clock"))
index 3b3094e7f3e2eb895daf0e6773dfc3f77251621e..d8c37e136702419ffbc141a13c60701431b84f47 100644 (file)
@@ -153,7 +153,7 @@ void load_custom_fonts()
 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
        std::string ardour_mono_file;
 
-       if (!find_file_in_search_path (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
+       if (!find_file (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
                cerr << _("Cannot find ArdourMono TrueType font") << endl;
        }
 
index 2956decef5774b4266e111c91f5af51511d02066..ad484aa7c7df1e92ce8c658fa117935b442eabea 100644 (file)
@@ -151,7 +151,7 @@ load_custom_fonts()
 {
        std::string ardour_mono_file;
 
-       if (!find_file_in_search_path (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
+       if (!find_file (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
                cerr << _("Cannot find ArdourMono TrueType font") << endl;
        }
 
index 6e565437280044431769306fb81a9bcb620e1116..5e2da98bd4467ce7d8fbe108f8926c63f4af3eb9 100644 (file)
@@ -238,7 +238,7 @@ string fonts_conf_file;
                fonts_conf_file += PROGRAM_NAME;
                fonts_conf_file += FONTS_CONF_LOCATION;
 #else
-       if (PBD::find_file_in_search_path (ARDOUR::ardour_config_search_path(), "fonts.conf", fonts_conf_file)) {
+       if (PBD::find_file (ARDOUR::ardour_config_search_path(), "fonts.conf", fonts_conf_file)) {
 #endif
                Glib::setenv ("FONTCONFIG_FILE", fonts_conf_file, true);
 
@@ -271,7 +271,7 @@ string pango_modules_file;
 #endif
                pango_modules_path.resize (pango_modules_path.size()-14); // Remove "/pango.modules" from the end
 #else
-       if (PBD::find_file_in_search_path (ARDOUR::ardour_config_search_path(), "pango.modules", pango_modules_file)) {
+       if (PBD::find_file (ARDOUR::ardour_config_search_path(), "pango.modules", pango_modules_file)) {
 
                Glib::ustring pango_modules_path = pango_modules_file;
                pango_modules_path.resize (pango_modules_path.size()-14); // Remove "/pango.modules" from the end
@@ -301,7 +301,7 @@ string gdk_pixbuf_loaders_file;
                gdk_pixbuf_loaders_file += PROGRAM_NAME;
                gdk_pixbuf_loaders_file += PIXBUFLOADERS_CONF_LOCATION;
 #else
-       if (PBD::find_file_in_search_path (ARDOUR::ardour_config_search_path(), "gdk-pixbuf.loaders", gdk_pixbuf_loaders_file)) {
+       if (PBD::find_file (ARDOUR::ardour_config_search_path(), "gdk-pixbuf.loaders", gdk_pixbuf_loaders_file)) {
 #endif
                // Set an environment variable so we can find our pixbuf modules.
                Glib::setenv ("GDK_PIXBUF_MODULE_FILE", Glib::filename_from_utf8(gdk_pixbuf_loaders_file), true);
@@ -327,7 +327,7 @@ string clearlooks_la_file;
                clearlooks_la_file += PROGRAM_NAME;
                clearlooks_la_file += CLEARLOOKS_CONF_LOCATION;
 #else
-       if (PBD::find_file_in_search_path (ARDOUR::ardour_config_search_path(), "libclearlooks.la", clearlooks_la_file)) {
+       if (PBD::find_file (ARDOUR::ardour_config_search_path(), "libclearlooks.la", clearlooks_la_file)) {
 #endif
                // Set an environment variable so we can find our clearlooks engine.
                // Note that this requires a modified version of libgtk (gtkthemes.c)
@@ -485,7 +485,7 @@ void load_custom_fonts()
 {
        std::string ardour_mono_file;
 
-       if (!find_file_in_search_path (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
+       if (!find_file (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
                cerr << _("Cannot find ArdourMono TrueType font") << endl;
        }
 
index 035046c10b6b4eb4313ce5395f50d7ed888b00a2..6dc34d33884c74336de12dca817a773465540621 100644 (file)
@@ -34,6 +34,7 @@
 
 using namespace std;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 
 BundleEditorMatrix::BundleEditorMatrix (Gtk::Window* parent, Session* session, boost::shared_ptr<Bundle> bundle)
        : PortMatrix (parent, session, DataType::NIL)
index 3bcbd7d8fa02f1796baf98aea98b179bcc60393a..4277149a35a608afeeedb0f80e0c581dc31b00e0 100644 (file)
@@ -52,6 +52,7 @@ CANVAS_VARIABLE(canvasvar_GhostTrackZeroLine, "ghost track zero line")
 CANVAS_VARIABLE(canvasvar_ImageTrack, "image track")
 CANVAS_VARIABLE(canvasvar_InactiveCrossfade, "inactive crossfade")
 CANVAS_VARIABLE(canvasvar_InactiveFadeHandle, "inactive fade handle")
+CANVAS_VARIABLE(canvasvar_InactiveGroupTab, "inactive group tab")
 CANVAS_VARIABLE(canvasvar_LocationCDMarker, "location cd marker")
 CANVAS_VARIABLE(canvasvar_LocationLoop, "location loop")
 CANVAS_VARIABLE(canvasvar_LocationMarker, "location marker")
@@ -132,6 +133,8 @@ CANVAS_VARIABLE(canvasvar_RecordingRect, "recording rect")
 CANVAS_VARIABLE(canvasvar_RecWaveFormFill, "recorded waveform fill")
 CANVAS_VARIABLE(canvasvar_RecWaveForm, "recorded waveform outline")
 CANVAS_VARIABLE(canvasvar_RubberBandRect, "rubber band rect")
+CANVAS_VARIABLE(canvasvar_RulerBase, "ruler base")
+CANVAS_VARIABLE(canvasvar_RulerText, "ruler text")
 CANVAS_VARIABLE(canvasvar_SelectedCrossfadeEditorLine, "selected crossfade editor line")
 CANVAS_VARIABLE(canvasvar_SelectedCrossfadeEditorWave, "selected crossfade editor wave")
 CANVAS_VARIABLE(canvasvar_SelectedFrameBase, "selected region base")
@@ -167,6 +170,9 @@ CANVAS_VARIABLE(canvasvar_TimeAxisFrame, "time axis frame")
 CANVAS_VARIABLE(canvasvar_SelectedTimeAxisFrame, "selected time axis frame")
 CANVAS_VARIABLE(canvasvar_TimeStretchFill, "time stretch fill")
 CANVAS_VARIABLE(canvasvar_TimeStretchOutline, "time stretch outline")
+CANVAS_VARIABLE(canvasvar_TrackNumberLabelFillStart, "tracknumber label: fill start")
+CANVAS_VARIABLE(canvasvar_TrackNumberLabelFillEnd, "tracknumber label: fill end")
+CANVAS_VARIABLE(canvasvar_TrackNumberLabelText, "tracknumber label: text")
 CANVAS_VARIABLE(canvasvar_TransportDragRect, "transport drag rect")
 CANVAS_VARIABLE(canvasvar_TransportLoopRect, "transport loop rect")
 CANVAS_VARIABLE(canvasvar_TransportMarkerBar, "transport marker bar")
@@ -203,6 +209,7 @@ BUTTON_VARS(ProcessorControlButton, "processor control button")
 BUTTON_VARS(MonitorButton, "monitor button")
 BUTTON_VARS(SoloIsolateButton, "solo isolate")
 BUTTON_VARS(SoloSafeButton, "solo safe")
+BUTTON_VARS(MidiDeviceButton, "midi device")
 BUTTON_VARS(MonitorSectionCutButton, "monitor section cut")
 BUTTON_VARS(MonitorSectionDimButton, "monitor section dim")
 BUTTON_VARS(MonitorSectionSoloButton, "monitor section solo")
@@ -234,6 +241,7 @@ BUTTON_VARS(RouteButton, "route button")
 BUTTON_VARS(MixerStripButton, "mixer strip button")
 BUTTON_VARS(MixerStripNameButton, "mixer strip name button")
 BUTTON_VARS(MidiInputButton, "midi input button")
+BUTTON_VARS(LockButton, "lock button")
 BUTTON_VARS(GenericButton, "generic button")
 
 #define CLOCK_VARS(root,name) \
@@ -252,3 +260,44 @@ CLOCK_VARS(PunchClock, "punch clock")
 CLOCK_VARS(SelectionClock, "selection clock")
 CLOCK_VARS(NudgeClock, "nudge clock")
 CLOCK_VARS(GenericClock, "clock")
+CANVAS_FONT_VARIABLE(canvasvar_SmallFont, "small font")
+CANVAS_FONT_VARIABLE(canvasvar_SmallerFont, "smaller font")
+CANVAS_FONT_VARIABLE(canvasvar_NormalFont, "normal font")
+CANVAS_FONT_VARIABLE(canvasvar_BigFont, "big font")
+CANVAS_FONT_VARIABLE(canvasvar_LargeFont, "large font")
+CANVAS_FONT_VARIABLE(canvasvar_LargerFont, "larger font")
+CANVAS_FONT_VARIABLE(canvasvar_HugerFont, "huger font")
+CANVAS_FONT_VARIABLE(canvasvar_MassiveFont, "massive font")
+CANVAS_FONT_VARIABLE(canvasvar_SmallBoldFont, "small bold font")
+CANVAS_FONT_VARIABLE(canvasvar_SmallerBoldFont, "smaller bold font")
+CANVAS_FONT_VARIABLE(canvasvar_NormalBoldFont, "normal bold font")
+CANVAS_FONT_VARIABLE(canvasvar_BigBoldFont, "big bold font")
+CANVAS_FONT_VARIABLE(canvasvar_LargeBoldFont, "large bold font")
+CANVAS_FONT_VARIABLE(canvasvar_LargerBoldFont, "larger bold font")
+CANVAS_FONT_VARIABLE(canvasvar_HugerBoldFont, "huger bold font")
+CANVAS_FONT_VARIABLE(canvasvar_MassiveBoldFont, "massive bold font")
+CANVAS_FONT_VARIABLE(canvasvar_SmallItalicFont, "small italic font")
+CANVAS_FONT_VARIABLE(canvasvar_SmallerItalicFont, "smaller italic font")
+CANVAS_FONT_VARIABLE(canvasvar_NormalItalicFont, "normal italic font")
+CANVAS_FONT_VARIABLE(canvasvar_BigItalicFont, "big italic font")
+CANVAS_FONT_VARIABLE(canvasvar_LargeItalicFont, "large italic font")
+CANVAS_FONT_VARIABLE(canvasvar_LargerItalicFont, "larger italic font")
+CANVAS_FONT_VARIABLE(canvasvar_HugerItalicFont, "huger italic font")
+CANVAS_FONT_VARIABLE(canvasvar_MassiveItalicFont, "massive italic font")
+CANVAS_FONT_VARIABLE(canvasvar_SmallMonospaceFont, "small monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_SmallerMonospaceFont, "smaller monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_NormalMonospaceFont, "normal monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_BigMonospaceFont, "big monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_LargeMonospaceFont, "large monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_LargerMonospaceFont, "larger monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_HugerMonospaceFont, "huger monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_MassiveMonospaceFont, "massive monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_SmallBoldMonospaceFont, "small bold monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_SmallerBoldMonospaceFont, "smaller bold monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_NormalBoldMonospaceFont, "normal bold monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_BigBoldMonospaceFont, "big bold monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_LargeBoldMonospaceFont, "large bold monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_LargerBoldMonospaceFont, "larger bold monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_HugerBoldMonospaceFont, "huger bold monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_MassiveBoldMonospaceFont, "massive bold monospace font")
+
index 9b588d1babadc97b530dcee308dbf5c7783a44a4..90761d77c814691a584a4f04bf6daf2e3b2ff31c 100644 (file)
@@ -167,8 +167,8 @@ ControlPoint::move_to (double x, double y, ShapeType shape)
        _shape = shape;
 }
 
-void
-ControlPoint::i2w (double& x, double& y) const
+ArdourCanvas::Item&
+ControlPoint::item() const 
 {
-       _item->item_to_canvas (x, y);
+       return *_item;
 }
index ce4c30be968aa0506009972f2eed4ac880b7675f..045f1241edc2b415b94764f5eb108d925641f0f1 100644 (file)
@@ -38,6 +38,7 @@ class Selection;
 namespace ArdourCanvas {
        class Rectangle;
        class Diamond;
+       class Item;
 }
 
 class ControlPoint : public Selectable
@@ -74,7 +75,7 @@ class ControlPoint : public Selectable
        uint32_t view_index() const         { return _view_index; }
        void     set_view_index(uint32_t i) { _view_index = i; }
 
-       void i2w (double &, double &) const;
+       ArdourCanvas::Item& item() const;
 
        ARDOUR::AutomationList::iterator model() const { return _model; }
        AutomationLine&                  line()  const { return _line; }
index c545a02b464451fe1747be91f8ac818e2ddf9470..02d3ceb4bb77e6df8555ddec776a755ed9f0d268 100644 (file)
@@ -49,7 +49,6 @@
 #include "crossfade_edit.h"
 #include "rgb_macros.h"
 #include "keyboard.h"
-#include "utils.h"
 #include "gui_thread.h"
 #include "actions.h"
 
index 960bcc2d26258422d9910efe72bc28d6226af23e..a042611cc9172d45caad943087e5c7140afcbd15 100644 (file)
@@ -36,7 +36,7 @@ namespace ArdourCanvas {
 class CrossfadeView : public TimeAxisViewItem
 {
 public:
-       CrossfadeView (ArdourCanvas::Group*,
+       CrossfadeView (ArdourCanvas::Container*,
                       RouteTimeAxisView&,
                       boost::shared_ptr<ARDOUR::Crossfade>,
                       double initial_samples_per_pixel,
index 87a70102a8ba91af019c713aa9427e86a2a628d6..6f8db99da0ea00c1dca7f8acfa12de4ac48c92c2 100644 (file)
@@ -45,6 +45,7 @@
 #include "pbd/unknown_type.h"
 #include "pbd/unwind.h"
 #include "pbd/stacktrace.h"
+#include "pbd/timersub.h"
 
 #include <glibmm/miscutils.h>
 #include <glibmm/uriutils.h>
 #include "tempo_lines.h"
 #include "time_axis_view.h"
 #include "utils.h"
+#include "verbose_cursor.h"
 
 #include "i18n.h"
 
 using namespace std;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtk;
 using namespace Glib;
@@ -301,6 +304,8 @@ Editor::Editor ()
        last_update_frame = 0;
         pre_press_cursor = 0;
        _drags = new DragManager (this);
+       lock_dialog = 0;
+       ruler_dialog = 0;
        current_mixer_strip = 0;
        tempo_lines = 0;
 
@@ -469,7 +474,6 @@ Editor::Editor ()
        transport_mark_label.hide();
        transport_mark_label.set_no_show_all();
 
-       initialize_rulers ();
        initialize_canvas ();
 
        _summary = new EditorSummary (this);
@@ -495,13 +499,13 @@ Editor::Editor ()
        controls_layout.add (*h);
 
        controls_layout.set_name ("EditControlsBase");
-       controls_layout.add_events (Gdk::SCROLL_MASK);
-       controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
-
-       controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
+       controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK|Gdk::SCROLL_MASK);
        controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
+       controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
 
        _cursors = new MouseCursors;
+       _cursors->set_cursor_set (ARDOUR_UI::config()->get_icon_set());
+       cerr << "Set cursor set to " << ARDOUR_UI::config()->get_icon_set() << endl;
 
        ArdourCanvas::GtkCanvas* time_pad = manage (new ArdourCanvas::GtkCanvas ());
 
@@ -513,38 +517,22 @@ Editor::Editor ()
        // CAIROCANVAS
        time_pad->show();
 
-       time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
-       time_canvas_vbox.set_size_request (-1, -1);
-
-       ruler_label_event_box.add (ruler_label_vbox);
-       ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
-       ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
-
-       time_bars_event_box.add (time_bars_vbox);
-       time_bars_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
-       time_bars_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
-
-       time_canvas_event_box.add (time_canvas_vbox);
-       time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
-
        edit_packer.set_col_spacings (0);
        edit_packer.set_row_spacings (0);
        edit_packer.set_homogeneous (false);
        edit_packer.set_border_width (0);
        edit_packer.set_name ("EditorWindow");
 
-       /* labels for the rulers */
-       edit_packer.attach (ruler_label_event_box,   1, 2, 0, 1,    FILL,        SHRINK, 0, 0);
-       /* labels for the marker "tracks" (time bars) */
-       edit_packer.attach (time_bars_event_box,     1, 2, 1, 2,    FILL,        SHRINK, 0, 0);
-       /* the rulers */
-       edit_packer.attach (time_canvas_event_box,   2, 3, 0, 1,    FILL|EXPAND, FILL, 0, 0);
+       time_bars_event_box.add (time_bars_vbox);
+       time_bars_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
+       time_bars_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
+
+       /* labels for the time bars */
+       edit_packer.attach (time_bars_event_box,     0, 1, 0, 1,    FILL,        SHRINK, 0, 0);
        /* track controls */
-       edit_packer.attach (controls_layout,         0, 2, 2, 3,    FILL,        FILL|EXPAND, 0, 0);
-       /* time bars canvas */
-       edit_packer.attach (*_time_bars_canvas_viewport, 2, 3, 1, 2,    FILL,    FILL, 0, 0);
-       /* track canvas */
-       edit_packer.attach (*_track_canvas_viewport,  2, 3, 2, 3,    FILL|EXPAND, FILL|EXPAND, 0, 0);
+       edit_packer.attach (controls_layout,         0, 1, 1, 2,    FILL,        FILL|EXPAND, 0, 0);
+       /* canvas */
+       edit_packer.attach (*_track_canvas_viewport,  1, 2, 0, 2,    FILL|EXPAND, FILL|EXPAND, 0, 0);
 
        bottom_hbox.set_border_width (2);
        bottom_hbox.set_spacing (3);
@@ -751,6 +739,7 @@ Editor::Editor ()
        Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
 
        Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Editor::parameter_changed, this, _1), gui_context());
+       ARDOUR_UI::config()->ParameterChanged.connect (sigc::mem_fun (*this, &Editor::ui_parameter_changed));
 
        TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
 
@@ -759,7 +748,6 @@ Editor::Editor ()
        _popup_region_menu_item = 0;
 
        _show_marker_lines = false;
-       _over_region_trim_target = false;
 
         /* Button bindings */
 
@@ -773,9 +761,14 @@ Editor::Editor ()
         }
 
        constructed = true;
-       instant_save ();
+
+       /* grab current parameter state */
+       boost::function<void (string)> pc (boost::bind (&Editor::ui_parameter_changed, this, _1));
+       ARDOUR_UI::config()->map_parameters (pc);
 
        setup_fade_images ();
+
+       instant_save ();
 }
 
 Editor::~Editor()
@@ -783,7 +776,6 @@ Editor::~Editor()
         delete button_bindings;
        delete _routes;
        delete _route_groups;
-       delete _time_bars_canvas_viewport;
        delete _track_canvas_viewport;
        delete _drags;
 }
@@ -811,7 +803,7 @@ Editor::add_toplevel_controls (Container& cont)
 bool
 Editor::get_smart_mode () const
 {
-       return ( (current_mouse_mode() == Editing::MouseObject) && smart_mode_action->get_active() );
+       return ((current_mouse_mode() == Editing::MouseObject) && smart_mode_action->get_active());
 }
 
 void
@@ -836,8 +828,6 @@ Editor::catch_vanishing_regionview (RegionView *rv)
        if (!_all_region_actions_sensitized) {
                sensitize_all_region_actions (true);
        }
-
-       _over_region_trim_target = false;
 }
 
 void
@@ -851,7 +841,9 @@ Editor::set_entered_regionview (RegionView* rv)
                entered_regionview->exited ();
        }
 
-       if ((entered_regionview = rv) != 0) {
+       entered_regionview = rv;
+
+       if (entered_regionview  != 0) {
                entered_regionview->entered (internal_editing ());
        }
 
@@ -870,7 +862,9 @@ Editor::set_entered_track (TimeAxisView* tav)
                entered_track->exited ();
        }
 
-       if ((entered_track = tav) != 0) {
+       entered_track = tav;
+
+       if (entered_track) {
                entered_track->entered ();
        }
 }
@@ -1123,6 +1117,60 @@ Editor::on_realize ()
 {
        Window::on_realize ();
        Realized ();
+
+       if (ARDOUR_UI::config()->get_lock_gui_after_seconds()) {
+               start_lock_event_timing ();
+       }
+
+       signal_event().connect (sigc::mem_fun (*this, &Editor::generic_event_handler));
+}
+
+void
+Editor::start_lock_event_timing ()
+{
+       /* check if we should lock the GUI every 30 seconds */
+
+       Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::lock_timeout_callback), 30 * 1000);
+}
+
+bool
+Editor::generic_event_handler (GdkEvent* ev)
+{
+       switch (ev->type) {
+       case GDK_BUTTON_PRESS:
+       case GDK_BUTTON_RELEASE:
+       case GDK_MOTION_NOTIFY:
+       case GDK_KEY_PRESS:
+       case GDK_KEY_RELEASE:
+               gettimeofday (&last_event_time, 0);
+               break;
+       default:
+               break;
+       }
+       return false;
+}
+
+bool
+Editor::lock_timeout_callback ()
+{
+       struct timeval now, delta;
+
+       gettimeofday (&now, 0);
+
+       timersub (&now, &last_event_time, &delta);
+
+       if (delta.tv_sec > ARDOUR_UI::config()->get_lock_gui_after_seconds()) {
+               lock ();
+               /* don't call again. Returning false will effectively
+                  disconnect us from the timer callback.
+
+                  unlock() will call start_lock_event_timing() to get things
+                  started again.
+               */
+               return false;
+       }
+
+       return true;
 }
 
 void
@@ -1401,185 +1449,48 @@ Editor::fill_xfade_menu (Menu_Helpers::MenuList& items, bool start)
 
 /** Pop up a context menu for when the user clicks on a start crossfade */
 void
-Editor::popup_xfade_in_context_menu (int button, int32_t time, ArdourCanvas::Item* /*item*/, ItemType /*item_type*/)
+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);
 
        MenuList& items (xfade_in_context_menu.items());
+       items.clear ();
 
-       if (items.empty()) {
-               fill_xfade_menu (items, true);
+       if (arv->audio_region()->fade_in_active()) {
+               items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
+       } else {
+               items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
        }
 
+       items.push_back (SeparatorElem());
+       fill_xfade_menu (items, true);
+
        xfade_in_context_menu.popup (button, time);
 }
 
 /** Pop up a context menu for when the user clicks on an end crossfade */
 void
-Editor::popup_xfade_out_context_menu (int button, int32_t time, ArdourCanvas::Item* /*item*/, ItemType /*item_type*/)
-{
-       using namespace Menu_Helpers;
-
-       MenuList& items (xfade_out_context_menu.items());
-
-       if (items.empty()) {
-               fill_xfade_menu (items, false);
-       }
-
-       xfade_out_context_menu.popup (button, time);
-}
-
-
-/** Pop up a context menu for when the user clicks on a fade in or fade out */
-void
-Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
+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);
 
-       if (arv == 0) {
-               fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
-               /*NOTREACHED*/
-       }
-
-       MenuList& items (fade_context_menu.items());
+       MenuList& items (xfade_out_context_menu.items());
        items.clear ();
 
-       switch (item_type) {
-       case FadeInItem:
-       case FadeInHandleItem:
-               if (arv->audio_region()->fade_in_active()) {
-                       items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
-               } else {
-                       items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
-               }
-               
-               items.push_back (SeparatorElem());
-               
-               if (Profile->get_sae()) {
-                       
-                       items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
-                       items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
-                       
-               } else {
-                       
-                       items.push_back (
-                               ImageMenuElem (
-                                       _("Linear"),
-                                       *_fade_in_images[FadeLinear],
-                                       sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
-                                       )
-                               );
-                               
-                       dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
-                               
-                       items.push_back (
-                               ImageMenuElem (
-                                       _("Slow"),
-                                       *_fade_in_images[FadeSlow],
-                                       sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
-                                       ));
-                               
-                       dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
-                               
-                       items.push_back (
-                               ImageMenuElem (
-                                       _("Fast"),
-                                       *_fade_in_images[FadeFast],
-                                       sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
-                                       ));
-                               
-                       dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
-                               
-                       items.push_back (
-                               ImageMenuElem (
-                                       _("Symmetric"),
-                                       *_fade_in_images[FadeSymmetric],
-                                       sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSymmetric)
-                                       ));
-                               
-                       items.push_back (
-                               ImageMenuElem (
-                                       _("Constant power"),
-                                       *_fade_in_images[FadeConstantPower],
-                                       sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeConstantPower)
-                                       ));
-
-                       dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
-               }
-
-               break;
-
-       case FadeOutItem:
-       case FadeOutHandleItem:
-               if (arv->audio_region()->fade_out_active()) {
-                       items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
-               } else {
-                       items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
-               }
-
-               items.push_back (SeparatorElem());
-
-               if (Profile->get_sae()) {
-                       items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
-                       items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
-               } else {
-
-                       items.push_back (
-                               ImageMenuElem (
-                                       _("Linear"),
-                                       *_fade_out_images[FadeLinear],
-                                       sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
-                                       )
-                               );
-
-                       dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
-
-                       items.push_back (
-                               ImageMenuElem (
-                                       _("Slow"),
-                                       *_fade_out_images[FadeSlow],
-                                       sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
-                                       ));
-
-                       dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
-
-                       items.push_back (
-                               ImageMenuElem (
-                                       _("Fast"),
-                                       *_fade_out_images[FadeFast],
-                                       sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
-                                       ));
-
-                       dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
-
-                       items.push_back (
-                               ImageMenuElem (
-                                       _("Symmetric"),
-                                       *_fade_out_images[FadeSymmetric],
-                                       sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSymmetric)
-                                       ));
-
-                       items.push_back (
-                               ImageMenuElem (
-                                       _("Constant power"),
-                                       *_fade_out_images[FadeConstantPower],
-                                       sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeConstantPower)
-                                       ));
-
-                       dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
-               }
-
-               break;
-
-       default:
-               fatal << _("programming error: ")
-                     << X_("non-fade canvas item passed to popup_fade_context_menu()")
-                     << endmsg;
-               /*NOTREACHED*/
+       if (arv->audio_region()->fade_out_active()) {
+               items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
+       } else {
+               items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
        }
 
-       fade_context_menu.popup (button, time);
+       items.push_back (SeparatorElem());
+       fill_xfade_menu (items, false);
+
+       xfade_out_context_menu.popup (button, time);
 }
 
 void
@@ -2164,7 +2075,7 @@ Editor::set_edit_point_preference (EditPoint ep, bool force)
                edit_point_selector.set_text (str);
        }
 
-       set_canvas_cursor ();
+       reset_canvas_cursor ();
 
        if (!force && !changed) {
                return;
@@ -2264,7 +2175,7 @@ Editor::set_state (const XMLNode& node, int /*version*/)
        }
 
        if ((prop = node.property ("zoom-focus"))) {
-               set_zoom_focus ((ZoomFocus) string_2_enum (prop->value(), zoom_focus));
+               zoom_focus_selection_done ((ZoomFocus) string_2_enum (prop->value(), zoom_focus));
        }
 
        if ((prop = node.property ("zoom"))) {
@@ -2280,11 +2191,11 @@ Editor::set_state (const XMLNode& node, int /*version*/)
        }
 
        if ((prop = node.property ("snap-to"))) {
-               set_snap_to ((SnapType) string_2_enum (prop->value(), _snap_type));
+               snap_type_selection_done ((SnapType) string_2_enum (prop->value(), _snap_type));
        }
 
        if ((prop = node.property ("snap-mode"))) {
-               set_snap_mode ((SnapMode) string_2_enum (prop->value(), _snap_mode));
+               snap_mode_selection_done((SnapMode) string_2_enum (prop->value(), _snap_mode));
        }
 
        if ((prop = node.property ("internal-snap-to"))) {
@@ -2561,19 +2472,29 @@ Editor::get_state ()
        return *node;
 }
 
-
-
-/** @param y y offset from the top of all trackviews.
+/** if @param trackview_relative_offset is true, @param y y is an offset into the trackview area, in pixel units
+ *  if @param trackview_relative_offset is false, @param y y is a global canvas *  coordinate, in pixel units
+ *
  *  @return pair: TimeAxisView that y is over, layer index.
+ *
  *  TimeAxisView may be 0.  Layer index is the layer number if the TimeAxisView is valid and is
  *  in stacked or expanded region display mode, otherwise 0.
  */
 std::pair<TimeAxisView *, double>
-Editor::trackview_by_y_position (double y)
+Editor::trackview_by_y_position (double y, bool trackview_relative_offset) const
 {
-       for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
+       if (!trackview_relative_offset) {
+               y -= _trackview_group->canvas_origin().y;
+       }
 
+       if (y < 0) {
+               return std::make_pair ( (TimeAxisView *) 0, 0);
+       }
+
+       for (TrackViewList::const_iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
+                       
                std::pair<TimeAxisView*, double> const r = (*iter)->covers_y_position (y);
+                       
                if (r.first) {
                        return r;
                }
@@ -4004,26 +3925,14 @@ Editor::transport_punch_location()
 bool
 Editor::control_layout_scroll (GdkEventScroll* ev)
 {
-       if (Keyboard::some_magic_widget_has_focus()) {
-               return false;
-       }
-
-       switch (ev->direction) {
-       case GDK_SCROLL_UP:
-               scroll_tracks_up_line ();
-               return true;
-               break;
-
-       case GDK_SCROLL_DOWN:
-               scroll_tracks_down_line ();
-               return true;
-
-       default:
-               /* no left/right handling yet */
-               break;
-       }
+       /* Just forward to the normal canvas scroll method. The coordinate
+          systems are different but since the canvas is always larger than the
+          track headers, and aligned with the trackview area, this will work.
 
-       return false;
+          In the not too distant future this layout is going away anyway and
+          headers will be on the canvas.
+       */
+       return canvas_scroll_event (ev, false);
 }
 
 void
@@ -4261,8 +4170,7 @@ void
 Editor::use_visual_state (VisualState& vs)
 {
        PBD::Unwinder<bool> nsv (no_save_visual, true);
-
-       _routes->suspend_redisplay ();
+       DisplaySuspender ds;
 
        vertical_adjustment.set_value (vs.y_position);
 
@@ -4278,7 +4186,6 @@ Editor::use_visual_state (VisualState& vs)
        }
 
        _routes->update_visibility ();
-       _routes->resume_redisplay ();
 }
 
 /** This is the core function that controls the zoom level of the canvas. It is called
@@ -4315,10 +4222,6 @@ Editor::set_samples_per_pixel (framecnt_t spp)
 
        ArdourCanvas::GtkCanvasViewport* c;
 
-       c = get_time_bars_canvas();
-       if (c) {
-               c->canvas()->zoomed ();
-       }
        c = get_track_canvas();
        if (c) {
                c->canvas()->zoomed ();
@@ -4860,16 +4763,16 @@ Editor::located ()
 void
 Editor::region_view_added (RegionView *)
 {
-       _summary->set_dirty ();
+       _summary->set_background_dirty ();
 }
 
 void
 Editor::region_view_removed ()
 {
-       _summary->set_dirty ();
+       _summary->set_background_dirty ();
 }
 
-TimeAxisView*
+RouteTimeAxisView*
 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
 {
        TrackViewList::const_iterator j = track_views.begin ();
@@ -4900,6 +4803,22 @@ Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
        return t;
 }
 
+void
+Editor::suspend_route_redisplay ()
+{
+       if (_routes) {
+               _routes->suspend_redisplay();
+       }
+}
+
+void
+Editor::resume_route_redisplay ()
+{
+       if (_routes) {
+               _routes->resume_redisplay();
+       }
+}
+
 void
 Editor::add_routes (RouteList& routes)
 {
@@ -4957,6 +4876,10 @@ Editor::add_routes (RouteList& routes)
 void
 Editor::timeaxisview_deleted (TimeAxisView *tv)
 {
+       if (tv == entered_track) {
+               entered_track = 0;
+       }
+
        if (_session && _session->deletion_in_progress()) {
                /* the situation is under control */
                return;
@@ -4968,10 +4891,6 @@ Editor::timeaxisview_deleted (TimeAxisView *tv)
 
        _routes->route_removed (tv);
 
-       if (tv == entered_track) {
-               entered_track = 0;
-       }
-
        TimeAxisView::Children c = tv->get_child_list ();
        for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
                if (entered_track == i->get()) {
@@ -5481,28 +5400,28 @@ void
 Editor::setup_fade_images ()
 {
        _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadein-linear")));
-       _fade_in_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadein-short-cut")));
-       _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadein-slow-cut")));
-       _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadein-fast-cut")));
-       _fade_in_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadein-long-cut")));
+       _fade_in_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadein-symmetric")));
+       _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadein-fast-cut")));
+       _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadein-slow-cut")));
+       _fade_in_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadein-constant-power")));
 
        _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadeout-linear")));
-       _fade_out_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-short-cut")));
-       _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut")));
-       _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut")));
-       _fade_out_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-long-cut")));
+       _fade_out_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-symmetric")));
+       _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut")));
+       _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut")));
+       _fade_out_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-constant-power")));
        
-       _xfade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadeout-linear")));
-       _xfade_in_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-short-cut")));
-       _xfade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut")));
-       _xfade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut")));
-       _xfade_in_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-long-cut")));
+       _xfade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadein-linear")));
+       _xfade_in_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadein-symmetric")));
+       _xfade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadein-fast-cut")));
+       _xfade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadein-slow-cut")));
+       _xfade_in_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadein-constant-power")));
 
        _xfade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadeout-linear")));
-       _xfade_out_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-short-cut")));
-       _xfade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut")));
-       _xfade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut")));
-       _xfade_out_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-long-cut")));
+       _xfade_out_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-symmetric")));
+       _xfade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut")));
+       _xfade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut")));
+       _xfade_out_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-constant-power")));
 
 }
 
@@ -5580,3 +5499,18 @@ Editor::zoom_vertical_modifier_released()
 {
        _stepping_axis_view = 0;
 }
+
+void
+Editor::ui_parameter_changed (string parameter)
+{
+       if (parameter == "icon-set") {
+               while (!_cursor_stack.empty()) {
+                       _cursor_stack.pop();
+               }
+               _cursors->set_cursor_set (ARDOUR_UI::config()->get_icon_set());
+       } else if (parameter == "draggable-playhead") {
+               if (_verbose_cursor) {
+                       playhead_cursor->set_sensitive (ARDOUR_UI::config()->get_draggable_playhead());
+               }
+       }
+}
index e0c642b4fce741f40fcf232797e173dfcb9df8bf..d6e38cee417d05298b01cb60964277e00be4a980 100644 (file)
@@ -23,6 +23,7 @@
 #include <list>
 #include <map>
 #include <set>
+#include <stack>
 #include <string>
 #include <sys/time.h>
 #include <cmath>
@@ -47,8 +48,8 @@
 #include "ardour/types.h"
 
 #include "canvas/fwd.h"
+#include "canvas/ruler.h"
 
-#include "gtk-custom-ruler.h"
 #include "ardour_button.h"
 #include "ardour_dialog.h"
 #include "ardour_dropdown.h"
@@ -116,6 +117,7 @@ class PlaylistSelector;
 class PluginSelector;
 class ProgressReporter;
 class RhythmFerret;
+class RulerDialog;
 class Selection;
 class SoundFileOmega;
 class StreamView;
@@ -178,7 +180,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        void set_internal_edit (bool yn);
        bool toggle_internal_editing_from_double_click (GdkEvent*);
 
-        void _ensure_time_axis_view_is_visible (const TimeAxisView& tav, bool at_top);
+        void _ensure_time_axis_view_is_visible (TimeAxisView const & tav, bool at_top);
        void foreach_time_axis_view (sigc::slot<void,TimeAxisView&>);
        void add_to_idle_resize (TimeAxisView*, int32_t);
 
@@ -402,6 +404,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        void center_screen (framepos_t);
 
        TrackViewList axis_views_from_routes (boost::shared_ptr<ARDOUR::RouteList>) const;
+
        Gtkmm2ext::TearOff* mouse_mode_tearoff () const { return _mouse_mode_tearoff; }
        Gtkmm2ext::TearOff* tools_tearoff () const { return _tools_tearoff; }
 
@@ -422,6 +425,10 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
 
        Gdk::Cursor* get_canvas_cursor () const { return current_canvas_cursor; }
        void set_canvas_cursor (Gdk::Cursor*, bool save=false);
+       
+       void push_canvas_cursor (Gdk::Cursor*);
+       void pop_canvas_cursor ();
+
        void set_current_trimmable (boost::shared_ptr<ARDOUR::Trimmable>);
        void set_current_movable (boost::shared_ptr<ARDOUR::Movable>);
 
@@ -446,20 +453,32 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
                _stepping_axis_view = v;
        }
 
-       ArdourCanvas::Group* get_trackview_group () const { return _trackview_group; }
-        ArdourCanvas::Group* get_time_bars_group () const;
-        ArdourCanvas::Group* get_track_canvas_group () const;
-        ArdourCanvas::GtkCanvasViewport* get_time_bars_canvas () const;
+       ArdourCanvas::Container* get_trackview_group () const { return _trackview_group; }
+        ArdourCanvas::Container* get_noscroll_group () const { return no_scroll_group; }
+        ArdourCanvas::ScrollGroup* get_hscroll_group () const { return h_scroll_group; }
+        ArdourCanvas::ScrollGroup* get_vscroll_group () const { return v_scroll_group; }
+        ArdourCanvas::ScrollGroup* get_hvscroll_group () const { return hv_scroll_group; }
+
         ArdourCanvas::GtkCanvasViewport* get_track_canvas () const;
 
         void override_visible_track_count ();
 
+       /* Ruler metrics methods */
+
+       void metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>&, gdouble, gdouble, gint);
+       void metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>&, gdouble, gdouble, gint);
+       void metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>&, gdouble, gdouble, gint);
+       void metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>&, gdouble, gdouble, gint);
+
   protected:
        void map_transport_state ();
        void map_position_change (framepos_t);
 
        void on_realize();
 
+       void suspend_route_redisplay ();
+       void resume_route_redisplay ();
+
   private:
 
        void color_handler ();
@@ -522,7 +541,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
 
        JoinObjectRangeState _join_object_range_state;
 
-       void update_join_object_range_location (double, double);
+       void update_join_object_range_location (double);
 
        boost::optional<int>  pre_notebook_shrink_pane_width;
 
@@ -547,7 +566,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        void refresh_location_display ();
        void refresh_location_display_internal (ARDOUR::Locations::LocationList&);
        void add_new_location (ARDOUR::Location *);
-       ArdourCanvas::Group* add_new_location_internal (ARDOUR::Location *);
+       ArdourCanvas::Container* add_new_location_internal (ARDOUR::Location *);
        void location_gone (ARDOUR::Location *);
        void remove_marker (ArdourCanvas::Item&, GdkEvent*);
        gint really_remove_marker (ARDOUR::Location* loc);
@@ -593,7 +612,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        LocationMarkerMap location_markers;
 
        void update_marker_labels ();
-       void update_marker_labels (ArdourCanvas::Group *);
+       void update_marker_labels (ArdourCanvas::Container *);
        void check_marker_label (Marker *);
 
        /** A set of lists of Markers that are in each of the canvas groups
@@ -602,7 +621,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
         *  a marker has moved we can decide whether we need to update the labels
         *  for all markers or for just a few.
         */
-       std::map<ArdourCanvas::Group *, std::list<Marker *> > _sorted_marker_lists;
+       std::map<ArdourCanvas::Container *, std::list<Marker *> > _sorted_marker_lists;
        void remove_sorted_marker (Marker *);
 
        void hide_marker (ArdourCanvas::Item*, GdkEvent*);
@@ -688,62 +707,77 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        Gtk::VBox           global_vpacker;
        Gtk::VBox           vpacker;
 
+       std::stack<Gdk::Cursor*> _cursor_stack;
        Gdk::Cursor*          current_canvas_cursor;
-       Gdk::Cursor* which_grabber_cursor ();
-       void set_canvas_cursor ();
+       Gdk::Cursor* which_grabber_cursor () const;
+       Gdk::Cursor* which_track_cursor () const;
+       Gdk::Cursor* which_mode_cursor () const;
+       Gdk::Cursor* which_trim_cursor (bool left_side) const;
+       bool reset_canvas_cursor ();
+       void choose_canvas_cursor_on_entry (GdkEventCrossing*, ItemType);
 
        ArdourCanvas::GtkCanvas* _track_canvas;
        ArdourCanvas::GtkCanvasViewport* _track_canvas_viewport;
 
-        ArdourCanvas::GtkCanvas* _time_bars_canvas;
-        ArdourCanvas::GtkCanvasViewport* _time_bars_canvas_viewport;
-
        bool within_track_canvas;
 
        friend class VerboseCursor;
        VerboseCursor* _verbose_cursor;
 
        void parameter_changed (std::string);
+       void ui_parameter_changed (std::string);
 
-       bool track_canvas_motion (GdkEvent*);
-
-       Gtk::EventBox             time_canvas_event_box;
        Gtk::EventBox             time_bars_event_box;
-       Gtk::EventBox             ruler_label_event_box;
+       Gtk::VBox                 time_bars_vbox;
 
-       ArdourCanvas::Group      *minsec_group;
        ArdourCanvas::Pixbuf     *logo_item;
-       ArdourCanvas::Group      *bbt_group;
-       ArdourCanvas::Group      *timecode_group;
-       ArdourCanvas::Group      *frame_group;
-       ArdourCanvas::Group      *tempo_group;
-       ArdourCanvas::Group      *meter_group;
-       ArdourCanvas::Group      *marker_group;
-       ArdourCanvas::Group      *range_marker_group;
-       ArdourCanvas::Group      *transport_marker_group;
-       ArdourCanvas::Group*      cd_marker_group;
+#if 0    
+    /* these will be needed when we have canvas rulers */
+       ArdourCanvas::Container      *minsec_group;
+       ArdourCanvas::Container      *bbt_group;
+       ArdourCanvas::Container      *timecode_group;
+       ArdourCanvas::Container      *frame_group;
+#endif
+
+       ArdourCanvas::Container      *tempo_group;
+       ArdourCanvas::Container      *meter_group;
+       ArdourCanvas::Container      *marker_group;
+       ArdourCanvas::Container      *range_marker_group;
+       ArdourCanvas::Container      *transport_marker_group;
+       ArdourCanvas::Container*      cd_marker_group;
 
        /* parent for groups which themselves contain time markers */
-       ArdourCanvas::Group*     _time_markers_group;
+       ArdourCanvas::Container*     _time_markers_group;
+
+       /* The group containing all other groups that are scrolled vertically
+          and horizontally.
+       */
+        ArdourCanvas::ScrollGroup* hv_scroll_group;
 
-       ArdourCanvas::Group*      meter_bar_group;
-       ArdourCanvas::Group*      tempo_bar_group;
-       ArdourCanvas::Group*      marker_bar_group;
-       ArdourCanvas::Group*      range_marker_bar_group;
-       ArdourCanvas::Group*      transport_marker_bar_group;
-       ArdourCanvas::Group*      cd_marker_bar_group;
+       /* The group containing all other groups that are scrolled vertically ONLY
+       */
+        ArdourCanvas::ScrollGroup* v_scroll_group;
+
+       /* The group containing all other groups that are scrolled horizontally ONLY
+       */
+        ArdourCanvas::ScrollGroup* h_scroll_group;
 
-       /* The group containing all trackviews.  Only scrolled vertically. */
-       ArdourCanvas::Group* _trackview_group;
+       /* The group containing all trackviews. */
+       ArdourCanvas::Container* no_scroll_group;
 
-       /* The group used for region motion.  Sits on top of _trackview_group */
-       ArdourCanvas::Group* _region_motion_group;
+       /* The group containing all trackviews. */
+       ArdourCanvas::Container* _trackview_group;
+
+       /* The group holding things (mostly regions) while dragging so they
+        * are on top of everything else
+        */
+       ArdourCanvas::Container* _drag_motion_group;
 
         /* a rect that sits at the bottom of all tracks to act as a drag-no-drop/clickable
         * target area.
         */
-        ArdourCanvas::Rectangle* _canvas_bottom_rect;
-        bool canvas_bottom_rect_event (GdkEvent* event);
+        ArdourCanvas::Rectangle* _canvas_drop_zone;
+        bool canvas_drop_zone_event (GdkEvent* event);
 
        enum RulerType {
                ruler_metric_timecode = 0,
@@ -760,7 +794,6 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
                ruler_video_timeline = 10,
        };
 
-       static GtkCustomMetric ruler_metrics[4];
        Glib::RefPtr<Gtk::ToggleAction> ruler_timecode_action;
        Glib::RefPtr<Gtk::ToggleAction> ruler_bbt_action;
        Glib::RefPtr<Gtk::ToggleAction> ruler_samples_action;
@@ -773,13 +806,10 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        Glib::RefPtr<Gtk::ToggleAction> ruler_cd_marker_action;
        bool                            no_ruler_shown_update;
 
-       bool ruler_button_press (GdkEventButton*);
-       bool ruler_button_release (GdkEventButton*);
-       bool ruler_mouse_motion (GdkEventMotion*);
-       bool ruler_scroll (GdkEventScroll* event);
-
        Gtk::Widget * ruler_grabbed_widget;
 
+       RulerDialog* ruler_dialog;
+
        void initialize_rulers ();
        void update_just_timecode ();
        void compute_fixed_ruler_scale (); //calculates the RulerScale of the fixed rulers
@@ -795,11 +825,6 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        void store_ruler_visibility ();
        void restore_ruler_visibility ();
 
-       static gint _metric_get_timecode (GtkCustomRulerMark **, gdouble, gdouble, gint);
-       static gint _metric_get_bbt (GtkCustomRulerMark **, gdouble, gdouble, gint);
-       static gint _metric_get_samples (GtkCustomRulerMark **, gdouble, gdouble, gint);
-       static gint _metric_get_minsec (GtkCustomRulerMark **, gdouble, gdouble, gint);
-
        enum MinsecRulerScale {
                minsec_show_seconds,
                minsec_show_minutes,
@@ -853,21 +878,10 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
                                      ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_begin,
                                      ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_end);
 
-       gint metric_get_timecode (GtkCustomRulerMark **, gdouble, gdouble, gint);
-       gint metric_get_bbt (GtkCustomRulerMark **, gdouble, gdouble, gint);
-       gint metric_get_samples (GtkCustomRulerMark **, gdouble, gdouble, gint);
-       gint metric_get_minsec (GtkCustomRulerMark **, gdouble, gdouble, gint);
-
-       Gtk::Widget        *_ruler_separator;
-       GtkWidget          *_timecode_ruler;
-       GtkWidget          *_bbt_ruler;
-       GtkWidget          *_samples_ruler;
-       GtkWidget          *_minsec_ruler;
-       Gtk::Widget        *timecode_ruler;
-       Gtk::Widget        *bbt_ruler;
-       Gtk::Widget        *samples_ruler;
-       Gtk::Widget        *minsec_ruler;
-       static Editor      *ruler_editor;
+       ArdourCanvas::Ruler* timecode_ruler;
+       ArdourCanvas::Ruler* bbt_ruler;
+       ArdourCanvas::Ruler* samples_ruler;
+       ArdourCanvas::Ruler* minsec_ruler;
 
        static const double timebar_height;
        guint32 visible_timebars;
@@ -893,7 +907,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
 
        /* videtimline related actions */
        Gtk::Label                videotl_label;
-       ArdourCanvas::Group*      videotl_group;
+       ArdourCanvas::Container*      videotl_group;
        Glib::RefPtr<Gtk::ToggleAction> ruler_video_action;
        Glib::RefPtr<Gtk::ToggleAction> xjadeo_proc_action;
        Glib::RefPtr<Gtk::ToggleAction> xjadeo_ontop_action;
@@ -915,8 +929,6 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        void export_video (bool range = false);
        void toggle_region_video_lock ();
 
-       Gtk::VBox          time_bars_vbox;
-
        friend class EditorCursor;
 
        EditorCursor*        playhead_cursor;
@@ -989,9 +1001,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        Gtk::Menu *edit_controls_left_menu;
        Gtk::Menu *edit_controls_right_menu;
 
-       Gtk::VBox           ruler_label_vbox;
        Gtk::VBox           track_canvas_vbox;
-       Gtk::VBox           time_canvas_vbox;
        Gtk::VBox           edit_controls_vbox;
        Gtk::HBox           edit_controls_hbox;
 
@@ -1044,8 +1054,8 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
 
        /* track views */
        TrackViewList track_views;
-       std::pair<TimeAxisView*, double> trackview_by_y_position (double);
-       TimeAxisView* axis_view_from_route (boost::shared_ptr<ARDOUR::Route>) const;
+       std::pair<TimeAxisView*, double> trackview_by_y_position (double, bool trackview_relative_offset = true) const;
+       RouteTimeAxisView* axis_view_from_route (boost::shared_ptr<ARDOUR::Route>) const;
 
        TrackViewList get_tracks_for_range_action () const;
 
@@ -1211,11 +1221,8 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        void temporal_zoom_by_frame (framepos_t start, framepos_t end);
        void temporal_zoom_to_frame (bool coarser, framepos_t frame);
 
-       void insert_region_list_drag (boost::shared_ptr<ARDOUR::Region>, int x, int y);
        void insert_region_list_selection (float times);
 
-       void insert_route_list_drag (boost::shared_ptr<ARDOUR::Route>, int x, int y);
-
        /* import & embed */
 
        void add_external_audio_action (Editing::ImportMode);
@@ -1244,7 +1251,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
                         int target_regions, int target_tracks, boost::shared_ptr<ARDOUR::Track>&, bool add_channel_suffix);
 
        int finish_bringing_in_material (boost::shared_ptr<ARDOUR::Region> region, uint32_t, uint32_t,  framepos_t& pos, Editing::ImportMode mode,
-                                     boost::shared_ptr<ARDOUR::Track>& existing_track);
+                                        boost::shared_ptr<ARDOUR::Track>& existing_track, const std::string& new_track_name);
 
        boost::shared_ptr<ARDOUR::AudioTrack> get_nth_selected_audio_track (int nth) const;
        boost::shared_ptr<ARDOUR::MidiTrack> get_nth_selected_midi_track (int nth) const;
@@ -1351,9 +1358,16 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        DragManager* _drags;
 
        void escape ();
+       void lock ();
+       void unlock ();
+       Gtk::Dialog* lock_dialog;
+
+       struct timeval last_event_time;
+       bool generic_event_handler (GdkEvent*);
+       bool lock_timeout_callback ();
+       void start_lock_event_timing ();
 
        Gtk::Menu fade_context_menu;
-       void popup_fade_context_menu (int, int, ArdourCanvas::Item*, ItemType);
 
        Gtk::Menu xfade_in_context_menu;
        Gtk::Menu xfade_out_context_menu;
@@ -1393,6 +1407,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
 
        /* Canvas event handlers */
 
+       bool canvas_scroll_event (GdkEventScroll* event, bool from_canvas);
        bool canvas_control_point_event (GdkEvent* event,ArdourCanvas::Item*, ControlPoint*);
        bool canvas_line_event (GdkEvent* event,ArdourCanvas::Item*, AutomationLine*);
        bool canvas_selection_rect_event (GdkEvent* event,ArdourCanvas::Item*, SelectionRect*);
@@ -1401,10 +1416,11 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        bool canvas_start_xfade_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*);
        bool canvas_end_xfade_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*);
        bool canvas_fade_in_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*);
-       bool canvas_fade_in_handle_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*);
+       bool canvas_fade_in_handle_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*, bool trim = false);
        bool canvas_fade_out_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*);
-       bool canvas_fade_out_handle_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*);
+       bool canvas_fade_out_handle_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*, bool trim = false);
        bool canvas_region_view_event (GdkEvent* event,ArdourCanvas::Item*, RegionView*);
+       bool canvas_wave_view_event (GdkEvent* event,ArdourCanvas::Item*, RegionView*);
        bool canvas_frame_handle_event (GdkEvent* event,ArdourCanvas::Item*, RegionView*);
        bool canvas_region_view_name_highlight_event (GdkEvent* event,ArdourCanvas::Item*, RegionView*);
        bool canvas_region_view_name_event (GdkEvent* event,ArdourCanvas::Item*, RegionView*);
@@ -1417,6 +1433,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        bool canvas_automation_track_event(GdkEvent* event, ArdourCanvas::Item*, AutomationTimeAxisView*);
        bool canvas_note_event (GdkEvent* event, ArdourCanvas::Item *);
 
+       bool canvas_ruler_event (GdkEvent* event, ArdourCanvas::Item *, ItemType);
        bool canvas_tempo_bar_event (GdkEvent* event, ArdourCanvas::Item*);
        bool canvas_meter_bar_event (GdkEvent* event, ArdourCanvas::Item*);
        bool canvas_marker_bar_event (GdkEvent* event, ArdourCanvas::Item*);
@@ -1441,15 +1458,11 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        friend class EditorRouteGroups;
        friend class EditorRegions;
 
-       /** true if the mouse is over a place where region trim can happen */
-       bool _over_region_trim_target;
-
        /* non-public event handlers */
 
        bool canvas_playhead_cursor_event (GdkEvent* event, ArdourCanvas::Item*);
        bool track_canvas_scroll (GdkEventScroll* event);
 
-       bool track_canvas_scroll_event (GdkEventScroll* event);
        bool track_canvas_button_press_event (GdkEventButton* event);
        bool track_canvas_button_release_event (GdkEventButton* event);
        bool track_canvas_motion_notify_event (GdkEventMotion* event);
@@ -1479,8 +1492,8 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
 
        TempoLines* tempo_lines;
 
-       ArdourCanvas::Group* global_rect_group;
-       ArdourCanvas::Group* time_line_group;
+       ArdourCanvas::Container* global_rect_group;
+       ArdourCanvas::Container* time_line_group;
 
        void hide_measures ();
         void draw_measures (ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
@@ -1939,7 +1952,6 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        bool entered_track_canvas (GdkEventCrossing*);
        void set_entered_track (TimeAxisView*);
        void set_entered_regionview (RegionView*);
-       void ensure_track_visible (TimeAxisView*);
        gint left_automation_track ();
 
        void reset_canvas_action_sensitivity (bool);
@@ -2061,8 +2073,6 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        Gtk::MenuItem& action_menu_item (std::string const &);
        void action_pre_activated (Glib::RefPtr<Gtk::Action> const &);
 
-       void set_canvas_cursor_for_region_view (double, RegionView *);
-
        MouseCursors* _cursors;
 
        void follow_mixer_selection ();
index 1072b497c2560d9f6c28115fdaaeb2c0f74ecb22..27fab388be8a9880910d756d8c2ceada9125c34e 100644 (file)
@@ -50,6 +50,7 @@ using namespace Gtk;
 using namespace Glib;
 using namespace std;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Editing;
 
@@ -150,6 +151,11 @@ Editor::register_actions ()
 
        ActionManager::register_action (editor_actions, "escape", _("Break drag or deselect all"), sigc::mem_fun (*this, &Editor::escape));
 
+       /* We don't bother registering "unlock" because it would be insensitive
+          when required. Editor::unlock() must be invoked directly.
+       */
+       ActionManager::register_action (editor_actions, "lock", _("Lock"), sigc::mem_fun (*this, &Editor::lock));
+
        toggle_reg_sens (editor_actions, "show-editor-mixer", _("Show Editor Mixer"), sigc::mem_fun (*this, &Editor::editor_mixer_button_toggled));
        toggle_reg_sens (editor_actions, "show-editor-list", _("Show Editor List"), sigc::mem_fun (*this, &Editor::editor_list_button_toggled));
 
@@ -702,7 +708,7 @@ Editor::load_bindings ()
 
        std::string binding_file;
 
-       if (find_file_in_search_path (ardour_config_search_path(), "editor.bindings", binding_file)) {
+       if (find_file (ardour_config_search_path(), "editor.bindings", binding_file)) {
                 key_bindings.load (binding_file);
                info << string_compose (_("Loaded editor bindings from %1"), binding_file) << endmsg;
         } else {
index 7a5f3982bf11bcfd274a5a42ebd175ed638ff100..339249260e4610bb64d00f7b9400d900686b29a8 100644 (file)
@@ -52,7 +52,6 @@
 #include "audio_time_axis.h"
 #include "midi_time_axis.h"
 #include "session_import_dialog.h"
-#include "utils.h"
 #include "gui_thread.h"
 #include "interthread_progress_window.h"
 #include "mouse_cursors.h"
@@ -528,8 +527,8 @@ Editor::embed_sndfiles (vector<string> paths, bool multifile,
        SoundFileInfo finfo;
        int ret = 0;
 
-       set_canvas_cursor (_cursors->wait);
-       gdk_flush ();
+        push_canvas_cursor (_cursors->wait);
+        gdk_flush ();
 
        for (vector<string>::iterator p = paths.begin(); p != paths.end(); ++p) {
 
@@ -601,8 +600,6 @@ Editor::embed_sndfiles (vector<string> paths, bool multifile,
                        }
                }
 
-               set_canvas_cursor (_cursors->wait);
-
                for (int n = 0; n < finfo.channels; ++n) {
 
                        try {
@@ -611,7 +608,7 @@ Editor::embed_sndfiles (vector<string> paths, bool multifile,
 
                                boost::shared_ptr<Source> s;
 
-                               if ((s = _session->source_by_path_and_channel (path, n)) == 0) {
+                               if ((s = _session->audio_source_by_path_and_channel (path, n)) == 0) {
 
                                        source = boost::dynamic_pointer_cast<AudioFileSource> (
                                                SourceFactory::createExternal (DataType::AUDIO, *_session,
@@ -644,7 +641,7 @@ Editor::embed_sndfiles (vector<string> paths, bool multifile,
        ret = add_sources (paths, sources, pos, disposition, mode, target_regions, target_tracks, track, true);
 
   out:
-       set_canvas_cursor (current_canvas_cursor);
+       pop_canvas_cursor ();
        return ret;
 }
 
@@ -657,7 +654,8 @@ Editor::add_sources (vector<string> paths, SourceList& sources, framepos_t& pos,
        uint32_t input_chan = 0;
        uint32_t output_chan = 0;
        bool use_timestamp;
-       
+       vector<string> track_names;
+
        use_timestamp = (pos == -1);
 
        // kludge (for MIDI we're abusing "channel" for "track" here)
@@ -694,6 +692,11 @@ Editor::add_sources (vector<string> paths, SourceList& sources, framepos_t& pos,
 
                regions.push_back (r);
 
+               /* if we're creating a new track, name it after the cleaned-up
+                * and "merged" region name.
+                */
+
+               track_names.push_back (region_name);
 
        } else if (target_regions == -1 || target_regions > 1) {
 
@@ -724,29 +727,26 @@ Editor::add_sources (vector<string> paths, SourceList& sources, framepos_t& pos,
                                        region_name = (*x)->name();
                                }
                                
-                               switch (sources.size()) {
-                                       /* zero and one channel handled
-                                          by previous if() condition
-                                       */
-                               case 2:
+                               if (sources.size() == 2) {
                                        if (n == 0) {
                                                region_name += "-L";
                                        } else {
                                                region_name += "-R";
                                        }
-                                       break;
-                               default:
-                                       region_name += (char) '-';
-                                       region_name += (char) ('1' + n);
-                                       break;
+                               } else if (sources.size() > 2) {
+                                       region_name += string_compose ("-%1", n+1);
                                }
                                
+                               track_names.push_back (region_name);
+                               
                        } else {
                                if (fs) {
                                        region_name = region_name_from_path (fs->path(), false, false, sources.size(), n);
-                               } else{
+                               } else {
                                        region_name = (*x)->name();
                                }
+
+                               track_names.push_back (PBD::basename_nosuffix (paths[n]));
                        }
 
                        PropertyList plist;
@@ -798,6 +798,12 @@ Editor::add_sources (vector<string> paths, SourceList& sources, framepos_t& pos,
        framepos_t rlen = 0;
 
        begin_reversible_command (Operations::insert_file);
+
+       /* we only use tracks names when importing to new tracks, but we
+        * require that one is defined for every region, just to keep
+        * the API simpler.
+        */
+       assert (regions.size() == track_names.size());
        
        for (vector<boost::shared_ptr<Region> >::iterator r = regions.begin(); r != regions.end(); ++r, ++n) {
                boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (*r);
@@ -830,9 +836,8 @@ Editor::add_sources (vector<string> paths, SourceList& sources, framepos_t& pos,
                                 pos = get_preferred_edit_position ();
                         }
                 }
-
-
-               finish_bringing_in_material (*r, input_chan, output_chan, pos, mode, track);
+               
+               finish_bringing_in_material (*r, input_chan, output_chan, pos, mode, track, track_names[n]);
 
                rlen = (*r)->length();
 
@@ -859,7 +864,7 @@ Editor::add_sources (vector<string> paths, SourceList& sources, framepos_t& pos,
 
 int
 Editor::finish_bringing_in_material (boost::shared_ptr<Region> region, uint32_t in_chans, uint32_t out_chans, framepos_t& pos,
-                                    ImportMode mode, boost::shared_ptr<Track>& existing_track)
+                                    ImportMode mode, boost::shared_ptr<Track>& existing_track, const string& new_track_name)
 {
        boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(region);
        boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(region);
@@ -919,7 +924,11 @@ Editor::finish_bringing_in_material (boost::shared_ptr<Region> region, uint32_t
                                existing_track = mt.front();
                        }
 
-                       existing_track->set_name (region->name());
+                       if (!new_track_name.empty()) {
+                               existing_track->set_name (new_track_name);
+                       } else {
+                               existing_track->set_name (region->name());
+                       }
                }
 
                boost::shared_ptr<Playlist> playlist = existing_track->playlist();
index 3b9c5dea379608c88c022783ca97950ccb81372b..9739f65dd1b60994e4e66b41262f6e2cce4a9f76 100644 (file)
 #include "canvas/canvas.h"
 #include "canvas/rectangle.h"
 #include "canvas/pixbuf.h"
+#include "canvas/scroll_group.h"
 #include "canvas/text.h"
 #include "canvas/debug.h"
 
 #include "ardour_ui.h"
+#include "automation_time_axis.h"
 #include "editor.h"
 #include "global_signals.h"
 #include "editing.h"
 
 using namespace std;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtk;
 using namespace Glib;
 using namespace Gtkmm2ext;
 using namespace Editing;
 
-/* XXX this is a hack. it ought to be the maximum value of an framepos_t */
-
-const double max_canvas_coordinate = (double) UINT32_MAX;
-
 void
 Editor::initialize_canvas ()
 {
        _track_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment);
        _track_canvas = _track_canvas_viewport->canvas ();
 
-       _time_bars_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, unused_adjustment);
-       _time_bars_canvas = _time_bars_canvas_viewport->canvas ();
-       
+       /* scroll group for items that should not automatically scroll
+        *  (e.g verbose cursor). It shares the canvas coordinate space.
+       */
+       no_scroll_group = new ArdourCanvas::Container (_track_canvas->root());
+
+       ArdourCanvas::ScrollGroup* hsg; 
+       ArdourCanvas::ScrollGroup* hg;
+       ArdourCanvas::ScrollGroup* vg;
+
+       hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), 
+                                                              ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
+                                                                                                            ArdourCanvas::ScrollGroup::ScrollsHorizontally));
+       CANVAS_DEBUG_NAME (hv_scroll_group, "canvas hv scroll");
+       _track_canvas->add_scroller (*hsg);
+
+       v_scroll_group = vg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsVertically);
+       CANVAS_DEBUG_NAME (v_scroll_group, "canvas v scroll");
+       _track_canvas->add_scroller (*vg);
+
+       h_scroll_group = hg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
+       CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll");
+       _track_canvas->add_scroller (*hg);
+
        _verbose_cursor = new VerboseCursor (this);
 
        /* on the bottom, an image */
@@ -86,9 +105,9 @@ Editor::initialize_canvas ()
                // logo_item->property_width_set() = true;
                // logo_item->show ();
        }
-       
+
        /*a group to hold global rects like punch/loop indicators */
-       global_rect_group = new ArdourCanvas::Group (_track_canvas->root());
+       global_rect_group = new ArdourCanvas::Container (hv_scroll_group);
        CANVAS_DEBUG_NAME (global_rect_group, "global rect group");
 
         transport_loop_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
@@ -100,66 +119,76 @@ Editor::initialize_canvas ()
        transport_punch_range_rect->hide();
 
        /*a group to hold time (measure) lines */
-       time_line_group = new ArdourCanvas::Group (_track_canvas->root());
+       time_line_group = new ArdourCanvas::Container (hv_scroll_group);
        CANVAS_DEBUG_NAME (time_line_group, "time line group");
 
-       _trackview_group = new ArdourCanvas::Group (_track_canvas->root());
+       _trackview_group = new ArdourCanvas::Container (hv_scroll_group);
        CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
-       _region_motion_group = new ArdourCanvas::Group (_trackview_group);
-       CANVAS_DEBUG_NAME (_region_motion_group, "Canvas Region Motion");
+       
+       // used to show zoom mode active zooming
+       zoom_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
+       zoom_rect->hide();
+       zoom_rect->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_zoom_rect_event), (ArdourCanvas::Item*) 0));
+
+       // used as rubberband rect
+       rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
+       rubberband_rect->hide();
+
+       /* a group to hold stuff while it gets dragged around. Must be the
+        * uppermost (last) group with hv_scroll_group as a parent
+        */
+       _drag_motion_group = new ArdourCanvas::Container (hv_scroll_group);                                                                                                                                     
+        CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
+
+       /* TIME BAR CANVAS */
+       
+       _time_markers_group = new ArdourCanvas::Container (h_scroll_group);
+       CANVAS_DEBUG_NAME (_time_markers_group, "time bars");
+
+       cd_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, 0.0));
+       CANVAS_DEBUG_NAME (cd_marker_group, "cd marker group");
+       /* the vide is temporarily placed a the same location as the
+          cd_marker_group, but is moved later.
+       */
+       videotl_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0));
+       CANVAS_DEBUG_NAME (videotl_group, "videotl group");
+       marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, timebar_height + 1.0));
+       CANVAS_DEBUG_NAME (marker_group, "marker group");
+       transport_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 2.0) + 1.0));
+       CANVAS_DEBUG_NAME (transport_marker_group, "transport marker group");
+       range_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 3.0) + 1.0));
+       CANVAS_DEBUG_NAME (range_marker_group, "range marker group");
+       tempo_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 4.0) + 1.0));
+       CANVAS_DEBUG_NAME (tempo_group, "tempo group");
+       meter_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0));
+       CANVAS_DEBUG_NAME (meter_group, "meter group");
 
-       meter_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ());
-       meter_bar = new ArdourCanvas::Rectangle (meter_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
+       meter_bar = new ArdourCanvas::Rectangle (meter_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
        CANVAS_DEBUG_NAME (meter_bar, "meter Bar");
        meter_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
 
-       tempo_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ());
-       tempo_bar = new ArdourCanvas::Rectangle (tempo_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
+       tempo_bar = new ArdourCanvas::Rectangle (tempo_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
        CANVAS_DEBUG_NAME (tempo_bar, "Tempo  Bar");
        tempo_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
 
-       range_marker_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ());
-       range_marker_bar = new ArdourCanvas::Rectangle (range_marker_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
+       range_marker_bar = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
        CANVAS_DEBUG_NAME (range_marker_bar, "Range Marker Bar");
        range_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
 
-       transport_marker_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ());
-       transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
+       transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
        CANVAS_DEBUG_NAME (transport_marker_bar, "transport Marker Bar");
        transport_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
 
-       marker_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ());
-       marker_bar = new ArdourCanvas::Rectangle (marker_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
+       marker_bar = new ArdourCanvas::Rectangle (marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
        CANVAS_DEBUG_NAME (marker_bar, "Marker Bar");
        marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
 
-       cd_marker_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ());
-       cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
+       cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
        CANVAS_DEBUG_NAME (cd_marker_bar, "CD Marker Bar");
        cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
-       
-       _time_markers_group = new ArdourCanvas::Group (_time_bars_canvas->root());
-
-       cd_marker_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, 0.0));
-       CANVAS_DEBUG_NAME (cd_marker_group, "cd marker group");
-       /* the vide is temporarily placed a the same location as the
-          cd_marker_group, but is moved later.
-       */
-       videotl_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0));
-       CANVAS_DEBUG_NAME (videotl_group, "videotl group");
-       marker_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, timebar_height + 1.0));
-       CANVAS_DEBUG_NAME (marker_group, "marker group");
-       transport_marker_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 2.0) + 1.0));
-       CANVAS_DEBUG_NAME (transport_marker_group, "transport marker group");
-       range_marker_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 3.0) + 1.0));
-       CANVAS_DEBUG_NAME (range_marker_group, "range marker group");
-       tempo_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 4.0) + 1.0));
-       CANVAS_DEBUG_NAME (tempo_group, "tempo group");
-       meter_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0));
-       CANVAS_DEBUG_NAME (meter_group, "meter group");
 
        ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height));
-
+       
        cd_marker_bar_drag_rect = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
        CANVAS_DEBUG_NAME (cd_marker_bar_drag_rect, "cd marker drag");
        cd_marker_bar_drag_rect->set_outline (false);
@@ -175,29 +204,20 @@ Editor::initialize_canvas ()
        transport_bar_drag_rect->set_outline (false);
        transport_bar_drag_rect->hide ();
 
-       transport_punchin_line = new ArdourCanvas::Line (_track_canvas->root());
+       transport_punchin_line = new ArdourCanvas::Line (hv_scroll_group);
        transport_punchin_line->set_x0 (0);
        transport_punchin_line->set_y0 (0);
        transport_punchin_line->set_x1 (0);
        transport_punchin_line->set_y1 (ArdourCanvas::COORD_MAX);
        transport_punchin_line->hide ();
 
-       transport_punchout_line  = new ArdourCanvas::Line (_track_canvas->root());
+       transport_punchout_line  = new ArdourCanvas::Line (hv_scroll_group);
        transport_punchout_line->set_x0 (0);
        transport_punchout_line->set_y0 (0);
        transport_punchout_line->set_x1 (0);
        transport_punchout_line->set_y1 (ArdourCanvas::COORD_MAX);
        transport_punchout_line->hide();
 
-       // used to show zoom mode active zooming
-       zoom_rect = new ArdourCanvas::Rectangle (_track_canvas->root(), ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
-       zoom_rect->hide();
-       zoom_rect->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_zoom_rect_event), (ArdourCanvas::Item*) 0));
-
-       // used as rubberband rect
-       rubberband_rect = new ArdourCanvas::Rectangle (_trackview_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
-       rubberband_rect->hide();
-
        tempo_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_tempo_bar_event), tempo_bar));
        meter_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_meter_bar_event), meter_bar));
        marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_marker_bar_event), marker_bar));
@@ -213,17 +233,17 @@ Editor::initialize_canvas ()
        }
 
 
-       _canvas_bottom_rect = new ArdourCanvas::Rectangle (_track_canvas->root(), ArdourCanvas::Rect (0.0, 0.0, max_canvas_coordinate, 20));
+       _canvas_drop_zone = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, 0.0));
        /* this thing is transparent */
-       _canvas_bottom_rect->set_fill (false);
-       _canvas_bottom_rect->set_outline (false);
-       _canvas_bottom_rect->Event.connect (sigc::mem_fun (*this, &Editor::canvas_bottom_rect_event));
+       _canvas_drop_zone->set_fill (false);
+       _canvas_drop_zone->set_outline (false);
+       _canvas_drop_zone->Event.connect (sigc::mem_fun (*this, &Editor::canvas_drop_zone_event));
 
        /* these signals will initially be delivered to the canvas itself, but if they end up remaining unhandled, they are passed to Editor-level
           handlers.
        */
 
-       _track_canvas->signal_scroll_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_scroll_event));
+       _track_canvas->signal_scroll_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_scroll_event), true));
        _track_canvas->signal_motion_notify_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_motion_notify_event));
        _track_canvas->signal_button_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_press_event));
        _track_canvas->signal_button_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_release_event));
@@ -253,6 +273,8 @@ Editor::initialize_canvas ()
 
        _track_canvas_viewport->signal_size_allocate().connect (sigc::mem_fun(*this, &Editor::track_canvas_viewport_allocate));
 
+       initialize_rulers ();
+
        ColorsChanged.connect (sigc::mem_fun (*this, &Editor::color_handler));
        color_handler();
 
@@ -273,6 +295,8 @@ Editor::track_canvas_viewport_size_allocated ()
        _visible_canvas_width  = _canvas_viewport_allocation.get_width ();
        _visible_canvas_height = _canvas_viewport_allocation.get_height ();
 
+       _canvas_drop_zone->set_y1 (_canvas_drop_zone->y0() + (_visible_canvas_height - 20.0));
+
        // SHOWTRACKS
 
        if (height_changed) {
@@ -327,13 +351,13 @@ Editor::reset_controls_layout_height (int32_t h)
         * (the drag-n-drop zone) is, in fact, at the bottom.
         */
 
-       _canvas_bottom_rect->set_position (ArdourCanvas::Duple (0, h));
+       _canvas_drop_zone->set_position (ArdourCanvas::Duple (0, h));
 
        /* track controls layout must span the full height of "h" (all tracks)
         * plus the bottom rect.
         */
 
-       h += _canvas_bottom_rect->height ();
+       h += _canvas_drop_zone->height ();
 
         /* set the height of the scrollable area (i.e. the sum of all contained widgets)
         * for the controls layout. The size request is set elsewhere.
@@ -395,7 +419,7 @@ Editor::drop_paths_part_two (const vector<string>& paths, framepos_t frame, doub
        }
 
 
-       std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (ypos);
+       std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (ypos, false);
        if (tvp.first == 0) {
 
                /* drop onto canvas background: create new tracks */
@@ -442,7 +466,7 @@ Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
 
        if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
 
-               /* D-n-D coordinates are window-relative, so convert to "world" coordinates
+               /* D-n-D coordinates are window-relative, so convert to canvas coordinates
                 */
 
                ev.type = GDK_BUTTON_RELEASE;
@@ -476,19 +500,46 @@ Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
 void
 Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
 {
-       if (!Config->get_autoscroll_editor ()) {
+       if (!Config->get_autoscroll_editor () || autoscroll_active ()) {
                return;
        }
 
+       /* define a rectangular boundary for scrolling. If the mouse moves
+        * outside of this area and/or continue to be outside of this area,
+        * then we will continuously auto-scroll the canvas in the appropriate
+        * direction(s)
+        *
+        * the boundary is defined in coordinates relative to the toplevel
+        * window since that is what we're going to call ::get_pointer() on
+        * during autoscrolling to determine if we're still outside the
+        * boundary or not.
+        */
 
        ArdourCanvas::Rect scrolling_boundary;
        Gtk::Allocation alloc;
-       
+       int cx, cy;
+
        if (from_headers) {
                alloc = controls_layout.get_allocation ();
-       } else {
+       } else {        
                alloc = _track_canvas_viewport->get_allocation ();
+               cx = alloc.get_x();
+               cy = alloc.get_y();
+
+               /* reduce height by the height of the timebars, which happens
+                  to correspond to the position of the hv_scroll_group.
+               */
                
+               alloc.set_height (alloc.get_height() - hv_scroll_group->position().y);
+               alloc.set_y (alloc.get_y() + hv_scroll_group->position().y);
+
+               /* now reduce it again so that we start autoscrolling before we
+                * move off the top or bottom of the canvas
+                */
+
+               alloc.set_height (alloc.get_height() - 20);
+               alloc.set_y (alloc.get_y() + 10);
+
                /* the effective width of the autoscroll boundary so
                   that we start scrolling before we hit the edge.
                   
@@ -501,19 +552,18 @@ Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
                        alloc.set_width (alloc.get_width() - 20);
                        alloc.set_x (alloc.get_x() + 10);
                } 
+
        }
        
-       scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), 
-                                                alloc.get_x() + alloc.get_width(), 
-                                                alloc.get_y() + alloc.get_height());
+       scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), alloc.get_x() + alloc.get_width(), alloc.get_y() + alloc.get_height());
        
        int x, y;
        Gdk::ModifierType mask;
 
        get_window()->get_pointer (x, y, mask);
 
-       if ((allow_horiz && (x < scrolling_boundary.x0 || x >= scrolling_boundary.x1)) ||
-           (allow_vert && (y < scrolling_boundary.y0 || y >= scrolling_boundary.y1))) {
+       if ((allow_horiz && ((x < scrolling_boundary.x0 && leftmost_frame > 0) || x >= scrolling_boundary.x1)) ||
+           (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
                start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
        }
 }
@@ -536,6 +586,7 @@ Editor::autoscroll_canvas ()
        get_window()->get_pointer (x, y, mask);
 
        VisualChange vc;
+       bool vertical_motion = false;
 
        if (autoscroll_horizontal_allowed) {
 
@@ -583,34 +634,32 @@ Editor::autoscroll_canvas ()
 
        if (autoscroll_vertical_allowed) {
                
-               const double vertical_pos = vertical_adjustment.get_value();
-               double new_pixel = vertical_pos;
-               const int speed_factor = 20;
+               // const double vertical_pos = vertical_adjustment.get_value();
+               const int speed_factor = 10;
 
                /* vertical */ 
                
-               new_pixel = vertical_pos;
-
                if (y < autoscroll_boundary.y0) {
 
                        /* scroll to make higher tracks visible */
 
                        if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
                                y_motion = scroll_up_one_track ();
+                               vertical_motion = true;
                        }
 
                } else if (y > autoscroll_boundary.y1) {
 
                        if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
                                y_motion = scroll_down_one_track ();
-                               
+                               vertical_motion = true;
                        }
                }
 
                no_stop = true;
        }
 
-       if (vc.pending) {
+       if (vc.pending || vertical_motion) {
 
                /* change horizontal first */
 
@@ -629,26 +678,33 @@ Editor::autoscroll_canvas ()
                
                /* the motion handler expects events in canvas coordinate space */
 
-               /* first convert from Editor window coordinates to canvas
-                * window coordinates
+               /* we asked for the mouse position above (::get_pointer()) via
+                * our own top level window (we being the Editor). Convert into 
+                * coordinates within the canvas window.
                 */
 
                int cx;
                int cy;
 
-               /* clamp x and y to remain within the visible area */
+               translate_coordinates (*_track_canvas, x, y, cx, cy);
 
-               x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
-               y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
+               /* clamp x and y to remain within the autoscroll boundary,
+                * which is defined in window coordinates
+                */
 
-               translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
+               x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
+               y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
+
+               /* now convert from Editor window coordinates to canvas
+                * window coordinates
+                */
 
                ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
                ev.x = d.x;
                ev.y = d.y;
 
                motion_handler (0, (GdkEvent*) &ev, true);
-
+               
        } else if (no_stop) {
 
                /* not changing visual state but pointer is outside the scrolling boundary
@@ -688,7 +744,7 @@ Editor::autoscroll_canvas ()
                ev.y = d.y;
 
                motion_handler (0, (GdkEvent*) &ev, true);
-
+               
        } else {
                stop_canvas_autoscroll ();
                return false;
@@ -749,26 +805,39 @@ Editor::entered_track_canvas (GdkEventCrossing */*ev*/)
 }
 
 void
-Editor::_ensure_time_axis_view_is_visible (const TimeAxisView& tav, bool at_top)
+Editor::_ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
 {
-       double begin = tav.y_position();
-       double v = vertical_adjustment.get_value ();
-
-       if (!at_top && (begin < v || begin + tav.current_height() > v + _visible_canvas_height)) {
-               /* try to put the TimeAxisView roughly central */
-               if (begin >= _visible_canvas_height/2.0) {
-                       begin -= _visible_canvas_height/2.0;
-               }
+       if (track.hidden()) {
+               return;
        }
 
-       /* Clamp the y pos so that we do not extend beyond the canvas full
-        * height. 
+       /* compute visible area of trackview group, as offsets from top of
+        * trackview group.
         */
-       if (_full_canvas_height - begin < _visible_canvas_height){
-               begin = _full_canvas_height - _visible_canvas_height;
+
+       double const current_view_min_y = vertical_adjustment.get_value();
+       double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
+
+       double const track_min_y = track.y_position ();
+       double const track_max_y = track.y_position () + track.effective_height ();
+
+       if (!at_top && 
+           (track_min_y > current_view_min_y &&
+            track_max_y <= current_view_max_y)) {
+               return;
        }
 
-       vertical_adjustment.set_value (begin);
+       double new_value;
+
+       if (track_min_y < current_view_min_y) {
+               // Track is above the current view
+               new_value = track_min_y;
+       } else {
+               // Track is below the current view
+               new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
+       }
+
+       vertical_adjustment.set_value(new_value);
 }
 
 /** Called when the main vertical_adjustment has changed */
@@ -800,8 +869,18 @@ Editor::set_horizontal_position (double p)
 void
 Editor::color_handler()
 {
+       ArdourCanvas::Color base = ARDOUR_UI::config()->get_canvasvar_RulerBase();
+       ArdourCanvas::Color text = ARDOUR_UI::config()->get_canvasvar_RulerText();
+       timecode_ruler->set_fill_color (base);
+       timecode_ruler->set_outline_color (text);
+       minsec_ruler->set_fill_color (base);
+       minsec_ruler->set_outline_color (text);
+       samples_ruler->set_fill_color (base);
+       samples_ruler->set_outline_color (text);
+       bbt_ruler->set_fill_color (base);
+       bbt_ruler->set_outline_color (text);
+       
        playhead_cursor->set_color (ARDOUR_UI::config()->get_canvasvar_PlayHead());
-       _verbose_cursor->set_color (ARDOUR_UI::config()->get_canvasvar_VerboseCanvasCursor());
 
        meter_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_MeterBar());
        meter_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
@@ -866,20 +945,6 @@ Editor::horizontal_position () const
        return sample_to_pixel (leftmost_frame);
 }
 
-void
-Editor::set_canvas_cursor (Gdk::Cursor* cursor, bool save)
-{
-       if (save) {
-               current_canvas_cursor = cursor;
-       }
-
-       Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
-
-       if (win) {
-               _track_canvas->get_window()->set_cursor (*cursor);
-       }
-}
-
 bool
 Editor::track_canvas_key_press (GdkEventKey*)
 {
@@ -920,26 +985,354 @@ Editor::clamp_verbose_cursor_y (double y)
        return y;
 }
 
-ArdourCanvas::Group*
-Editor::get_time_bars_group () const
+ArdourCanvas::GtkCanvasViewport*
+Editor::get_track_canvas() const
 {
-       return _time_bars_canvas->root();
+       return _track_canvas_viewport;
 }
 
-ArdourCanvas::Group*
-Editor::get_track_canvas_group() const
+void
+Editor::set_canvas_cursor (Gdk::Cursor* cursor, bool save)
 {
-       return _track_canvas->root();
+       if (save) {
+               current_canvas_cursor = cursor;
+       }
+
+       Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
+
+       if (win && cursor) {
+               win->set_cursor (*cursor);
+       }
 }
 
-ArdourCanvas::GtkCanvasViewport*
-Editor::get_time_bars_canvas() const
+void
+Editor::push_canvas_cursor (Gdk::Cursor* cursor)
 {
-       return _time_bars_canvas_viewport;
+       if (cursor) {
+               _cursor_stack.push (cursor);
+               set_canvas_cursor (cursor, false);
+       }
 }
 
-ArdourCanvas::GtkCanvasViewport*
-Editor::get_track_canvas() const
+void
+Editor::pop_canvas_cursor ()
 {
-       return _track_canvas_viewport;
+       if (!_cursor_stack.empty()) {
+               Gdk::Cursor* cursor = _cursor_stack.top ();
+               _cursor_stack.pop ();
+               set_canvas_cursor (cursor, false);
+       }
+}
+
+Gdk::Cursor*
+Editor::which_grabber_cursor () const
+{
+       Gdk::Cursor* c = _cursors->grabber;
+
+       if (_internal_editing) {
+               switch (mouse_mode) {
+               case MouseDraw:
+                       c = _cursors->midi_pencil;
+                       break;
+
+               case MouseObject:
+                       c = _cursors->grabber_note;
+                       break;
+
+               case MouseTimeFX:
+                       c = _cursors->midi_resize;
+                       break;
+                       
+               case MouseRange:
+                       c = _cursors->grabber_note;
+                       break;
+
+               default:
+                       break;
+               }
+
+       } else {
+
+               switch (_edit_point) {
+               case EditAtMouse:
+                       c = _cursors->grabber_edit_point;
+                       break;
+               default:
+                       boost::shared_ptr<Movable> m = _movable.lock();
+                       if (m && m->locked()) {
+                               c = _cursors->speaker;
+                       }
+                       break;
+               }
+       }
+
+       return c;
+}
+
+Gdk::Cursor*
+Editor::which_trim_cursor (bool left) const
+{
+       if (!entered_regionview) {
+               return 0;
+       }
+
+       Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
+               
+       if (left) {
+               
+               if (ct & Trimmable::FrontTrimEarlier) {
+                       return _cursors->left_side_trim;
+               } else {
+                       return _cursors->left_side_trim_right_only;
+               }
+       } else {
+               if (ct & Trimmable::EndTrimLater) {
+                       return _cursors->right_side_trim;
+               } else {
+                       return _cursors->right_side_trim_left_only;
+               }
+       }
+}
+
+Gdk::Cursor*
+Editor::which_mode_cursor () const
+{
+       Gdk::Cursor* mode_cursor = 0;
+
+       switch (mouse_mode) {
+       case MouseRange:
+               mode_cursor = _cursors->selector;
+               if (_internal_editing) {
+                       mode_cursor = which_grabber_cursor();
+               }
+               break;
+
+       case MouseObject:
+               /* don't use mode cursor, pick a grabber cursor based on the item */
+               break;
+
+       case MouseDraw:
+               mode_cursor = _cursors->midi_pencil;
+               break;
+
+       case MouseGain:
+               mode_cursor = _cursors->cross_hair;
+               break;
+
+       case MouseZoom:
+               if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
+                       mode_cursor = _cursors->zoom_out;
+               } else {
+                       mode_cursor = _cursors->zoom_in;
+               }
+               break;
+
+       case MouseTimeFX:
+               mode_cursor = _cursors->time_fx; // just use playhead
+               break;
+
+       case MouseAudition:
+               mode_cursor = _cursors->speaker;
+               break;
+       }
+
+       /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
+       if (!_internal_editing && get_smart_mode() ) {
+
+               double x, y;
+               get_pointer_position (x, y);
+
+               if (x >= 0 && y >= 0) {
+                       
+                       vector<ArdourCanvas::Item const *> items;
+
+                       /* Note how we choose a specific scroll group to get
+                        * items from. This could be problematic.
+                        */
+                       
+                       hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
+                       
+                       // first item will be the upper most 
+                       
+                       if (!items.empty()) {
+                               const ArdourCanvas::Item* i = items.front();
+                               
+                               if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
+                                       pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
+                                       if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
+                                               mode_cursor = _cursors->up_down;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       return mode_cursor;
+}
+
+Gdk::Cursor*
+Editor::which_track_cursor () const
+{
+       Gdk::Cursor* cursor = 0;
+
+       assert (mouse_mode == MouseObject || get_smart_mode());
+
+       if (!_internal_editing) {
+               switch (_join_object_range_state) {
+               case JOIN_OBJECT_RANGE_NONE:
+               case JOIN_OBJECT_RANGE_OBJECT:
+                       cursor = which_grabber_cursor ();
+                       break;
+               case JOIN_OBJECT_RANGE_RANGE:
+                       cursor = _cursors->selector;
+                       break;
+               }
+       }
+
+       return cursor;
+}
+
+bool
+Editor::reset_canvas_cursor ()
+{
+       if (!is_drawable()) {
+               return false;
+       }
+
+       Gdk::Cursor* cursor = which_mode_cursor ();
+
+       if (cursor) {
+               set_canvas_cursor (cursor);
+               return true;
+       }
+
+       return false;
+}
+
+void
+Editor::choose_canvas_cursor_on_entry (GdkEventCrossing* /*event*/, ItemType type)
+{
+       Gdk::Cursor* cursor = 0;
+
+       if (_drags->active()) {
+               return;
+       }
+
+       cursor = which_mode_cursor ();
+
+       if (mouse_mode == MouseObject || get_smart_mode ()) {
+
+               /* find correct cursor to use in object/smart mode */
+
+               switch (type) {
+               case RegionItem:
+               case RegionViewNameHighlight:
+               case RegionViewName:
+               case WaveItem:
+               case StreamItem:
+               case AutomationTrackItem:
+                       cursor = which_track_cursor ();
+                       break;
+               case PlayheadCursorItem:
+                       switch (_edit_point) {
+                       case EditAtMouse:
+                               cursor = _cursors->grabber_edit_point;
+                               break;
+                       default:
+                               cursor = _cursors->grabber;
+                               break;
+                       }
+                       break;
+               case SelectionItem:
+                       cursor = _cursors->selector;
+                       break;
+               case ControlPointItem:
+                       cursor = _cursors->fader;
+                       break;
+               case GainLineItem:
+                       cursor = _cursors->fader;
+                       break;
+               case AutomationLineItem:
+                       cursor = _cursors->cross_hair;
+                       break;
+               case StartSelectionTrimItem:
+                       cursor = _cursors->left_side_trim;
+                       break;
+               case EndSelectionTrimItem:
+                       cursor = _cursors->right_side_trim;
+                       break;
+               case FadeInItem:
+                       cursor = _cursors->fade_in;
+                       break;
+               case FadeInHandleItem:
+                       cursor = _cursors->fade_in;
+                       break;
+               case FadeInTrimHandleItem:
+                       cursor = _cursors->fade_in;
+                       break;
+               case FadeOutItem:
+                       cursor = _cursors->fade_out;
+                       break;
+               case FadeOutHandleItem:
+                       cursor = _cursors->fade_out;
+                       break;
+               case FadeOutTrimHandleItem:
+                       cursor = _cursors->fade_out;
+                       break;
+               case NoteItem:
+                       cursor = which_grabber_cursor();
+                       break;
+               case FeatureLineItem:
+                       cursor = _cursors->cross_hair;
+                       break;
+               case LeftFrameHandle:
+                       cursor = which_trim_cursor (true);
+                       break;
+               case RightFrameHandle:
+                       cursor = which_trim_cursor (false);
+                       break;
+               case StartCrossFadeItem:
+                       cursor = _cursors->fade_in;
+                       break;
+               case EndCrossFadeItem:
+                       cursor = _cursors->fade_out;
+                       break;
+               case CrossfadeViewItem:
+                       cursor = _cursors->cross_hair;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       switch (type) {
+               /* These items use the timebar cursor at all times */
+       case TimecodeRulerItem:
+       case MinsecRulerItem:
+       case BBTRulerItem:
+       case SamplesRulerItem:
+               cursor = _cursors->timebar;
+               break;
+
+               /* These items use the grabber cursor at all times */
+       case MeterMarkerItem:
+       case TempoMarkerItem:
+       case MeterBarItem:
+       case TempoBarItem:
+       case MarkerItem:
+       case MarkerBarItem:
+       case RangeMarkerBarItem:
+       case CdMarkerBarItem:
+       case VideoBarItem:
+       case TransportMarkerBarItem:
+               cursor = which_grabber_cursor();
+               break;
+
+       default:
+               break;
+       }
+
+       if (cursor) {
+               set_canvas_cursor (cursor, false);
+       }
 }
index 864624a690e5cc2ffae7e08f58b4c5ecaffaba73..2609abadc84c2779f687211730a9821a217f2aab 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "canvas/canvas.h"
 #include "canvas/text.h"
+#include "canvas/scroll_group.h"
 
 #include "editor.h"
 #include "keyboard.h"
@@ -60,6 +61,10 @@ using Gtkmm2ext::Keyboard;
 bool
 Editor::track_canvas_scroll (GdkEventScroll* ev)
 {
+       if (Keyboard::some_magic_widget_has_focus()) {
+               return false;
+       }
+       
        framepos_t xdelta;
        int direction = ev->direction;
 
@@ -68,7 +73,7 @@ Editor::track_canvas_scroll (GdkEventScroll* ev)
         */
 
        Duple event_coords = _track_canvas->window_to_canvas (Duple (ev->x, ev->y));
-       
+
   retry:
        switch (direction) {
        case GDK_SCROLL_UP:
@@ -85,7 +90,7 @@ Editor::track_canvas_scroll (GdkEventScroll* ev)
                } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
                        if (!current_stepping_trackview) {
                                step_timeout = Glib::signal_timeout().connect (sigc::mem_fun(*this, &Editor::track_height_step_timeout), 500);
-                               std::pair<TimeAxisView*, int> const p = trackview_by_y_position (event_coords.y);
+                               std::pair<TimeAxisView*, int> const p = trackview_by_y_position (event_coords.y, false);
                                current_stepping_trackview = p.first;
                                if (!current_stepping_trackview) {
                                        return false;
@@ -114,7 +119,7 @@ Editor::track_canvas_scroll (GdkEventScroll* ev)
                } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
                        if (!current_stepping_trackview) {
                                step_timeout = Glib::signal_timeout().connect (sigc::mem_fun(*this, &Editor::track_height_step_timeout), 500);
-                               std::pair<TimeAxisView*, int> const p = trackview_by_y_position (event_coords.y);
+                               std::pair<TimeAxisView*, int> const p = trackview_by_y_position (event_coords.y, false);
                                current_stepping_trackview = p.first;
                                if (!current_stepping_trackview) {
                                        return false;
@@ -156,8 +161,15 @@ Editor::track_canvas_scroll (GdkEventScroll* ev)
 }
 
 bool
-Editor::track_canvas_scroll_event (GdkEventScroll *event)
+Editor::canvas_scroll_event (GdkEventScroll *event, bool from_canvas)
 {
+       if (from_canvas) {
+               boost::optional<ArdourCanvas::Rect> rulers = _time_markers_group->bounding_box();
+               if (rulers && rulers->contains (Duple (event->x, event->y))) {
+                       return canvas_ruler_event ((GdkEvent*) event, timecode_ruler, TimecodeRulerItem);
+               }
+       }
+
        _track_canvas->grab_focus();
        return track_canvas_scroll (event);
 }
@@ -188,16 +200,6 @@ Editor::track_canvas_motion_notify_event (GdkEventMotion */*event*/)
        return false;
 }
 
-bool
-Editor::track_canvas_motion (GdkEvent *ev)
-{
-       if (_verbose_cursor->visible ()) {
-               _verbose_cursor->set_position (ev->motion.x + 10, ev->motion.y + 10);
-       }
-
-       return false;
-}
-
 bool
 Editor::typed_event (ArdourCanvas::Item* item, GdkEvent *event, ItemType type)
 {
@@ -267,16 +269,14 @@ Editor::canvas_region_view_event (GdkEvent *event, ArdourCanvas::Item* item, Reg
                break;
 
        case GDK_ENTER_NOTIFY:
-               if (event->crossing.detail != GDK_NOTIFY_INFERIOR) {
-                       set_entered_regionview (rv);
-                       ret = true;
-               }
+               set_entered_regionview (rv);
+               ret = enter_handler (item, event, RegionItem);
                break;
 
        case GDK_LEAVE_NOTIFY:
                if (event->crossing.detail != GDK_NOTIFY_INFERIOR) {
                        set_entered_regionview (0);
-                       ret = true;
+                       ret = leave_handler (item, event, RegionItem);
                }
                break;
 
@@ -287,6 +287,42 @@ Editor::canvas_region_view_event (GdkEvent *event, ArdourCanvas::Item* item, Reg
        return ret;
 }
 
+bool
+Editor::canvas_wave_view_event (GdkEvent *event, ArdourCanvas::Item* item, RegionView* rv)
+{
+       /* we only care about enter events here, required for mouse/cursor
+        * tracking. there is a non-linear (non-child/non-parent) relationship
+        * between various components of a regionview and so when we leave one
+        * of them (e.g. a trim handle) and enter another (e.g. the waveview)
+        * no other items get notified. enter/leave handling does not propagate
+        * in the same way as other events, so we need to catch this because
+        * entering (and leaving) the waveview is equivalent to
+        * entering/leaving the regionview (which is why it is passed in as a
+        * third argument).
+        *
+        * And in fact, we really only care about enter events.
+        */
+
+       bool ret = false;
+
+       if (!rv->sensitive ()) {
+               return false;
+       }
+
+       switch (event->type) {
+       case GDK_ENTER_NOTIFY:
+               set_entered_regionview (rv);
+               ret = enter_handler (item, event, WaveItem);
+               break;
+
+       default:
+               break;
+       }
+
+       return ret;
+}       
+
+
 bool
 Editor::canvas_stream_view_event (GdkEvent *event, ArdourCanvas::Item* item, RouteTimeAxisView *tv)
 {
@@ -313,11 +349,14 @@ Editor::canvas_stream_view_event (GdkEvent *event, ArdourCanvas::Item* item, Rou
 
        case GDK_ENTER_NOTIFY:
                set_entered_track (tv);
-               ret = true;
+               ret = enter_handler (item, event, StreamItem);
                break;
 
        case GDK_LEAVE_NOTIFY:
-               set_entered_track (0);
+               if (event->crossing.detail != GDK_NOTIFY_INFERIOR) {
+                       set_entered_track (0);
+               }
+               ret = leave_handler (item, event, StreamItem);
                break;
 
        default:
@@ -477,7 +516,7 @@ Editor::canvas_fade_in_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRe
 }
 
 bool
-Editor::canvas_fade_in_handle_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv)
+Editor::canvas_fade_in_handle_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv, bool trim)
 {
        bool ret = false;
 
@@ -493,11 +532,11 @@ Editor::canvas_fade_in_handle_event (GdkEvent *event, ArdourCanvas::Item* item,
                clicked_control_point = 0;
                clicked_axisview = &rv->get_time_axis_view();
                clicked_routeview = dynamic_cast<RouteTimeAxisView*>(clicked_axisview);
-               ret = button_press_handler (item, event, FadeInHandleItem);
+               ret = button_press_handler (item, event, trim ? FadeInTrimHandleItem : FadeInHandleItem);
                break;
 
        case GDK_BUTTON_RELEASE:
-               ret = button_release_handler (item, event, FadeInHandleItem);
+               ret = button_release_handler (item, event, trim ? FadeInTrimHandleItem : FadeInHandleItem);
                maybe_locate_with_edit_preroll ( rv->region()->position() );
                break;
 
@@ -506,11 +545,11 @@ Editor::canvas_fade_in_handle_event (GdkEvent *event, ArdourCanvas::Item* item,
                break;
 
        case GDK_ENTER_NOTIFY:
-               ret = enter_handler (item, event, FadeInHandleItem);
+               ret = enter_handler (item, event, trim ? FadeInTrimHandleItem : FadeInHandleItem);
                break;
 
        case GDK_LEAVE_NOTIFY:
-               ret = leave_handler (item, event, FadeInHandleItem);
+               ret = leave_handler (item, event, trim ? FadeInTrimHandleItem : FadeInHandleItem);
                break;
 
        default:
@@ -561,7 +600,7 @@ Editor::canvas_fade_out_event (GdkEvent *event, ArdourCanvas::Item* item, AudioR
 }
 
 bool
-Editor::canvas_fade_out_handle_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv)
+Editor::canvas_fade_out_handle_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv, bool trim)
 {
        bool ret = false;
 
@@ -577,11 +616,11 @@ Editor::canvas_fade_out_handle_event (GdkEvent *event, ArdourCanvas::Item* item,
                clicked_control_point = 0;
                clicked_axisview = &rv->get_time_axis_view();
                clicked_routeview = dynamic_cast<RouteTimeAxisView*>(clicked_axisview);
-               ret = button_press_handler (item, event, FadeOutHandleItem);
+               ret = button_press_handler (item, event, trim ? FadeOutTrimHandleItem : FadeOutHandleItem);
                break;
 
        case GDK_BUTTON_RELEASE:
-               ret = button_release_handler (item, event, FadeOutHandleItem);
+               ret = button_release_handler (item, event, trim ? FadeOutTrimHandleItem : FadeOutHandleItem);
                maybe_locate_with_edit_preroll ( rv->region()->last_frame() - rv->get_fade_out_shape_width() );
                break;
 
@@ -590,11 +629,11 @@ Editor::canvas_fade_out_handle_event (GdkEvent *event, ArdourCanvas::Item* item,
                break;
 
        case GDK_ENTER_NOTIFY:
-               ret = enter_handler (item, event, FadeOutHandleItem);
+               ret = enter_handler (item, event, trim ? FadeOutTrimHandleItem : FadeOutHandleItem);
                break;
 
        case GDK_LEAVE_NOTIFY:
-               ret = leave_handler (item, event, FadeOutHandleItem);
+               ret = leave_handler (item, event, trim ? FadeOutTrimHandleItem : FadeOutHandleItem);
                break;
 
        default:
@@ -975,6 +1014,59 @@ Editor::canvas_meter_marker_event (GdkEvent *event, ArdourCanvas::Item* item, Me
        return typed_event (item, event, MeterMarkerItem);
 }
 
+bool
+Editor::canvas_ruler_event (GdkEvent *event, ArdourCanvas::Item* item, ItemType type)
+{
+       framepos_t xdelta;
+       bool handled = false;
+
+       if (event->type == GDK_SCROLL) {
+               
+               /* scroll events in the rulers are handled a little differently from
+                  scrolling elsewhere in the canvas.
+               */
+
+               switch (event->scroll.direction) {
+               case GDK_SCROLL_UP:
+                       temporal_zoom_step (false);
+                       handled = true;
+                       break;
+                       
+               case GDK_SCROLL_DOWN:
+                       temporal_zoom_step (true);
+                       handled = true;
+                       break;
+                       
+               case GDK_SCROLL_LEFT:
+                       xdelta = (current_page_samples() / 2);
+                       if (leftmost_frame > xdelta) {
+                               reset_x_origin (leftmost_frame - xdelta);
+                       } else {
+                               reset_x_origin (0);
+                       }
+                       handled = true;
+                       break;
+                       
+               case GDK_SCROLL_RIGHT:
+                       xdelta = (current_page_samples() / 2);
+                       if (max_framepos - xdelta > leftmost_frame) {
+                               reset_x_origin (leftmost_frame + xdelta);
+                       } else {
+                               reset_x_origin (max_framepos - current_page_samples());
+                       }
+                       handled = true;
+                       break;
+                       
+               default:
+                       /* what? */
+                       break;
+               }
+               return handled;
+       }
+
+       return typed_event (item, event, type);
+}
+
 bool
 Editor::canvas_tempo_bar_event (GdkEvent *event, ArdourCanvas::Item* item)
 {
@@ -1010,9 +1102,34 @@ Editor::canvas_note_event (GdkEvent *event, ArdourCanvas::Item* item)
 }
 
 bool
-Editor::canvas_bottom_rect_event (GdkEvent* event)
+Editor::canvas_drop_zone_event (GdkEvent* event)
 {
-       cerr << "CBR event, type " << event << endl;
+       GdkEventScroll scroll;  
+       ArdourCanvas::Duple winpos;
+       
+       switch (event->type) {
+       case GDK_BUTTON_RELEASE:
+               if (event->button.button == 1) {
+                       selection->clear_objects ();
+                       selection->clear_tracks ();
+               }
+               break;
+
+       case GDK_SCROLL:
+               /* convert coordinates back into window space so that
+                  we can just call canvas_scroll_event().
+               */
+               winpos = _track_canvas->canvas_to_window (Duple (event->scroll.x, event->scroll.y));
+               scroll = event->scroll;
+               scroll.x = winpos.x;
+               scroll.y = winpos.y;
+               return canvas_scroll_event (&scroll, true);
+               break;
+
+       default:
+               break;
+       }
+
        return true;
 }
 
@@ -1123,7 +1240,7 @@ Editor::drop_regions (const Glib::RefPtr<Gdk::DragContext>& /*context*/,
 
        framepos_t const pos = window_event_sample (&event, &px, &py);
 
-       std::pair<TimeAxisView*, int> const tv = trackview_by_y_position (py);
+       std::pair<TimeAxisView*, int> const tv = trackview_by_y_position (py, false);
 
        if (tv.first != 0) {
 
index 1dd0d42a515ca062cc1ed428ced70a907dcd5b2c..ffe6fc0d015a8df9ef6d87ab73f1058926478f45 100644 (file)
@@ -22,8 +22,8 @@
 
 #include "canvas/canvas.h"
 #include "canvas/debug.h"
+#include "canvas/scroll_group.h"
 
-#include "utils.h"
 #include "editor_cursors.h"
 #include "editor.h"
 
@@ -33,26 +33,20 @@ using namespace Gtk;
 
 EditorCursor::EditorCursor (Editor& ed, bool (Editor::*callbck)(GdkEvent*,ArdourCanvas::Item*))
        : _editor (ed)
-       , _time_bars_canvas_item (new ArdourCanvas::Arrow (_editor._time_bars_canvas->root ()))
-       , _track_canvas_item (new ArdourCanvas::Line (_editor._track_canvas->root ()))
+       , _track_canvas_item (new ArdourCanvas::Arrow (_editor.get_hscroll_group()))
        , _length (1.0)
 {
-       CANVAS_DEBUG_NAME (_time_bars_canvas_item, "timebars editor cursor");
        CANVAS_DEBUG_NAME (_track_canvas_item, "track canvas editor cursor");
 
-       _time_bars_canvas_item->set_show_head (0, true);
-       _time_bars_canvas_item->set_head_height (0, 9);
-       _time_bars_canvas_item->set_head_width (0, 16);
-       _time_bars_canvas_item->set_head_outward (0, false);
-       _time_bars_canvas_item->set_show_head (1, false); // head only
-
-       _time_bars_canvas_item->set_data ("cursor", this);
+       _track_canvas_item->set_show_head (0, true);
+       _track_canvas_item->set_head_height (0, 9);
+       _track_canvas_item->set_head_width (0, 16);
+       _track_canvas_item->set_head_outward (0, false);
+       _track_canvas_item->set_show_head (1, false); // head only
        _track_canvas_item->set_data ("cursor", this);
 
-       _time_bars_canvas_item->Event.connect (sigc::bind (sigc::mem_fun (ed, callbck), _time_bars_canvas_item));
        _track_canvas_item->Event.connect (sigc::bind (sigc::mem_fun (ed, callbck), _track_canvas_item));
 
-       _time_bars_canvas_item->set_y1 (ArdourCanvas::COORD_MAX);
        _track_canvas_item->set_y1 (ArdourCanvas::COORD_MAX);
        
        _current_frame = 1; /* force redraw at 0 */
@@ -70,12 +64,8 @@ EditorCursor::set_position (framepos_t frame)
 
        double const new_pos = _editor.sample_to_pixel_unrounded (frame);
 
-       if (new_pos != _time_bars_canvas_item->x ()) {
-               _time_bars_canvas_item->set_x (new_pos);
-       }
-
-       if (new_pos != _track_canvas_item->x0 ()) {
-               _track_canvas_item->set_x (new_pos, new_pos);
+       if (new_pos != _track_canvas_item->x ()) {
+               _track_canvas_item->set_x (new_pos);
        }
 
        _current_frame = frame;
@@ -84,20 +74,23 @@ EditorCursor::set_position (framepos_t frame)
 void
 EditorCursor::show ()
 {
-       _time_bars_canvas_item->show ();
        _track_canvas_item->show ();
 }
 
 void
 EditorCursor::hide ()
 {
-       _time_bars_canvas_item->hide ();
        _track_canvas_item->hide ();
 }
 
 void
 EditorCursor::set_color (ArdourCanvas::Color color)
 {
-       _time_bars_canvas_item->set_color (color);
-       _track_canvas_item->set_outline_color (color);
+       _track_canvas_item->set_color (color);
+}
+
+void
+EditorCursor::set_sensitive (bool yn)
+{
+       _track_canvas_item->set_ignore_events (!yn);
 }
index 4a2d20e8f88833010c2289d35f077564a48e8a56..50958d1ac02235ca5745cd189f38a98f501d6b59 100644 (file)
@@ -18,6 +18,7 @@
 */
 
 #include "pbd/signals.h"
+#include "ardour/types.h"
 
 #include "canvas/arrow.h"
 #include "canvas/line.h"
@@ -36,25 +37,21 @@ class EditorCursor {
        void show ();
        void hide ();
        void set_color (ArdourCanvas::Color);
+       void set_sensitive (bool);
 
        framepos_t current_frame () const {
                return _current_frame;
        }
 
-       ArdourCanvas::Line& track_canvas_item () {
+       ArdourCanvas::Arrow& track_canvas_item () {
                return *_track_canvas_item;
        }
 
-       ArdourCanvas::Arrow& time_bar_canvas_item () {
-               return *_time_bars_canvas_item;
-       }
-
        PBD::Signal1<void, framepos_t> PositionChanged;
 
   private:     
        Editor&               _editor;
-       ArdourCanvas::Arrow*  _time_bars_canvas_item;
-       ArdourCanvas::Line*   _track_canvas_item;
+       ArdourCanvas::Arrow*  _track_canvas_item;
        framepos_t            _current_frame;
        double                _length;
 };
index 33a77fb27506ba336e6c39440ae533d79ef18d3a..9ad867bbcbbc8b425794341a045155f9c01ef325 100644 (file)
 
 #include "ardour/audioengine.h"
 #include "ardour/audioregion.h"
+#include "ardour/audio_track.h"
 #include "ardour/dB.h"
 #include "ardour/midi_region.h"
+#include "ardour/midi_track.h"
 #include "ardour/operations.h"
 #include "ardour/region_factory.h"
 #include "ardour/session.h"
 
+#include "canvas/scroll_group.h"
+
 #include "editor.h"
 #include "i18n.h"
 #include "keyboard.h"
@@ -46,7 +50,6 @@
 #include "ardour_ui.h"
 #include "gui_thread.h"
 #include "control_point.h"
-#include "utils.h"
 #include "region_gain_line.h"
 #include "editor_drag.h"
 #include "audio_time_axis.h"
@@ -173,28 +176,19 @@ DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
 {
        bool r = false;
 
-       _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
-
-       for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
-               bool const t = (*i)->motion_handler (e, from_autoscroll);
-               if (t) {
-                       r = true;
-               }
-
-       }
-
-       return r;
-}
-
-bool
-DragManager::window_motion_handler (GdkEvent* e, bool from_autoscroll)
-{
-       bool r = false;
+       /* calling this implies that we expect the event to have canvas
+        * coordinates 
+        *
+        * Can we guarantee that this is true?
+        */
 
        _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
 
        for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
                bool const t = (*i)->motion_handler (e, from_autoscroll);
+               /* run all handlers; return true if at least one of them
+                  returns true (indicating that the event has been handled).
+               */
                if (t) {
                        r = true;
                }
@@ -215,10 +209,11 @@ DragManager::have_item (ArdourCanvas::Item* i) const
        return j != _drags.end ();
 }
 
-Drag::Drag (Editor* e, ArdourCanvas::Item* i)
+Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
        : _editor (e)
        , _item (i)
        , _pointer_frame_offset (0)
+       , _trackview_only (trackview_only)
        , _move_threshold_passed (false)
        , _was_double_click (false)
        , _raw_grab_frame (0)
@@ -264,15 +259,19 @@ Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
        _grab_frame = adjusted_frame (_raw_grab_frame, event);
        _last_pointer_frame = _grab_frame;
        _last_pointer_x = _grab_x;
+
+       if (_trackview_only) {
+               _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
+       }
+
        _last_pointer_y = _grab_y;
 
        if (cursor == 0) {
                _item->grab ();
-                            
        } else {
                /* CAIROCANVAS need a variant here that passes *cursor */
                _item->grab ();
-
+               _editor->push_canvas_cursor (cursor);
        }
 
        if (_editor->session() && _editor->session()->transport_rolling()) {
@@ -309,6 +308,7 @@ Drag::end_grab (GdkEvent* event)
        finished (event, _move_threshold_passed);
 
        _editor->verbose_cursor()->hide ();
+       _editor->pop_canvas_cursor ();
 
        return _move_threshold_passed;
 }
@@ -335,13 +335,23 @@ Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
        return adjusted_frame (_drags->current_pointer_frame (), event, snap);
 }
 
+double
+Drag::current_pointer_y () const
+{
+       if (!_trackview_only) {
+               return _drags->current_pointer_y ();
+       }
+       
+       return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
+}
+
 bool
 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
 {
        /* check to see if we have moved in any way that matters since the last motion event */
        if (_move_threshold_passed &&
            (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
-           (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
+           (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
                return false;
        }
 
@@ -352,7 +362,7 @@ Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
        if (!from_autoscroll && !_move_threshold_passed) {
 
                bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
-               bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
+               bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
 
                _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
        }
@@ -368,13 +378,14 @@ Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
                                motion (event, _move_threshold_passed != old_move_threshold_passed);
                                
                                _last_pointer_x = _drags->current_pointer_x ();
-                               _last_pointer_y = _drags->current_pointer_y ();
+                               _last_pointer_y = current_pointer_y ();
                                _last_pointer_frame = adjusted_current_frame (event);
                        }
 
                        return true;
                }
        }
+
        return false;
 }
 
@@ -395,37 +406,22 @@ Drag::abort ()
 void
 Drag::show_verbose_cursor_time (framepos_t frame)
 {
-       _editor->verbose_cursor()->set_time (
-               frame,
-               _drags->current_pointer_x() + 10,
-               _drags->current_pointer_y() + 10
-               );
-
+       _editor->verbose_cursor()->set_time (frame);
        _editor->verbose_cursor()->show ();
 }
 
 void
-Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
+Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
 {
-       _editor->verbose_cursor()->show (xoffset);
-
-       _editor->verbose_cursor()->set_duration (
-               start, end,
-               _drags->current_pointer_x() + 10,
-               _drags->current_pointer_y() + 10
-               );
+       _editor->verbose_cursor()->set_duration (start, end);
+       _editor->verbose_cursor()->show ();
 }
 
 void
 Drag::show_verbose_cursor_text (string const & text)
 {
+       _editor->verbose_cursor()->set (text);
        _editor->verbose_cursor()->show ();
-
-       _editor->verbose_cursor()->set (
-               text,
-               _drags->current_pointer_x() + 10,
-               _drags->current_pointer_y() + 10
-               );
 }
 
 boost::shared_ptr<Region>
@@ -480,7 +476,7 @@ RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<Re
         */
 
        for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
-               _views.push_back (DraggingView (*i, this));
+               _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
        }
 
        RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
@@ -535,7 +531,7 @@ RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 
        show_verbose_cursor_time (_last_frame_position);
 
-       pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
+       pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
        if (tv.first) {
                _last_pointer_time_axis_view = find_time_axis_view (tv.first);
                _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
@@ -640,46 +636,47 @@ RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
 void
 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
 {
-       assert (!_views.empty ());
+       double delta_layer = 0;
+       int delta_time_axis_view = 0;
 
-       /* Find the TimeAxisView that the pointer is now over */
-       pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
+       assert (!_views.empty ());
 
-       /* Bail early if we're not over a track */
-       RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
+       /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
 
-       if (!rtv || !rtv->is_track()) {
-               _editor->verbose_cursor()->hide ();
-               return;
-       }
+       /* Find the TimeAxisView that the pointer is now over */
+       pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
+       TimeAxisView* tv = r.first;
 
-       if (first_move && tv.first->view()->layer_display() == Stacked) {
-               tv.first->view()->set_layer_display (Expanded);
-       }
+       if (tv && tv->view()) {
+               double layer = r.second;
 
-       /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
+               if (first_move && tv->view()->layer_display() == Stacked) {
+                       tv->view()->set_layer_display (Expanded);
+               }
 
-       /* Here's the current pointer position in terms of time axis view and layer */
-       int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
-       double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
+               /* Here's the current pointer position in terms of time axis view and layer */
+               int const current_pointer_time_axis_view = find_time_axis_view (tv);
+               double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
+               
+               /* Work out the change in y */
 
+               delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
+               delta_layer = current_pointer_layer - _last_pointer_layer;
+       } 
+       
        /* Work out the change in x */
        framepos_t pending_region_position;
        double const x_delta = compute_x_delta (event, &pending_region_position);
        _last_frame_position = pending_region_position;
 
-       /* Work out the change in y */
-
-       int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
-       double delta_layer = current_pointer_layer - _last_pointer_layer;
-
+       /* Verify change in y */
        if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
                /* this y movement is not allowed, so do no y movement this time */
                delta_time_axis_view = 0;
                delta_layer = 0;
        }
 
-       if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
+       if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
                /* haven't reached next snap point, and we're not switching
                   trackviews nor layers. nothing to do.
                */
@@ -697,22 +694,20 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
                }
 
                if (first_move) {
-
                        rv->drag_start (); 
 
-                       /* Reparent to a non scrolling group so that we can keep the
-                          region selection above all time axis views.
-                          Reparenting means that we will have to move the region view
-                          within its new parent, as the two parent groups have different coordinates.
+                       /* reparent the regionview into a group above all
+                        * others
+                        */
+                       
+                       ArdourCanvas::Item* rvg = rv->get_canvas_group();
+                        Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
+                        Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
+                        rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
+                       /* move the item so that it continues to appear at the
+                          same location now that its parent has changed.
                        */
-
-                       ArdourCanvas::Group* rvg = rv->get_canvas_group();
-                       Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
-
-                       rv->get_canvas_group()->reparent (_editor->_region_motion_group);
-
-                       rv->fake_set_opaque (true);
-                       rvg->set_position (rv_canvas_offset);
+                       rvg->move (rv_canvas_offset - dmg_canvas_offset);
                }
 
                /* If we have moved tracks, we'll fudge the layer delta so that the
@@ -725,61 +720,100 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
                        this_delta_layer = - i->layer;
                }
 
-               /* The TimeAxisView that this region is now on */
-               TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
+               if (tv) {
 
-               /* Ensure it is moved from stacked -> expanded if appropriate */
-               if (tv->view()->layer_display() == Stacked) {
-                       tv->view()->set_layer_display (Expanded);
-               }
+                       int track_index;
+                       
+                       if (i->time_axis_view >= 0) {
+                               track_index = i->time_axis_view + delta_time_axis_view;
+                       } else {
+                               track_index = _time_axis_views.size() - 1 + delta_time_axis_view;
+                       }
+                        
+                       if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
+                               continue;
+                       }
+
+                       /* The TimeAxisView that this region is now over */
+                       TimeAxisView* current_tv = _time_axis_views[track_index];
+
+                       /* Ensure it is moved from stacked -> expanded if appropriate */
+                       if (current_tv->view()->layer_display() == Stacked) {
+                               current_tv->view()->set_layer_display (Expanded);
+                       }
                
-               /* We're only allowed to go -ve in layer on Expanded views */
-               if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
-                       this_delta_layer = - i->layer;
-               }
+                       /* We're only allowed to go -ve in layer on Expanded views */
+                       if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
+                               this_delta_layer = - i->layer;
+                       }
                
-               /* Set height */
-               rv->set_height (tv->view()->child_height ());
+                       /* Set height */
+                       rv->set_height (current_tv->view()->child_height ());
                
-               /* Update show/hidden status as the region view may have come from a hidden track,
-                  or have moved to one.
-               */
-               if (tv->hidden ()) {
-                       rv->get_canvas_group()->hide ();
-               } else {
-                       rv->get_canvas_group()->show ();
-               }
+                       /* Update show/hidden status as the region view may have come from a hidden track,
+                          or have moved to one.
+                       */
+                       if (current_tv->hidden ()) {
+                               rv->get_canvas_group()->hide ();
+                       } else {
+                               rv->get_canvas_group()->show ();
+                       }
 
-               /* Update the DraggingView */
-               i->time_axis_view += delta_time_axis_view;
-               i->layer += this_delta_layer;
+                       /* Update the DraggingView */
+                       i->time_axis_view = track_index;
+                       i->layer += this_delta_layer;
 
-               if (_brushing) {
-                       _editor->mouse_brush_insert_region (rv, pending_region_position);
-               } else {
-                       double x = 0;
-                       double y = 0;
+                       if (_brushing) {
+                               _editor->mouse_brush_insert_region (rv, pending_region_position);
+                       } else {
+                               Duple track_origin;
 
-                       /* Get the y coordinate of the top of the track that this region is now on */
-                       tv->canvas_display()->item_to_canvas (x, y);
+                               /* Get the y coordinate of the top of the track that this region is now over */
+                               track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
+                               
+                               /* And adjust for the layer that it should be on */
+                               StreamView* cv = current_tv->view ();
+                               switch (cv->layer_display ()) {
+                               case Overlaid:
+                                       break;
+                               case Stacked:
+                                       track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
+                                       break;
+                               case Expanded:
+                                       track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
+                                       break;
+                               }
 
-                       /* And adjust for the layer that it should be on */
-                       StreamView* cv = tv->view ();
-                       switch (cv->layer_display ()) {
-                       case Overlaid:
-                               break;
-                       case Stacked:
-                               y += (cv->layers() - i->layer - 1) * cv->child_height ();
-                               break;
-                       case Expanded:
-                               y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
-                               break;
+                               /* need to get the parent of the regionview
+                                * canvas group and get its position in
+                                * equivalent coordinate space as the trackview
+                                * we are now dragging over.
+                                */
+                               
+                               /* Now move the region view */
+                               rv->move (x_delta, track_origin.y - rv->get_canvas_group()->canvas_origin().y);
                        }
+               } else {
 
-                       /* Now move the region view */
-                       rv->move (x_delta, y - rv->get_canvas_group()->position().y);
-               }
+                       /* Only move the region into the empty dropzone at the bottom if the pointer
+                        * is down there.
+                        */
+
+                       if (current_pointer_y() >= 0) {
+                               
+                               Coord last_track_bottom_edge;
+                               if (!_time_axis_views.empty()) {
+                                       TimeAxisView* last = _time_axis_views.back();
+                                       last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
+                               } else {
+                                       last_track_bottom_edge = 0;
+                               }
 
+                               rv->move (x_delta, last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y);
+                               i->time_axis_view = -1;
+                       }
+               }
+               
        } /* foreach region */
 
        _total_x_delta += x_delta;
@@ -826,7 +860,7 @@ RegionMoveDrag::motion (GdkEvent* event, bool first_move)
                        }
 
                        nrv->get_canvas_group()->show ();
-                       new_regionviews.push_back (DraggingView (nrv, this));
+                       new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
 
                        /* swap _primary to the copy */
 
@@ -927,12 +961,44 @@ RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
        _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
 }
 
+RouteTimeAxisView*
+RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
+{                      
+       /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
+          new track.
+        */
+                       
+       try {
+               if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
+                       list<boost::shared_ptr<AudioTrack> > audio_tracks;
+                       audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
+                       RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
+                       if (rtav) {
+                               rtav->set_height (original->current_height());
+                       }
+                       return rtav;
+               } else {
+                       ChanCount one_midi_port (DataType::MIDI, 1);
+                       list<boost::shared_ptr<MidiTrack> > midi_tracks;
+                       midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
+                       RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
+                       if (rtav) {
+                               rtav->set_height (original->current_height());
+                       }
+                       return rtav;
+               }                                               
+       } catch (...) {
+               error << _("Could not create new track after region placed in the drop zone") << endmsg;
+               return 0;
+       }
+}
+
 void
 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
 {
        RegionSelection new_views;
        PlaylistSet modified_playlists;
-       list<RegionView*> views_to_delete;
+       RouteTimeAxisView* new_time_axis_view = 0;      
 
        if (_brushing) {
                /* all changes were made during motion event handlers */
@@ -952,7 +1018,9 @@ RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed
        }
 
        /* insert the regions into their new playlists */
-       for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+       for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
+
+               RouteTimeAxisView* dest_rtv = 0;
 
                if (i->view->region()->locked() || i->view->region()->video_locked()) {
                        continue;
@@ -965,27 +1033,31 @@ RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed
                } else {
                        where = i->view->region()->position();
                }
-
-               RegionView* new_view = insert_region_into_playlist (
-                       i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
-                       );
-
-               if (new_view == 0) {
-                       continue;
+               
+               if (i->time_axis_view < 0) {
+                       if (!new_time_axis_view) {
+                               new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
+                       }
+                       dest_rtv = new_time_axis_view;
+               } else {
+                       dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
+               }               
+               
+               if (dest_rtv != 0) {
+                       RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
+                       if (new_view != 0) {
+                               new_views.push_back (new_view);
+                       }
                }
+       
+               /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
+                  since deletion will automagically remove it from _views, thus invalidating i as an iterator.
+                */
 
-               new_views.push_back (new_view);
-
-               /* we don't need the copied RegionView any more */
-               views_to_delete.push_back (i->view);
-       }
-
-       /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
-          because when views are deleted they are automagically removed from _views, which messes
-          up the iteration.
-       */
-       for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
-               delete *i;
+               list<DraggingView>::const_iterator next = i;
+               ++next;
+               delete i->view;
+               i = next;
        }
 
        /* If we've created new regions either by copying or moving
@@ -1013,6 +1085,7 @@ RegionMoveDrag::finished_no_copy (
        PlaylistSet modified_playlists;
        PlaylistSet frozen_playlists;
        set<RouteTimeAxisView*> views_to_update;
+       RouteTimeAxisView* new_time_axis_view = 0;
 
        if (_brushing) {
                /* all changes were made during motion event handlers */
@@ -1029,15 +1102,26 @@ RegionMoveDrag::finished_no_copy (
        for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
 
                RegionView* rv = i->view;
-
-               RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
-               double const dest_layer = i->layer;
+               RouteTimeAxisView* dest_rtv = 0;
 
                if (rv->region()->locked() || rv->region()->video_locked()) {
                        ++i;
                        continue;
                }
+               
+               if (i->time_axis_view < 0) {
+                       if (!new_time_axis_view) {
+                               new_time_axis_view = create_destination_time_axis (rv->region(), i->initial_time_axis_view);
+                       }
+                       dest_rtv = new_time_axis_view;
+               } else {
+                       dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
+               }
+                       
+               assert (dest_rtv);
 
+               double const dest_layer = i->layer;
+               
                views_to_update.insert (dest_rtv);
 
                framepos_t where;
@@ -1071,7 +1155,7 @@ RegionMoveDrag::finished_no_copy (
                           visible.
                        */
                        rv->hide_region_editor();
-                       rv->fake_set_opaque (false);
+
 
                        remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
 
@@ -1306,7 +1390,6 @@ RegionMotionDrag::aborted (bool)
                rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
                rv->get_canvas_group()->set_y_position (0);
                rv->drag_end ();
-               rv->fake_set_opaque (false);
                rv->move (-_total_x_delta, 0);
                rv->set_height (rtv->view()->child_height ());
        }
@@ -1348,7 +1431,7 @@ RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, Rout
 
        _primary->get_canvas_group()->show ();
        _primary->set_position (pos, 0);
-       _views.push_back (DraggingView (_primary, this));
+       _views.push_back (DraggingView (_primary, this, v));
 
        _last_frame_position = pos;
 
@@ -1407,7 +1490,7 @@ RegionSpliceDrag::motion (GdkEvent* event, bool)
 {
        /* Which trackview is this ? */
 
-       pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
+       pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
        RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
 
        /* The region motion is only processed if the pointer is over
@@ -1420,6 +1503,8 @@ RegionSpliceDrag::motion (GdkEvent* event, bool)
                */
                _editor->verbose_cursor()->hide ();
                return;
+       } else {
+               _editor->verbose_cursor()->show ();
        }
 
        int dir;
@@ -1503,11 +1588,20 @@ RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, co
                                (*i)->drag_start();
                                ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
                                Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
-                               rvg->reparent (_editor->_region_motion_group);
-                               (*i)->fake_set_opaque (true);
-                               rvg->set_position (rv_canvas_offset);
+                               Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
+                               rvg->reparent (_editor->_drag_motion_group);
+
+                               // XXX without the following, things jump in the y direction during drags
+                               // with it, they jump in the x direction
+                               // so we need to do the move in the y direction only
+                               // rvg->move (rv_canvas_offset - dmg_canvas_offset);
+                               std::cerr << "rv_canvas_offset = " << rv_canvas_offset << ", dmg_canvas_offset = " << dmg_canvas_offset << std::endl;
+                               Duple fudge = rv_canvas_offset - dmg_canvas_offset;
+                               fudge.x = 0;
+                               rvg->move (fudge);
+
                        }
-                       _views.push_back (DraggingView (*i, this));
+                       _views.push_back (DraggingView (*i, this, tav));
                }
        }
 }
@@ -1619,7 +1713,7 @@ RegionRippleDrag::motion (GdkEvent* event, bool first_move)
 {
        /* Which trackview is this ? */
 
-       pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
+       pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
        RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
 
        /* The region motion is only processed if the pointer is over
@@ -1957,8 +2051,7 @@ VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
        Timecode::Time timecode;
        _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
        snprintf (buf, sizeof (buf), "Video Start:\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, (_startdrag_video_offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
-       _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
-       _editor->verbose_cursor()->show ();
+       show_verbose_cursor_text (buf);
 }
 
 void
@@ -1986,7 +2079,6 @@ VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
                DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
                if (first_move) {
                        rv->drag_start ();
-                       rv->fake_set_opaque (true);
                        rv->region()->clear_changes ();
                        rv->region()->suspend_property_changes();
                }
@@ -2008,8 +2100,7 @@ VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
                        , _("Diff:"),
                                (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
                                );
-       _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
-       _editor->verbose_cursor()->show ();
+       show_verbose_cursor_text (buf);
 }
 
 void
@@ -2034,7 +2125,6 @@ VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
 
        for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
                i->view->drag_end();
-               i->view->fake_set_opaque (false);
                i->view->region()->resume_property_changes ();
 
                _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
@@ -2098,11 +2188,20 @@ TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
                if (pf < (region_start + region_length/2)) {
                        /* closer to front */
                        _operation = StartTrim;
-                       Drag::start_grab (event, _editor->cursors()->left_side_trim);
+
+                       if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
+                               Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
+                       } else {
+                               Drag::start_grab (event, _editor->cursors()->left_side_trim);
+                       }
                } else {
                        /* closer to end */
                        _operation = EndTrim;
-                       Drag::start_grab (event, _editor->cursors()->right_side_trim);
+                       if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
+                               Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
+                       } else {
+                               Drag::start_grab (event, _editor->cursors()->right_side_trim);
+                       }
                }
        }
 
@@ -2145,7 +2244,7 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
                speed = tv->track()->speed();
        }
 
-       framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
+       framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
 
        if (first_move) {
 
@@ -2161,13 +2260,15 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
                case ContentsTrim:
                        trim_type = "Region content trim";
                        break;
+               default:
+                       assert(0);
+                       break;
                }
 
                _editor->begin_reversible_command (trim_type);
 
                for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
                        RegionView* rv = i->view;
-                       rv->fake_set_opaque (false);
                        rv->enable_display (false);
                        rv->region()->playlist()->clear_owned_changes ();
 
@@ -2193,42 +2294,67 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
                non_overlap_trim = true;
        }
 
+       /* contstrain trim to fade length */
+       if (_preserve_fade_anchor) {
+               switch (_operation) {
+                       case StartTrim:
+                               for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+                                       AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
+                                       if (!arv) continue;
+                                       boost::shared_ptr<AudioRegion> ar (arv->audio_region());
+                                       if (ar->locked()) continue;
+                                       framecnt_t len = ar->fade_in()->back()->when;
+                                       if (len < dt) dt = min(dt, len);
+                               }
+                               break;
+                       case EndTrim:
+                               for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+                                       AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
+                                       if (!arv) continue;
+                                       boost::shared_ptr<AudioRegion> ar (arv->audio_region());
+                                       if (ar->locked()) continue;
+                                       framecnt_t len = ar->fade_out()->back()->when;
+                                       if (len < -dt) dt = max(dt, -len);
+                               }
+                               break;
+                       case ContentsTrim:
+                               break;
+               }
+       }
+
+
        switch (_operation) {
        case StartTrim:
-               for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+               for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
                        bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
                        if (changed && _preserve_fade_anchor) {
                                AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
                                if (arv) {
-                                       double distance;
-                                       double new_length;
-                                       framecnt_t len;
                                        boost::shared_ptr<AudioRegion> ar (arv->audio_region());
-                                       distance = _drags->current_pointer_x() - grab_x();
-                                       len = ar->fade_in()->back()->when;
-                                       new_length = len - _editor->pixel_to_sample (distance);
-                                       new_length = ar->verify_xfade_bounds (new_length, true  /*START*/ );
-                                       arv->reset_fade_in_shape_width (ar, new_length);  //the grey shape
+                                       framecnt_t len = ar->fade_in()->back()->when;
+                                       framecnt_t diff = ar->first_frame() - i->initial_position;
+                                       framepos_t new_length = len - diff;
+                                       i->anchored_fade_length = min (ar->length(), new_length);
+                                       //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true  /*START*/ );
+                                       arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
                                }
                        }
                }
                break;
 
        case EndTrim:
-               for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+               for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
                        bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
                        if (changed && _preserve_fade_anchor) {
                                AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
                                if (arv) {
-                                       double distance;
-                                       double new_length;
-                                       framecnt_t len;
                                        boost::shared_ptr<AudioRegion> ar (arv->audio_region());
-                                       distance = grab_x() - _drags->current_pointer_x();
-                                       len = ar->fade_out()->back()->when;
-                                       new_length = len - _editor->pixel_to_sample (distance);
-                                       new_length = ar->verify_xfade_bounds (new_length, false  /*END*/ );
-                                       arv->reset_fade_out_shape_width (ar, new_length);  //the grey shape
+                                       framecnt_t len = ar->fade_out()->back()->when;
+                                       framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
+                                       framepos_t new_length = len + diff;
+                                       i->anchored_fade_length = min (ar->length(), new_length);
+                                       //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false  /*END*/ );
+                                       arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
                                }
                        }
                }
@@ -2277,15 +2403,10 @@ TrimDrag::finished (GdkEvent* event, bool movement_occurred)
                                if (_preserve_fade_anchor) {
                                        AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
                                        if (arv) {
-                                               double distance;
-                                               double new_length;
-                                               framecnt_t len;
                                                boost::shared_ptr<AudioRegion> ar (arv->audio_region());
-                                               distance = _drags->current_pointer_x() - grab_x();
-                                               len = ar->fade_in()->back()->when;
-                                               new_length = len - _editor->pixel_to_sample (distance);
-                                               new_length = ar->verify_xfade_bounds (new_length, true  /*START*/ );
-                                               ar->set_fade_in_length(new_length);
+                                               arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
+                                               ar->set_fade_in_length(i->anchored_fade_length);
+                                               ar->set_fade_in_active(true);
                                        }
                                }
                                if (_jump_position_when_done) {
@@ -2297,15 +2418,10 @@ TrimDrag::finished (GdkEvent* event, bool movement_occurred)
                                if (_preserve_fade_anchor) {
                                        AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
                                        if (arv) {
-                                               double distance;
-                                               double new_length;
-                                               framecnt_t len;
                                                boost::shared_ptr<AudioRegion> ar (arv->audio_region());
-                                               distance = _drags->current_pointer_x() - grab_x();
-                                               len = ar->fade_out()->back()->when;
-                                               new_length = len - _editor->pixel_to_sample (distance);
-                                               new_length = ar->verify_xfade_bounds (new_length, false  /*END*/ );
-                                               ar->set_fade_out_length(new_length);
+                                               arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
+                                               ar->set_fade_out_length(i->anchored_fade_length);
+                                               ar->set_fade_out_active(true);
                                        }
                                }
                                if (_jump_position_when_done) {
@@ -2335,7 +2451,6 @@ TrimDrag::finished (GdkEvent* event, bool movement_occurred)
                        for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
                                 i->view->thaw_after_trim ();
                                i->view->enable_display (true);
-                               i->view->fake_set_opaque (true);
 
                                /* Trimming one region may affect others on the playlist, so we need
                                   to get undo Commands from the whole playlist rather than just the
@@ -2675,7 +2790,7 @@ TempoMarkerDrag::aborted (bool moved)
 }
 
 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
-       : Drag (e, &c.time_bar_canvas_item())
+       : Drag (e, &c.track_canvas_item(), false)
        , _cursor (c)
        , _stop (s)
 {
@@ -3424,10 +3539,7 @@ ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
 
        _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
 
-       _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
-                                       event->button.x + 10, event->button.y + 10);
-
-       _editor->verbose_cursor()->show ();
+       show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
 
        _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
 
@@ -3440,7 +3552,7 @@ void
 ControlPointDrag::motion (GdkEvent* event, bool)
 {
        double dx = _drags->current_pointer_x() - last_pointer_x();
-       double dy = _drags->current_pointer_y() - last_pointer_y();
+       double dy = current_pointer_y() - last_pointer_y();
 
        if (event->button.state & Keyboard::SecondaryModifier) {
                dx *= 0.1;
@@ -3487,7 +3599,7 @@ ControlPointDrag::motion (GdkEvent* event, bool)
 
        _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
 
-       _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
+       show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
 }
 
 void
@@ -3573,16 +3685,13 @@ LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
 
        _line->start_drag_line (before, after, fraction);
 
-       _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
-                                       event->button.x + 10, event->button.y + 10);
-
-       _editor->verbose_cursor()->show ();
+       show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
 }
 
 void
 LineDrag::motion (GdkEvent* event, bool)
 {
-       double dy = _drags->current_pointer_y() - last_pointer_y();
+       double dy = current_pointer_y() - last_pointer_y();
 
        if (event->button.state & Keyboard::SecondaryModifier) {
                dy *= 0.1;
@@ -3601,7 +3710,7 @@ LineDrag::motion (GdkEvent* event, bool)
        /* we are ignoring x position for this drag, so we can just pass in anything */
        _line->drag_motion (0, fraction, true, false, ignored);
 
-       _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
+       show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
 }
 
 void
@@ -3747,43 +3856,50 @@ RubberbandSelectDrag::motion (GdkEvent* event, bool)
                start = grab;
        }
 
-       if (_drags->current_pointer_y() < grab_y()) {
-               y1 = _drags->current_pointer_y();
+       if (current_pointer_y() < grab_y()) {
+               y1 = current_pointer_y();
                y2 = grab_y();
        } else {
-               y2 = _drags->current_pointer_y();
+               y2 = current_pointer_y();
                y1 = grab_y();
        }
 
-
        if (start != end || y1 != y2) {
 
                double x1 = _editor->sample_to_pixel (start);
                double x2 = _editor->sample_to_pixel (end);
                const double min_dimension = 2.0;
 
-               _editor->rubberband_rect->set_x0 (x1);
                if (_vertical_only) {
                        /* fixed 10 pixel width */
-                       _editor->rubberband_rect->set_x1 (x1 + 10);
+                       x2 = x1 + 10;
                } else {
                        if (x2 < x1) {
                                x2 = min (x1 - min_dimension, x2);
                        } else {
                                x2 = max (x1 + min_dimension, x2);
                        }
-                       _editor->rubberband_rect->set_x1 (x2);
                } 
 
-               _editor->rubberband_rect->set_y0 (y1);
                if (y2 < y1) {
                        y2 = min (y1 - min_dimension, y2);
                } else {
                        y2 = max (y1 + min_dimension, y2);
                }
 
-               _editor->rubberband_rect->set_y1 (y2);
-               
+               /* translate rect into item space and set */
+
+                ArdourCanvas::Rect r (x1, y1, x2, y2);
+
+               /* this drag is a _trackview_only == true drag, so the y1 and
+                * y2 (computed using current_pointer_y() and grab_y()) will be
+                * relative to the top of the trackview group). The
+                * rubberband rect has the same parent/scroll offset as the
+                * the trackview group, so we can use the "r" rect directly
+                * to set the shape of the rubberband.
+                */
+
+               _editor->rubberband_rect->set (r);
                _editor->rubberband_rect->show();
                _editor->rubberband_rect->raise_to_top();
 
@@ -3810,11 +3926,11 @@ RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
        double y1;
        double y2;
        
-       if (_drags->current_pointer_y() < grab_y()) {
-               y1 = _drags->current_pointer_y();
+       if (current_pointer_y() < grab_y()) {
+               y1 = current_pointer_y();
                y2 = grab_y();
        } else {
-               y2 = _drags->current_pointer_y();
+               y2 = current_pointer_y();
                y1 = grab_y();
        }
 
@@ -4042,7 +4158,7 @@ SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
                show_verbose_cursor_time (adjusted_current_frame (event));
        }
 
-       _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
+       _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
 }
 
 void
@@ -4133,25 +4249,31 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
                TrackViewList to_be_removed_from_selection;
                TrackViewList& all_tracks (_editor->track_views);
 
-               for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
+               ArdourCanvas::Coord const top = grab_y();
+               ArdourCanvas::Coord const bottom = current_pointer_y();
+
+               if (top >= 0 && bottom >= 0) {
+
+                       for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
                        
-                       if ((*i)->covered_by_y_range (grab_y(), _drags->current_pointer_y())) {
-                               if (!(*i)->get_selected()) {
-                                       to_be_added_to_selection.push_back (*i);
-                               }
-                       } else {
-                               if ((*i)->get_selected()) {
-                                       to_be_removed_from_selection.push_back (*i);
+                               if ((*i)->covered_by_y_range (top, bottom)) {
+                                       if (!(*i)->get_selected()) {
+                                               to_be_added_to_selection.push_back (*i);
+                                       }
+                               } else {
+                                       if ((*i)->get_selected()) {
+                                               to_be_removed_from_selection.push_back (*i);
+                                       }
                                }
                        }
-               }
 
-               if (!to_be_added_to_selection.empty()) {
-                       _editor->selection->add (to_be_added_to_selection);
-               }
-
-               if (!to_be_removed_from_selection.empty()) {
-                       _editor->selection->remove (to_be_removed_from_selection);
+                       if (!to_be_added_to_selection.empty()) {
+                               _editor->selection->add (to_be_added_to_selection);
+                       }
+                       
+                       if (!to_be_removed_from_selection.empty()) {
+                               _editor->selection->remove (to_be_removed_from_selection);
+                       }
                }
        }
        break;
@@ -4199,8 +4321,6 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
                break;
        }
 
-       _editor->maybe_autoscroll (true, false, false);
-
        if (start != end) {
                switch (_operation) {
                case SelectionMove:     
@@ -4287,7 +4407,7 @@ SelectionDrag::aborted (bool)
 }
 
 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
-       : Drag (e, i),
+       : Drag (e, i, false),
          _operation (o),
          _copy (false)
 {
@@ -4388,8 +4508,6 @@ RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
                }
        }
 
-       _editor->maybe_autoscroll (true, false, false);
-
        if (start != end) {
                _editor->temp_location->set (start, end);
 
@@ -4665,7 +4783,7 @@ NoteDrag::total_dy () const
        MidiStreamView* msv = _region->midi_stream_view ();
        double const y = _region->midi_view()->y_position ();
        /* new current note */
-       uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
+       uint8_t n = msv->y_to_note (current_pointer_y () - y);
        /* clamp */
        n = max (msv->lowest_note(), n);
        n = min (msv->highest_note(), n);
@@ -4823,7 +4941,7 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        /* Get line states before we start changing things */
        for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
                i->state = &i->line->get_state ();
-               i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
+               i->original_fraction = y_fraction (i->line, current_pointer_y());
        }
 
        if (_ranges.empty()) {
@@ -4929,7 +5047,7 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        }
 
        for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
-               i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
+               i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
        }
 }
 
@@ -4941,7 +5059,7 @@ AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
        }
 
        for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
-               float const f = y_fraction (l->line, _drags->current_pointer_y());
+               float const f = y_fraction (l->line, current_pointer_y());
                /* we are ignoring x position for this drag, so we can just pass in anything */
                uint32_t ignored;
                l->line->drag_motion (0, f, true, false, ignored);
@@ -4972,9 +5090,13 @@ AutomationRangeDrag::aborted (bool)
        }
 }
 
-DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
+DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
        : view (v)
+       , initial_time_axis_view (itav)
 {
+       /* note that time_axis_view may be null if the regionview was created
+        * as part of a copy operation.
+        */
        time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
        layer = v->region()->layer ();
        initial_y = v->get_canvas_group()->position().y;
index 48e08d572079b85f50bf09bf24e9075176b92ad6..d9eda5685a0b4f5ef95e296dad1aa645d6d93582 100644 (file)
@@ -54,7 +54,6 @@ public:
        ~DragManager ();
 
        bool motion_handler (GdkEvent *, bool);
-       bool window_motion_handler (GdkEvent *, bool);
 
        void abort ();
        void add (Drag *);
@@ -103,7 +102,7 @@ private:
 class Drag
 {
 public:
-        Drag (Editor *, ArdourCanvas::Item *);
+        Drag (Editor *, ArdourCanvas::Item *, bool trackview_only = true);
        virtual ~Drag () {}
 
        void set_manager (DragManager* m) {
@@ -212,6 +211,8 @@ protected:
                return _last_pointer_frame;
        }
 
+       double current_pointer_y () const;
+
        boost::shared_ptr<ARDOUR::Region> add_midi_region (MidiTimeAxisView*);
 
        void show_verbose_cursor_time (framepos_t);
@@ -228,11 +229,11 @@ protected:
        bool _was_rolling; ///< true if the session was rolling before the drag started, otherwise false
 
 private:
-
+       bool _trackview_only; ///< true if pointer y value should always be relative to the top of the trackview group
        bool _move_threshold_passed; ///< true if the move threshold has been passed, otherwise false
         bool _was_double_click; ///< true if drag initiated by a double click event
        double _grab_x; ///< trackview x of the grab start position
-       double _grab_y; ///< trackview y of the grab start position
+       double _grab_y; ///< y of the grab start position, possibly adjusted if _trackview_only is true
        double _last_pointer_x; ///< trackview x of the pointer last time a motion occurred
        double _last_pointer_y; ///< trackview y of the pointer last time a motion occurred
        ARDOUR::framepos_t _raw_grab_frame; ///< unsnapped frame that the mouse was at when start_grab was called, or 0
@@ -246,7 +247,7 @@ class RegionDrag;
 class DraggingView
 {
 public:
-       DraggingView (RegionView *, RegionDrag *);
+       DraggingView (RegionView *, RegionDrag *, TimeAxisView* original_tav);
 
        RegionView* view; ///< the view
        /** index into RegionDrag::_time_axis_views of the view that this region is currently being displayed on,
@@ -261,7 +262,9 @@ public:
        double initial_y; ///< the initial y position of the view before any reparenting
        framepos_t initial_position; ///< initial position of the region
        framepos_t initial_end; ///< initial end position of the region
+       framepos_t anchored_fade_length; ///< fade_length when anchored during drag
        boost::shared_ptr<ARDOUR::Playlist> initial_playlist;
+       TimeAxisView* initial_time_axis_view;
 };
 
 /** Abstract base class for drags that involve region(s) */
@@ -379,6 +382,7 @@ private:
 
 
        void collect_new_region_view (RegionView *);
+       RouteTimeAxisView* create_destination_time_axis (boost::shared_ptr<ARDOUR::Region>, TimeAxisView* original);
 
        bool _copy;
        RegionView* _new_region_view;
index d02181c9b6c56b8f715a87d2c9728fe783edbebe..fff113a7e200b5fd1ceea96b04bfa4fe3e22d2ad 100644 (file)
 #include "gtkmm2ext/utils.h"
 
 #include "ardour/route_group.h"
-#include "editor_group_tabs.h"
+
+#include "canvas/utils.h"
+
+#include "ardour_ui.h"
 #include "editor.h"
-#include "route_time_axis.h"
-#include "utils.h"
+#include "editor_group_tabs.h"
 #include "editor_route_groups.h"
 #include "editor_routes.h"
+#include "rgb_macros.h"
+#include "route_time_axis.h"
+#include "utils.h"
+
 #include "i18n.h"
 
 using namespace std;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 
 EditorGroupTabs::EditorGroupTabs (Editor* e)
        : EditorComponent (e)
@@ -83,13 +90,17 @@ void
 EditorGroupTabs::draw_tab (cairo_t* cr, Tab const & tab) const
 {
        double const arc_radius = get_width();
-
+       double r, g, b, a;
+       
        if (tab.group && tab.group->is_active()) {
-               cairo_set_source_rgba (cr, tab.color.get_red_p (), tab.color.get_green_p (), tab.color.get_blue_p (), 1);
+               ArdourCanvas::color_to_rgba (tab.color, r, g, b, a);
        } else {
-               cairo_set_source_rgba (cr, 1, 1, 1, 0.2);
+               ArdourCanvas::color_to_rgba (ARDOUR_UI::config()->get_canvasvar_InactiveGroupTab(), r, g, b, a);
        }
 
+       a = 1.0;
+
+       cairo_set_source_rgba (cr, r, g, b, a);
        cairo_move_to (cr, 0, tab.from + arc_radius);
        cairo_arc (cr, get_width(), tab.from + arc_radius, arc_radius, M_PI, 3 * M_PI / 2);
        cairo_line_to (cr, get_width(), tab.to);
@@ -103,7 +114,10 @@ EditorGroupTabs::draw_tab (cairo_t* cr, Tab const & tab) const
                cairo_text_extents_t ext;
                cairo_text_extents (cr, tab.group->name().c_str(), &ext);
 
-               cairo_set_source_rgb (cr, 1, 1, 1);
+               ArdourCanvas::Color c = ArdourCanvas::contrasting_text_color (ArdourCanvas::rgba_to_color (r, g, b, a));
+               ArdourCanvas::color_to_rgba (c, r, g, b, a);
+
+               cairo_set_source_rgb (cr, r, g, b);
                cairo_move_to (cr, get_width() - ext.height / 2, tab.from + (f.second + tab.to - tab.from) / 2);
                cairo_save (cr);
                cairo_rotate (cr, - M_PI / 2);
index fb1995659cfc1e3c01522a51a48778c970fd1e13..ce9de4bc1207140b001fe3b7e98c7b42b2912767 100644 (file)
@@ -23,6 +23,7 @@
 enum ItemType {
        RegionItem,
        StreamItem,
+       WaveItem,
        PlayheadCursorItem,
        MarkerItem,
        MarkerBarItem,
@@ -45,16 +46,22 @@ enum ItemType {
        AutomationTrackItem,
        FadeInItem,
        FadeInHandleItem,
+       FadeInTrimHandleItem,
        FadeOutItem,
        FadeOutHandleItem,
+       FadeOutTrimHandleItem,
        NoteItem,
        FeatureLineItem,
-        LeftFrameHandle,
-        RightFrameHandle,
+       LeftFrameHandle,
+       RightFrameHandle,
        StartCrossFadeItem,
        EndCrossFadeItem,
        CrossfadeViewItem,
-
+       TimecodeRulerItem,
+       MinsecRulerItem,
+       BBTRulerItem,
+       SamplesRulerItem,
+       
        /* don't remove this */
 
        NoItem
index 3cf3ef83b7a5fe53a7bf3c309a31d4bf1a7225be..4f6f1b91ce6e6ca17ad55dd02b348e234b7737e6 100644 (file)
@@ -64,7 +64,7 @@ Editor::add_new_location (Location *location)
 {
        ENSURE_GUI_THREAD (*this, &Editor::add_new_location, location);
 
-       ArdourCanvas::Group* group = add_new_location_internal (location);
+       ArdourCanvas::Container* group = add_new_location_internal (location);
 
        /* Do a full update of the markers in this group */
        update_marker_labels (group);
@@ -82,14 +82,14 @@ Editor::add_new_location (Location *location)
  *  the caller must call update_marker_labels () after calling this.
  *  @return canvas group that the location's marker was added to.
  */
-ArdourCanvas::Group*
+ArdourCanvas::Container*
 Editor::add_new_location_internal (Location* location)
 {
        LocationMarkers *lam = new LocationMarkers;
        uint32_t color;
 
        /* make a note here of which group this marker ends up in */
-       ArdourCanvas::Group* group = 0;
+       ArdourCanvas::Container* group = 0;
 
        if (location->is_cd_marker()) {
                color = location_cd_marker_color;
@@ -311,14 +311,14 @@ struct MarkerComparator {
 void
 Editor::update_marker_labels ()
 {
-       for (std::map<ArdourCanvas::Group *, std::list<Marker *> >::iterator i = _sorted_marker_lists.begin(); i != _sorted_marker_lists.end(); ++i) {
+       for (std::map<ArdourCanvas::Container *, std::list<Marker *> >::iterator i = _sorted_marker_lists.begin(); i != _sorted_marker_lists.end(); ++i) {
                update_marker_labels (i->first);
        }
 }
 
 /** Look at all markers in a group and update label widths */
 void
-Editor::update_marker_labels (ArdourCanvas::Group* group)
+Editor::update_marker_labels (ArdourCanvas::Container* group)
 {
        list<Marker*>& sorted = _sorted_marker_lists[group];
 
@@ -1465,16 +1465,23 @@ Editor::update_punch_range_view ()
        Location* tpl;
 
        if ((_session->config.get_punch_in() || _session->config.get_punch_out()) && ((tpl = transport_punch_location()) != 0)) {
-               ArdourCanvas::Rect const v = _track_canvas->visible_area ();
 
+               double pixel_start;
+               double pixel_end;
+               
                if (_session->config.get_punch_in()) {
-                       transport_punch_range_rect->set_x0 (sample_to_pixel (tpl->start()));
-                       transport_punch_range_rect->set_x1 (_session->config.get_punch_out() ? sample_to_pixel (tpl->end()) : sample_to_pixel (max_framepos));
+                       pixel_start = sample_to_pixel (tpl->start());
+               } else {
+                       pixel_start = 0;
+               }
+               if (_session->config.get_punch_out()) {
+                       pixel_end = sample_to_pixel (tpl->end());
                } else {
-                       transport_punch_range_rect->set_x0 (0);
-                       transport_punch_range_rect->set_x1 (_session->config.get_punch_out() ? sample_to_pixel (tpl->end()) : v.width ());
+                       pixel_end = sample_to_pixel (max_framepos);
                }
                
+               transport_punch_range_rect->set_x0 (pixel_start);
+               transport_punch_range_rect->set_x1 (pixel_end);                 
                transport_punch_range_rect->show();
 
        } else {
@@ -1569,7 +1576,7 @@ Editor::toggle_marker_lines ()
 void
 Editor::remove_sorted_marker (Marker* m)
 {
-       for (std::map<ArdourCanvas::Group *, std::list<Marker *> >::iterator i = _sorted_marker_lists.begin(); i != _sorted_marker_lists.end(); ++i) {
+       for (std::map<ArdourCanvas::Container *, std::list<Marker *> >::iterator i = _sorted_marker_lists.begin(); i != _sorted_marker_lists.end(); ++i) {
                i->second.remove (m);
        }
 }
index 67e749de50171773f140088195516aabd6010dcf..cfd832385f3656763305689b1e0e384a872a0d0b 100644 (file)
@@ -166,7 +166,6 @@ void
 Editor::ensure_all_elements_drawn ()
 {
        controls_layout.queue_draw ();
-       ruler_label_event_box.queue_draw ();
        time_bars_event_box.queue_draw ();
 }
 #endif
index 8e5a357ac4fa831211ec405702c6b958d4e14b2e..fffb67c46ddd28c5cab2728f0f17f848064f58fb 100644 (file)
@@ -60,7 +60,6 @@
 #include "automation_time_axis.h"
 #include "control_point.h"
 #include "prompter.h"
-#include "utils.h"
 #include "selection.h"
 #include "keyboard.h"
 #include "editing.h"
@@ -114,7 +113,7 @@ Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
                return false;
        }
 
-       if (pointer_window != canvas_window && pointer_window != _time_bars_canvas->get_window()) {
+       if (pointer_window != canvas_window) {
                in_track_canvas = false;
                return false;
        }
@@ -134,18 +133,16 @@ Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
 framepos_t
 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
 {
-       double x;
-       double y;
+       ArdourCanvas::Duple d;
 
-       if (!gdk_event_get_coords (event, &x, &y)) {
+       if (!gdk_event_get_coords (event, &d.x, &d.y)) {
                return 0;
        }
 
        /* event coordinates are in window units, so convert to canvas
-        * (i.e. account for scrolling)
         */
 
-       ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (x, y));
+       d = _track_canvas->window_to_canvas (d);
 
        if (pcx) {
                *pcx = d.x;
@@ -187,51 +184,6 @@ Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) c
        return pixel_to_sample_from_event (x);
 }
 
-Gdk::Cursor*
-Editor::which_grabber_cursor ()
-{
-       Gdk::Cursor* c = _cursors->grabber;
-
-       if (_internal_editing) {
-               switch (mouse_mode) {
-               case MouseDraw:
-                       c = _cursors->midi_pencil;
-                       break;
-
-               case MouseObject:
-                       c = _cursors->grabber_note;
-                       break;
-
-               case MouseTimeFX:
-                       c = _cursors->midi_resize;
-                       break;
-                       
-               case MouseRange:
-                       c = _cursors->grabber_note;
-                       break;
-
-               default:
-                       break;
-               }
-
-       } else {
-
-               switch (_edit_point) {
-               case EditAtMouse:
-                       c = _cursors->grabber_edit_point;
-                       break;
-               default:
-                       boost::shared_ptr<Movable> m = _movable.lock();
-                       if (m && m->locked()) {
-                               c = _cursors->speaker;
-                       }
-                       break;
-               }
-       }
-
-       return c;
-}
-
 void
 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
 {
@@ -239,7 +191,6 @@ Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
 
        if (!st || st == t) {
                _trimmable = t;
-               set_canvas_cursor ();
        }
 }
 
@@ -250,91 +201,7 @@ Editor::set_current_movable (boost::shared_ptr<Movable> m)
 
        if (!sm || sm != m) {
                _movable = m;
-               set_canvas_cursor ();
-       }
-}
-
-void
-Editor::set_canvas_cursor ()
-{
-       switch (mouse_mode) {
-       case MouseRange:
-               current_canvas_cursor = _cursors->selector;
-               if (_internal_editing) {
-                       current_canvas_cursor = which_grabber_cursor();
-               }
-               break;
-
-       case MouseObject:
-               current_canvas_cursor = which_grabber_cursor();
-               break;
-
-       case MouseDraw:
-               current_canvas_cursor = _cursors->midi_pencil;
-               break;
-
-       case MouseGain:
-               current_canvas_cursor = _cursors->cross_hair;
-               break;
-
-       case MouseZoom:
-               if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
-                       current_canvas_cursor = _cursors->zoom_out;
-               } else {
-                       current_canvas_cursor = _cursors->zoom_in;
-               }
-               break;
-
-       case MouseTimeFX:
-               current_canvas_cursor = _cursors->time_fx; // just use playhead
-               break;
-
-       case MouseAudition:
-               current_canvas_cursor = _cursors->speaker;
-               break;
-       }
-
-       if (!_internal_editing) {
-               switch (_join_object_range_state) {
-               case JOIN_OBJECT_RANGE_NONE:
-                       break;
-               case JOIN_OBJECT_RANGE_OBJECT:
-                       current_canvas_cursor = which_grabber_cursor ();
-                       break;
-               case JOIN_OBJECT_RANGE_RANGE:
-                       current_canvas_cursor = _cursors->selector;
-                       break;
-               }
-       }
-
-       /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
-       if (!_internal_editing && get_smart_mode() ) {
-
-               double x, y;
-               get_pointer_position (x, y);
-
-               if (x >= 0 && y >= 0) {
-                       
-                       vector<ArdourCanvas::Item const *> items;
-                       
-                       _track_canvas->root()->add_items_at_point (ArdourCanvas::Duple (x,y), items);
-                       
-                       // first item will be the upper most 
-                       
-                       if (!items.empty()) {
-                               const ArdourCanvas::Item* i = items.front();
-                               
-                               if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
-                                       pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
-                                       if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
-                                               current_canvas_cursor = _cursors->up_down;
-                                       }
-                               }
-                       }
-               }
        }
-
-       set_canvas_cursor (current_canvas_cursor, true);
 }
 
 void
@@ -497,7 +364,7 @@ Editor::mouse_mode_toggled (MouseMode m)
        }
 */
        
-       set_canvas_cursor ();
+       reset_canvas_cursor ();
        set_gain_envelope_visibility ();
        
        update_time_selection_display ();
@@ -623,7 +490,7 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
           to cut notes or regions.
        */
 
-       MouseMode eff_mouse_mode = mouse_mode;
+       MouseMode eff_mouse_mode = effective_mouse_mode ();
 
        if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
                /* context clicks are always about object properties, even if
@@ -632,6 +499,20 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
                eff_mouse_mode = MouseObject;
        }
 
+       /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
+       if (get_smart_mode()) {
+               switch (item_type) {
+                 case FadeInHandleItem:
+                 case FadeInTrimHandleItem:
+                 case FadeOutHandleItem:
+                 case FadeOutTrimHandleItem:
+                         eff_mouse_mode = MouseObject;
+                         break;
+               default:
+                       break;
+               }
+       }
+
        if (((mouse_mode != MouseObject) &&
             (mouse_mode != MouseAudition || item_type != RegionItem) &&
             (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
@@ -684,21 +565,15 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
        case RegionViewName:
        case LeftFrameHandle:
        case RightFrameHandle:
-               if (eff_mouse_mode != MouseRange) {
-                       set_selected_regionview_from_click (press, op);
-               } else if (event->type == GDK_BUTTON_PRESS) {
-                       set_selected_track_as_side_effect (op);
-               }
-               break;
-
        case FadeInHandleItem:
+       case FadeInTrimHandleItem:
        case FadeInItem:
        case FadeOutHandleItem:
+       case FadeOutTrimHandleItem:
        case FadeOutItem:
        case StartCrossFadeItem:
        case EndCrossFadeItem:
-               if (eff_mouse_mode != MouseRange) {
-                       cerr << "Should be setting selected regionview\n";
+               if (get_smart_mode() || eff_mouse_mode != MouseRange) {
                        set_selected_regionview_from_click (press, op);
                } else if (event->type == GDK_BUTTON_PRESS) {
                        set_selected_track_as_side_effect (op);
@@ -789,6 +664,10 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
        case MarkerBarItem:
        case TempoBarItem:
        case MeterBarItem:
+       case TimecodeRulerItem:
+       case SamplesRulerItem:
+       case MinsecRulerItem:
+       case BBTRulerItem:
                if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
                        _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
                }
@@ -844,8 +723,17 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
        Editing::MouseMode eff = effective_mouse_mode ();
 
        /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
-       if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
-               eff = MouseObject;
+       if (get_smart_mode()) { 
+               switch (item_type) {
+                 case FadeInHandleItem:
+                 case FadeInTrimHandleItem:
+                 case FadeOutHandleItem:
+                 case FadeOutTrimHandleItem:
+                       eff = MouseObject;
+                       break;
+               default:
+                       break;
+               }
        }
 
        /* there is no Range mode when in internal edit mode */
@@ -1046,18 +934,28 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
 
                        case RegionViewNameHighlight:
                        case LeftFrameHandle:
-                        case RightFrameHandle:
+                       case RightFrameHandle:
                                if (!clicked_regionview->region()->locked()) {
                                        _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
                                        return true;
                                }
                                break;
 
+                       case FadeInTrimHandleItem:
+                       case FadeOutTrimHandleItem:
+                               if (!clicked_regionview->region()->locked()) {
+                                       _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
+                                       return true;
+                               }
+                               break;
+
                        case RegionViewName:
                        {
                                /* rename happens on edit clicks */
-                               _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
-                               return true;
+                               if (clicked_regionview->get_name_highlight()) {
+                                       _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
+                                       return true;
+                               }
                                break;
                        }
 
@@ -1229,7 +1127,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                scrub_reverse_distance = 0;
                last_scrub_x = event->button.x;
                scrubbing_direction = 0;
-               set_canvas_cursor (_cursors->transparent);
+               push_canvas_cursor (_cursors->transparent);
                return true;
                break;
 
@@ -1272,12 +1170,12 @@ Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
 
                switch (item_type) {
                case RegionViewNameHighlight:
-                        _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
-                        return true;
-                        break;
+                       _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
+                       return true;
+                       break;
 
-                case LeftFrameHandle:
-                case RightFrameHandle:
+               case LeftFrameHandle:
+               case RightFrameHandle:
                        if (!internal_editing ()) {
                                _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
                        }
@@ -1333,21 +1231,6 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                return false;
        }
 
-       Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas_viewport->get_window();
-
-       if (canvas_window) {
-               Glib::RefPtr<const Gdk::Window> pointer_window;
-               int x, y;
-               double wx, wy;
-               Gdk::ModifierType mask;
-
-               pointer_window = canvas_window->get_pointer (x, y, mask);
-
-               if (pointer_window == _track_canvas->get_window()) {
-                       _track_canvas->window_to_canvas (x, y, wx, wy);
-               }
-       }
-
         pre_press_cursor = current_canvas_cursor;
 
        _track_canvas->grab_focus();
@@ -1380,7 +1263,11 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                case CdMarkerBarItem:
                case TransportMarkerBarItem:
                case StreamItem:
-                       /* button press on these events never does anything to
+               case TimecodeRulerItem:
+               case SamplesRulerItem:
+               case MinsecRulerItem:
+               case BBTRulerItem:
+                       /* button press on these items never does anything to
                           change the editing mode.
                        */
                        break;
@@ -1558,16 +1445,17 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                        switch (item_type) {
                        case FadeInItem:
                        case FadeInHandleItem:
-                       case FadeOutItem:
-                       case FadeOutHandleItem:
-                               popup_fade_context_menu (1, event->button.time, item, item_type);
-                               break;
-
+                       case FadeInTrimHandleItem:
                        case StartCrossFadeItem:
+                       case LeftFrameHandle:
                                popup_xfade_in_context_menu (1, event->button.time, item, item_type);
                                break;
 
+                       case FadeOutItem:
+                       case FadeOutHandleItem:
+                       case FadeOutTrimHandleItem:
                        case EndCrossFadeItem:
+                       case RightFrameHandle:
                                popup_xfade_out_context_menu (1, event->button.time, item, item_type);
                                break;
 
@@ -1577,8 +1465,6 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
 
                        case RegionItem:
                        case RegionViewNameHighlight:
-                       case LeftFrameHandle:
-                       case RightFrameHandle:
                        case RegionViewName:
                                popup_track_context_menu (1, event->button.time, item_type, false);
                                break;
@@ -1598,6 +1484,10 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                        case TempoBarItem:
                        case MeterBarItem:
                        case VideoBarItem:
+                       case TimecodeRulerItem:
+                       case SamplesRulerItem:
+                       case MinsecRulerItem:
+                       case BBTRulerItem:
                                popup_ruler_menu (where, item_type);
                                break;
 
@@ -1710,6 +1600,13 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                        return true;
                        break;
 
+               case TimecodeRulerItem:
+               case SamplesRulerItem:
+               case MinsecRulerItem:
+               case BBTRulerItem:
+                       return true;
+                       break;
+
                default:
                        break;
                }
@@ -1760,7 +1657,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                        break;
 
                case MouseAudition:
-                       set_canvas_cursor (current_canvas_cursor);
+                       pop_canvas_cursor ();
                        if (scrubbing_direction == 0) {
                                /* no drag, just a click */
                                switch (item_type) {
@@ -1841,26 +1738,24 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        double fraction;
         bool ret = true;
 
+       /* by the time we reach here, entered_regionview and entered trackview
+        * will have already been set as appropriate. Things are done this 
+        * way because this method isn't passed a pointer to a variable type of
+        * thing that is entered (which may or may not be canvas item).
+        * (e.g. the actual entered regionview)
+        */
+
+       choose_canvas_cursor_on_entry (&event->crossing, item_type);
+
        switch (item_type) {
        case ControlPointItem:
                if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
                        cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
                        cp->show ();
 
-                       double at_x, at_y;
-                       at_x = cp->get_x();
-                       at_y = cp->get_y ();
-                       cp->i2w (at_x, at_y);
-                       at_x += 10.0;
-                       at_y += 10.0;
-
                        fraction = 1.0 - (cp->get_y() / cp->line().height());
 
-                       if (is_drawable() && !_drags->active ()) {
-                               set_canvas_cursor (_cursors->fader);
-                       }
-
-                       _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
+                       _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
                        _verbose_cursor->show ();
                }
                break;
@@ -1871,9 +1766,6 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                        if (line) {
                                line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
                        }
-                       if (is_drawable()) {
-                               set_canvas_cursor (_cursors->fader);
-                       }
                }
                break;
 
@@ -1883,108 +1775,14 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                        if (line) {
                                line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
                        }
-                       if (is_drawable()) {
-                               set_canvas_cursor (_cursors->fader);
-                       }
-               }
-               break;
-
-       case RegionViewNameHighlight:
-               if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
-                       set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
-                       _over_region_trim_target = true;
                }
                break;
 
-       case LeftFrameHandle:
-       case RightFrameHandle:
-               if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
-                       set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
-               }
-                break;
-
-       case RegionItem:
-               switch (effective_mouse_mode()) {
-               case MouseRange:
-                       set_canvas_cursor (_cursors->selector);
-                       break;
-               default:
-                       set_canvas_cursor (which_grabber_cursor());
-                       break;
-               }
-               break;  
-
-       case StartSelectionTrimItem:
-               if (is_drawable()) {
-                       set_canvas_cursor (_cursors->left_side_trim);
-               }
-               break;
-       case EndSelectionTrimItem:
-               if (is_drawable()) {
-                       set_canvas_cursor (_cursors->right_side_trim);
-               }
-               break;
-
-       case PlayheadCursorItem:
-               if (is_drawable()) {
-                       switch (_edit_point) {
-                       case EditAtMouse:
-                               set_canvas_cursor (_cursors->grabber_edit_point);
-                               break;
-                       default:
-                               set_canvas_cursor (_cursors->grabber);
-                               break;
-                       }
-               }
-               break;
-
-
-       case RegionViewName:
-
-               /* when the name is not an active item, the entire name highlight is for trimming */
-
-               if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
-                       if (mouse_mode == MouseObject && is_drawable()) {
-                               set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
-                               _over_region_trim_target = true;
-                       }
-               }
-               break;
-
-
        case AutomationTrackItem:
-               if (is_drawable()) {
-                       Gdk::Cursor *cursor;
-                       switch (mouse_mode) {
-                       case MouseRange:
-                               cursor = _cursors->selector;
-                               break;
-                       case MouseZoom:
-                               cursor = _cursors->zoom_in;
-                               break;
-                       default:
-                               cursor = _cursors->cross_hair;
-                               break;
-                       }
-
-                       set_canvas_cursor (cursor);
-
-                       AutomationTimeAxisView* atv;
-                       if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
-                               clear_entered_track = false;
-                               set_entered_track (atv);
-                       }
-               }
-               break;
-
-       case MarkerBarItem:
-       case RangeMarkerBarItem:
-       case TransportMarkerBarItem:
-       case CdMarkerBarItem:
-       case MeterBarItem:
-       case TempoBarItem:
-               if (is_drawable()) {
-                       set_canvas_cursor (_cursors->timebar);
+               AutomationTimeAxisView* atv;
+               if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
+                       clear_entered_track = false;
+                       set_entered_track (atv);
                }
                break;
 
@@ -1997,32 +1795,30 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                // fall through
        case MeterMarkerItem:
        case TempoMarkerItem:
-               if (is_drawable()) {
-                       set_canvas_cursor (_cursors->timebar);
-               }
                break;
 
        case FadeInHandleItem:
+       case FadeInTrimHandleItem:
                if (mouse_mode == MouseObject && !internal_editing()) {
                        ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
                        if (rect) {
                                RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
                                rect->set_fill_color (rv->get_fill_color());
-                               set_canvas_cursor (_cursors->fade_in);
                        }
                }
                break;
 
        case FadeOutHandleItem:
+       case FadeOutTrimHandleItem:
                if (mouse_mode == MouseObject && !internal_editing()) {
                        ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
                        if (rect) {
                                RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
                                rect->set_fill_color (rv->get_fill_color ());
-                               set_canvas_cursor (_cursors->fade_out);
                        }
                }
                break;
+
        case FeatureLineItem:
        {
                ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
@@ -2031,16 +1827,13 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        break;
 
        case SelectionItem:
-               if ( get_smart_mode() ) {
-                       set_canvas_cursor ();
-               }
                break;
 
        default:
                break;
        }
 
-       /* second pass to handle entered track status in a comprehensible way.
+       /* third pass to handle entered track status in a comprehensible way.
         */
 
        switch (item_type) {
@@ -2056,7 +1849,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                break;
 
        default:
-               set_entered_track (0);
+
                break;
        }
 
@@ -2069,31 +1862,12 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
        AutomationLine* al;
        Marker *marker;
        Location *loc;
-       RegionView* rv;
        bool is_start;
        bool ret = true;
 
        switch (item_type) {
        case ControlPointItem:
-               if (is_drawable()) {
-                       set_canvas_cursor (current_canvas_cursor);
-               }
-
-               _verbose_cursor->hide ();
-               break;
-
-       case RegionViewNameHighlight:
-       case LeftFrameHandle:
-       case RightFrameHandle:
-       case StartSelectionTrimItem:
-       case EndSelectionTrimItem:
-       case PlayheadCursorItem:
-
-               _over_region_trim_target = false;
-
-               if (is_drawable()) {
-                       set_canvas_cursor (current_canvas_cursor);
-               }
+               _verbose_cursor->hide (); 
                break;
 
        case GainLineItem:
@@ -2105,31 +1879,6 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
                                line->set_outline_color (al->get_line_color());
                        }
                }
-               if (is_drawable()) {
-                       set_canvas_cursor (current_canvas_cursor);
-               }
-               break;
-
-       case RegionViewName:
-               /* see enter_handler() for notes */
-               _over_region_trim_target = false;
-
-               if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
-                       if (is_drawable() && mouse_mode == MouseObject) {
-                               set_canvas_cursor (current_canvas_cursor);
-                       }
-               }
-               break;
-
-       case RangeMarkerBarItem:
-       case TransportMarkerBarItem:
-       case CdMarkerBarItem:
-       case MeterBarItem:
-       case TempoBarItem:
-       case MarkerBarItem:
-               if (is_drawable()) {
-                       set_canvas_cursor (current_canvas_cursor);
-               }
                break;
 
        case MarkerItem:
@@ -2143,38 +1892,29 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
                // fall through
        case MeterMarkerItem:
        case TempoMarkerItem:
-
-               if (is_drawable()) {
-                       set_canvas_cursor (current_canvas_cursor);
-               }
-
                break;
 
+       case FadeInTrimHandleItem:
+       case FadeOutTrimHandleItem:
        case FadeInHandleItem:
        case FadeOutHandleItem:
-               rv = static_cast<RegionView*>(item->get_data ("regionview"));
-               {
-                       ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
-                       if (rect) {
-                               rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
-                       }
+       {
+               ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
+               if (rect) {
+                       rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
                }
-               set_canvas_cursor (current_canvas_cursor);
-               break;
+       }
+       break;
 
        case AutomationTrackItem:
-               if (is_drawable()) {
-                       set_canvas_cursor (current_canvas_cursor);
-                       clear_entered_track = true;
-                       Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
-               }
                break;
+
        case FeatureLineItem:
-               {
-                       ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
-                       line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
-               }
-               break;
+       {
+               ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
+               line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
+       }
+       break;
 
        default:
                break;
@@ -2183,16 +1923,6 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
        return ret;
 }
 
-gint
-Editor::left_automation_track ()
-{
-       if (clear_entered_track) {
-               set_entered_track (0);
-               clear_entered_track = false;
-       }
-       return false;
-}
-
 void
 Editor::scrub (framepos_t frame, double current_x)
 {
@@ -2297,34 +2027,19 @@ Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from
                current_stepping_trackview = 0;
                step_timeout.disconnect ();
        }
-
+       
        if (_session && _session->actively_recording()) {
                /* Sorry. no dragging stuff around while we record */
                return true;
        }
-
-       JoinObjectRangeState const old = _join_object_range_state;
-       update_join_object_range_location (event->motion.x, event->motion.y);
-
-       if (!_internal_editing && _join_object_range_state != old) {
-               set_canvas_cursor ();
-       }
-
-       if (!_internal_editing && _over_region_trim_target) {
-               set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
-       }
-
-       bool handled = false;
+       
+       update_join_object_range_location (event->motion.y);
+       
        if (_drags->active ()) {
-               handled = _drags->motion_handler (event, from_autoscroll);
+               return _drags->motion_handler (event, from_autoscroll);
        }
 
-       if (!handled) {
-               return false;
-       }
-
-       track_canvas_motion (event);
-       return true;
+       return false;
 }
 
 bool
@@ -2399,19 +2114,19 @@ Editor::edit_notes (TimeAxisViewItem& tavi)
        if (s.empty ()) {
                return;
        }
-       
+
        EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
-        d->show_all ();
+       d->show_all ();
        ensure_float (*d);
 
-        d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
+       d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
 }
 
 void
 Editor::note_edit_done (int r, EditNoteDialog* d)
 {
-        d->done (r);
-        delete d;
+       d->done (r);
+       delete d;
 }
 
 void
@@ -2481,7 +2196,7 @@ Editor::collect_new_region_view (RegionView* rv)
 void
 Editor::collect_and_select_new_region_view (RegionView* rv)
 {
-       selection->add(rv);
+       selection->add(rv);
        latest_regionviews.push_back (rv);
 }
 
@@ -2499,7 +2214,7 @@ Editor::cancel_selection ()
 void
 Editor::cancel_time_selection ()
 {
-    for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
+       for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
                (*i)->hide_selection ();
        }
        selection->time.clear ();
@@ -2685,8 +2400,6 @@ Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region
                return;
        }
 
-       _region_motion_group->raise_to_top ();
-
        switch (Config->get_edit_mode()) {
                case Splice:
                        _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
@@ -2697,6 +2410,7 @@ Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region
                default:
                        _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
                        break;
+
        }
 }
 
@@ -2709,8 +2423,6 @@ Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* r
                return;
        }
 
-       _region_motion_group->raise_to_top ();
-
        _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
 }
 
@@ -2813,15 +2525,15 @@ Editor::set_internal_edit (bool yn)
        }
 
        _internal_editing = yn;
-       
+
        if (yn) {
-                pre_internal_mouse_mode = mouse_mode;
+               pre_internal_mouse_mode = mouse_mode;
                pre_internal_snap_type = _snap_type;
                pre_internal_snap_mode = _snap_mode;
 
-                for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
-                        (*i)->enter_internal_edit_mode ();
-                }
+               for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
+                       (*i)->enter_internal_edit_mode ();
+               }
 
                set_snap_to (internal_snap_type);
                set_snap_mode (internal_snap_mode);
@@ -2831,62 +2543,70 @@ Editor::set_internal_edit (bool yn)
                internal_snap_mode = _snap_mode;
                internal_snap_type = _snap_type;
 
-                for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
-                        (*i)->leave_internal_edit_mode ();
-                }
+               for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
+                       (*i)->leave_internal_edit_mode ();
+               }
 
-                if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
-                        /* we were drawing .. flip back to something sensible */
-                        set_mouse_mode (pre_internal_mouse_mode);
-                }
+               if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
+                       /* we were drawing .. flip back to something sensible */
+                       set_mouse_mode (pre_internal_mouse_mode);
+               }
 
                set_snap_to (pre_internal_snap_type);
                set_snap_mode (pre_internal_snap_mode);
        }
-       
-       set_canvas_cursor ();
+
+       reset_canvas_cursor ();
 }
 
-/** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
- *  used by the `join object/range' tool mode.
+/** Update _join_object_range_state which indicate whether we are over the top
+ *  or bottom half of a route view, used by the `join object/range' tool
+ *  mode. Coordinates in canvas space.
  */
 void
-Editor::update_join_object_range_location (double /*x*/, double y)
+Editor::update_join_object_range_location (double y)
 {
-       /* XXX: actually, this decides based on whether the mouse is in the top
-          or bottom half of a the waveform part RouteTimeAxisView;
-
-          Note that entered_{track,regionview} is not always setup (e.g. if
-          the mouse is over a TimeSelection), and to get a Region
-          that we're over requires searching the playlist.
-       */
-
-       if ( !get_smart_mode() ) {
+       if (_internal_editing || !get_smart_mode()) {
                _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
                return;
        }
 
+       JoinObjectRangeState const old = _join_object_range_state;
+
        if (mouse_mode == MouseObject) {
                _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
        } else if (mouse_mode == MouseRange) {
                _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
        }
 
-       /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
-       pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
-
-       if (tvp.first) {
 
-               RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
-               if (rtv) {
+       if (entered_regionview) {
 
-                       double cx = 0;
-                       double cy = y;
-                       rtv->canvas_display()->canvas_to_item (cx, cy);
+               ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
+               double const c = item_space.y / entered_regionview->height();
+                       
+               _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
+               
+               if (_join_object_range_state != old) {
+                       set_canvas_cursor (which_track_cursor ());
+               }
 
-                       double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
+       } else if (entered_track) {
 
-                       _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
+               RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
+               
+               if (entered_route_view) {
+                       /* track/bus ... but not in a region ... use range mode */
+                       _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
+                       if (_join_object_range_state != old) {
+                               set_canvas_cursor (which_track_cursor ());
+                       }
+               } else {
+                       /* Other kinds of tracks use object mode */
+                       _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
+                       if (_join_object_range_state != old) {
+                               set_canvas_cursor (which_track_cursor ());
+                       }
                }
        }
 }
@@ -2912,51 +2632,6 @@ Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
        e->region_view().delete_note (e->note ());
 }
 
-void
-Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
-{
-       /* XXX: this check should not be necessary */
-       if (rv == 0) {
-               return;
-       }
-
-       ArdourCanvas::Group* g = rv->get_canvas_group ();
-       ArdourCanvas::Group* p = g->parent ();
-
-       /* Compute x in region view parent coordinates */
-       double dy = 0;
-       p->canvas_to_item (x, dy);
-
-       boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
-       assert (item_bbox);
-       ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
-
-       /* First or last 10% of region is used for trimming, if the whole
-          region is wider than 20 pixels at the current zoom level.
-       */
-
-       double const w = parent_bbox.width();
-
-       if (w > 20.0 && x >= parent_bbox.x0 && x < parent_bbox.x1) {
-               
-               Trimmable::CanTrim ct = rv->region()->can_trim ();
-
-               if (((x - parent_bbox.x0) / w) < 0.10) {
-                       if (ct & Trimmable::FrontTrimEarlier) {
-                               set_canvas_cursor (_cursors->left_side_trim, true);
-                       } else {
-                               set_canvas_cursor (_cursors->left_side_trim_right_only, true);
-                       }
-               } else if (((parent_bbox.x1 - x) / w) < 0.10) {
-                       if (ct & Trimmable::EndTrimLater) {
-                               set_canvas_cursor (_cursors->right_side_trim, true);
-                       } else {
-                               set_canvas_cursor (_cursors->right_side_trim_left_only, true);
-                       }
-               }
-       }
-}
-
 /** Obtain the pointer position in canvas coordinates */
 void
 Editor::get_pointer_position (double& x, double& y) const
index b48db60a42ac79e58fda7296ec1d2afab7cc997a..45568bdb3c9e1666608edf676c172548602ca5ef 100644 (file)
@@ -57,6 +57,7 @@
 
 #include "canvas/canvas.h"
 
+#include "actions.h"
 #include "ardour_ui.h"
 #include "audio_region_view.h"
 #include "audio_streamview.h"
@@ -70,7 +71,6 @@
 #include "editor_drag.h"
 #include "editor_regions.h"
 #include "editor_routes.h"
-#include "gtk-custom-hruler.h"
 #include "gui_thread.h"
 #include "insert_time_dialog.h"
 #include "interthread_progress_window.h"
@@ -89,7 +89,6 @@
 #include "strip_silence_dialog.h"
 #include "time_axis_view.h"
 #include "transpose_dialog.h"
-#include "utils.h"
 
 #include "i18n.h"
 
@@ -1333,29 +1332,32 @@ Editor::scroll_tracks_up_line ()
 bool
 Editor::scroll_down_one_track ()
 {
-       double vertical_pos = vertical_adjustment.get_value () + vertical_adjustment.get_page_size() - 1.0; 
-
        TrackViewList::reverse_iterator next = track_views.rend();
        std::pair<TimeAxisView*,double> res;
+       const double bottom_of_trackviews = vertical_adjustment.get_value() + vertical_adjustment.get_page_size() - 1;
 
        for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
                if ((*t)->hidden()) {
                        continue;
                }
                
-               res = (*t)->covers_y_position (vertical_pos);
+               /* If this is the bottom visible trackview, we want to display
+                  the next one.
+               */
+
+               res = (*t)->covers_y_position (bottom_of_trackviews);
 
                if (res.first) {
                        break;
                }
 
-               next = t;
+               ++next; // moves "next" towards the "front" since it is a reverse iterator
        }
 
        /* move to the track below the first one that covers the */
        
        if (next != track_views.rend()) {
-               ensure_track_visible (*next);
+               ensure_time_axis_view_is_visible (**next);
                return true;
        }
 
@@ -1376,9 +1378,11 @@ Editor::scroll_up_one_track ()
                        continue;
                }
 
-               res = (*t)->covers_y_position(vertical_pos);
+               /* find the trackview at the top of the trackview group */
+               res = (*t)->covers_y_position (vertical_pos);
                
                if (res.first) {
+                       cerr << res.first->name() << " covers the top\n";
                        break;
                }
 
@@ -1386,7 +1390,7 @@ Editor::scroll_up_one_track ()
        }
        
        if (prev != track_views.end()) {
-               ensure_track_visible (*prev);
+               ensure_time_axis_view_is_visible (**prev);
                return true;
        }
 
@@ -1398,7 +1402,7 @@ Editor::scroll_up_one_track ()
 void
 Editor::tav_zoom_step (bool coarser)
 {
-       _routes->suspend_redisplay ();
+       DisplaySuspender ds;
 
        TrackViewList* ts;
 
@@ -1412,14 +1416,12 @@ Editor::tav_zoom_step (bool coarser)
                TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
                        tv->step_height (coarser);
        }
-
-       _routes->resume_redisplay ();
 }
 
 void
 Editor::tav_zoom_smooth (bool coarser, bool force_all)
 {
-       _routes->suspend_redisplay ();
+       DisplaySuspender ds;
 
        TrackViewList* ts;
 
@@ -1444,8 +1446,6 @@ Editor::tav_zoom_smooth (bool coarser, bool force_all)
                        tv->set_height (h + 5);
                }
        }
-
-       _routes->resume_redisplay ();
 }
 
 bool
@@ -1458,8 +1458,23 @@ Editor::clamp_samples_per_pixel (framecnt_t& fpp) const
                clamped = true;
        }
 
-       if (max_framepos / fpp < 800) {
-               fpp = max_framepos / 800;
+       framecnt_t sr;
+
+       if (_session) {
+               sr = _session->frame_rate ();
+       } else {
+               sr = 48000;
+       }
+
+       const framecnt_t three_days = 3 * 24 * 60 * 60 * sr;
+       const framecnt_t lots_of_pixels = 4000;
+
+       /* if the zoom level is greater than what you'd get trying to display 3
+        * days of audio on a really big screen, scale it down.
+        */
+
+       if (fpp * lots_of_pixels > three_days) {
+               fpp = three_days / _track_canvas->width();
                clamped = true;
        }
 
@@ -1690,7 +1705,7 @@ Editor::temporal_zoom_region (bool both_axes)
 
                /* hide irrelevant tracks */
 
-               _routes->suspend_redisplay ();
+               DisplaySuspender ds;
 
                for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
                        if (find (tracks.begin(), tracks.end(), (*i)) == tracks.end()) {
@@ -1698,8 +1713,6 @@ Editor::temporal_zoom_region (bool both_axes)
                        }
                }
 
-               _routes->resume_redisplay ();
-
                vertical_adjustment.set_value (0.0);
        }
 
@@ -1777,23 +1790,41 @@ Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
        if (!_session) {
                return;
        }
-       double range_before = frame - leftmost_frame;
-       double new_fpp;
 
-       new_fpp = samples_per_pixel;
+       framecnt_t range_before = frame - leftmost_frame;
+       framecnt_t new_spp;
 
        if (coarser) {
-               new_fpp *= 1.61803399;
-               range_before *= 1.61803399;
+               if (samples_per_pixel <= 1) {
+                       new_spp = 2;
+               } else {
+                       new_spp = samples_per_pixel + (samples_per_pixel/2);
+               }
+               range_before += range_before/2;
        } else {
-               new_fpp = max(1.0,(new_fpp/1.61803399));
-               range_before /= 1.61803399;
+               if (samples_per_pixel >= 1) {
+                       new_spp = samples_per_pixel - (samples_per_pixel/2);
+               } else {
+                       /* could bail out here since we cannot zoom any finer,
+                          but leave that to the clamp_samples_per_pixel() and
+                          equality test below
+                       */
+                       new_spp = samples_per_pixel;
+               }
+
+               range_before -= range_before/2;
        }
 
-       if (new_fpp == samples_per_pixel)  {
+       clamp_samples_per_pixel (new_spp);
+
+       if (new_spp == samples_per_pixel)  {
                return;
        }
 
+       /* zoom focus is automatically taken as @param frame when this
+          method is used.
+       */
+       
        framepos_t new_leftmost = frame - (framepos_t)range_before;
 
        if (new_leftmost > frame) {
@@ -1804,7 +1835,7 @@ Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
                new_leftmost = 0;
        }
 
-       reposition_and_zoom (new_leftmost, new_fpp);
+       reposition_and_zoom (new_leftmost, new_spp);
 }
 
 
@@ -3499,10 +3530,8 @@ Editor::freeze_thread ()
        /* create event pool because we may need to talk to the session */
        SessionEvent::create_per_thread_pool ("freeze events", 64);
        /* create per-thread buffers for process() tree to use */
-       current_interthread_info->process_thread.get_buffers ();
        clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
        current_interthread_info->done = true;
-       current_interthread_info->process_thread.drop_buffers();
        return 0;
 }
 
@@ -5600,7 +5629,7 @@ Editor::select_next_route()
 
        selection->set(current);
 
-       ensure_track_visible(current);
+       ensure_time_axis_view_is_visible (*current);
 }
 
 void
@@ -5631,37 +5660,7 @@ Editor::select_prev_route()
 
        selection->set (current);
 
-       ensure_track_visible(current);
-}
-
-void
-Editor::ensure_track_visible(TimeAxisView *track)
-{
-       if (track->hidden())
-               return;
-
-       double const current_view_min_y = vertical_adjustment.get_value();
-       double const current_view_max_y = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
-
-       double const track_min_y = track->y_position ();
-       double const track_max_y = track->y_position () + track->effective_height ();
-
-       if (track_min_y >= current_view_min_y &&
-           track_max_y <= current_view_max_y) {
-               return;
-       }
-
-       double new_value;
-
-       if (track_min_y < current_view_min_y) {
-               // Track is above the current view
-               new_value = track_min_y;
-       } else {
-               // Track is below the current view
-               new_value = track->y_position () + track->effective_height() - vertical_adjustment.get_page_size();
-       }
-
-       vertical_adjustment.set_value(new_value);
+       ensure_time_axis_view_is_visible (*current);
 }
 
 void
@@ -6575,8 +6574,11 @@ edit your ardour.rc file to set the\n\
                return;
        }
 
-       for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
-               _session->remove_route (*x);
+       {
+               Session::StateProtector sp (_session);
+               for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
+                       _session->remove_route (*x);
+               }
        }
 }
 
@@ -6778,7 +6780,13 @@ Editor::fit_tracks (TrackViewList & tracks)
                ++visible_tracks;
        }
 
-       uint32_t h = (uint32_t) floor ((_visible_canvas_height - child_heights) / visible_tracks);
+       /* compute the per-track height from:
+
+          total canvas visible height - 
+                 height that will be taken by visible children of selected
+                 tracks - height of the ruler/hscroll area 
+       */
+       uint32_t h = (uint32_t) floor ((_visible_canvas_height - (child_heights + _trackview_group->canvas_origin().y)) / visible_tracks);
        double first_y_pos = DBL_MAX;
 
        if (h < TimeAxisView::preset_height (HeightSmall)) {
@@ -6803,9 +6811,7 @@ Editor::fit_tracks (TrackViewList & tracks)
 
        /* operate on all tracks, hide unselected ones that are in the middle of selected ones */
 
-       bool prev_was_selected = false;
-       bool is_selected = tracks.contains (all.front());
-       bool next_is_selected;
+       bool within_selected = false;
 
        for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t) {
 
@@ -6813,26 +6819,16 @@ Editor::fit_tracks (TrackViewList & tracks)
 
                next = t;
                ++next;
-
-               if (next != all.end()) {
-                       next_is_selected = tracks.contains (*next);
-               } else {
-                       next_is_selected = false;
-               }
-
+               
                if ((*t)->marked_for_display ()) {
-                       if (is_selected) {
+                       if (tracks.contains (*t)) { 
                                (*t)->set_height (h);
                                first_y_pos = std::min ((*t)->y_position (), first_y_pos);
-                       } else {
-                               if (prev_was_selected && next_is_selected) {
-                                       hide_track_in_display (*t);
-                               }
+                               within_selected = true;
+                       } else if (within_selected) {
+                               hide_track_in_display (*t);
                        }
                }
-
-               prev_was_selected = is_selected;
-               is_selected = next_is_selected;
        }
 
        /*
@@ -7016,3 +7012,47 @@ Editor::toggle_midi_input_active (bool flip_others)
        
        _session->set_exclusive_input_active (rl, onoff, flip_others);
 }
+
+void
+Editor::lock ()
+{
+       if (!lock_dialog) {
+               lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
+
+               Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
+               lock_dialog->get_vbox()->pack_start (*padlock);
+
+               ArdourButton* b = manage (new ArdourButton);
+               b->set_name ("lock button");
+               b->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Click to unlock")));
+               b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
+               lock_dialog->get_vbox()->pack_start (*b);
+               
+               lock_dialog->get_vbox()->show_all ();
+               lock_dialog->set_size_request (200, 200);
+       }
+       
+#ifdef __APPLE__
+       /* The global menu bar continues to be accessible to applications
+          with modal dialogs, which means that we need to desensitize
+          all items in the menu bar. Since those items are really just
+          proxies for actions, that means disabling all actions.
+       */
+       ActionManager::disable_all_actions ();
+#endif
+       lock_dialog->present ();
+}
+
+void
+Editor::unlock ()
+{
+       lock_dialog->hide ();
+       
+#ifdef __APPLE__
+       ActionManager::pop_action_state ();
+#endif 
+
+       if (ARDOUR_UI::config()->get_lock_gui_after_seconds()) {
+               start_lock_event_timing ();
+       }
+}
index bf13bd5a6596d496a04a199edc2afd5b303a0ec0..4113b6d00b571f9451bc65c6b9dab903a806c3ea 100644 (file)
@@ -53,6 +53,7 @@
 
 using namespace std;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtk;
 using namespace Glib;
@@ -114,7 +115,7 @@ EditorRegions::EditorRegions (Editor* e)
                { 3, _("Length"),  _("Length of the region") },
                { 4, _("Sync"),  _("Position of region sync point, relative to start of the region") },
                { 5, _("Fade In"),  _("Length of region fade-in (units: secondary clock), () if disabled") },
-               { 6, _("Fade Out"),  _("Length of region fade-out (units: secondary clock), () if dsisabled") },
+               { 6, _("Fade Out"),  _("Length of region fade-out (units: secondary clock), () if disabled") },
                { 7, _("L"),  _("Region position locked?") },
                { 8, _("G"),  _("Region position glued to Bars|Beats time?") },
                { 9, _("M"),  _("Region muted?") },
@@ -346,7 +347,7 @@ EditorRegions::add_region (boost::shared_ptr<Region> region)
                        c.set_rgb(0,65535,0);     // FIXME: error color from style
 
                } else {
-                       set_color(c, rgba_from_style ("RegionListWholeFile", 0xff, 0, 0, 0, "fg", Gtk::STATE_NORMAL, false ));
+                       set_color_from_rgba (c, rgba_from_style ("RegionListWholeFile", 0xff, 0, 0, 0, "fg", Gtk::STATE_NORMAL, false ));
 
                }
 
index 72f6adfa8259abf3200d553d26f8fd81753fb05f..d74ba50d64bca7f8cb9045c9e6fae80fc5eb2bf6 100644 (file)
 #include "gtkmm2ext/cell_renderer_color_selector.h"
 
 #include "ardour/route_group.h"
+#include "ardour/route.h"
+#include "ardour/session.h"
 
+#include "ardour_ui.h"
 #include "editor.h"
+#include "editor_group_tabs.h"
+#include "editor_route_groups.h"
+#include "editor_routes.h"
+#include "gui_thread.h"
 #include "keyboard.h"
 #include "marker.h"
-#include "time_axis_view.h"
 #include "prompter.h"
-#include "gui_thread.h"
-#include "editor_group_tabs.h"
 #include "route_group_dialog.h"
 #include "route_time_axis.h"
-#include "editor_routes.h"
-#include "editor_route_groups.h"
-#include "ardour_ui.h"
-
-#include "ardour/route.h"
-#include "ardour/session.h"
+#include "time_axis_view.h"
+#include "utils.h"
 
 #include "i18n.h"
 
 using namespace std;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtk;
 using Gtkmm2ext::Keyboard;
@@ -72,6 +73,7 @@ EditorRouteGroups::EditorRouteGroups (Editor* e)
 
        Gtkmm2ext::CellRendererColorSelector* color_renderer = manage (new Gtkmm2ext::CellRendererColorSelector);
        TreeViewColumn* color_column = manage (new TreeViewColumn ("", *color_renderer));
+
        color_column->add_attribute (color_renderer->property_color(), _columns.gdkcolor);
        
        _display.append_column (*color_column);
@@ -268,8 +270,7 @@ EditorRouteGroups::button_press_event (GdkEventButton* ev)
 
        switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
        case 0: 
-               c = (*iter)[_columns.gdkcolor];
-
+               c =  (*iter)[_columns.gdkcolor];
                color_dialog.get_colorsel()->set_previous_color (c);
                color_dialog.get_colorsel()->set_current_color (c);
 
@@ -278,7 +279,7 @@ EditorRouteGroups::button_press_event (GdkEventButton* ev)
                        break;
                case RESPONSE_ACCEPT:
                        c = color_dialog.get_colorsel()->get_current_color();
-                       GroupTabs::set_group_color (group, c);
+                       GroupTabs::set_group_color (group, gdk_color_to_rgba (c));
                        ARDOUR_UI::config()->set_dirty ();
                        break;
                        
@@ -409,7 +410,7 @@ EditorRouteGroups::row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel
 
        group->apply_changes (plist);
 
-       GroupTabs::set_group_color ((*iter)[_columns.routegroup], (*iter)[_columns.gdkcolor]);
+       GroupTabs::set_group_color ((*iter)[_columns.routegroup], gdk_color_to_rgba ((*iter)[_columns.gdkcolor]));
 }
 
 void
@@ -430,7 +431,10 @@ EditorRouteGroups::add (RouteGroup* group)
        row[_columns.active_shared] = group->is_route_active ();
        row[_columns.active_state] = group->is_active ();
        row[_columns.is_visible] = !group->is_hidden();
-       row[_columns.gdkcolor] = GroupTabs::group_color (group);
+       
+       Gdk::Color c;
+       set_color_from_rgba (c, GroupTabs::group_color (group));
+       row[_columns.gdkcolor] = c;
        
        _in_row_change = true;
 
@@ -500,7 +504,10 @@ EditorRouteGroups::property_changed (RouteGroup* group, const PropertyChange&)
                        (*iter)[_columns.active_shared] = group->is_route_active ();
                        (*iter)[_columns.active_state] = group->is_active ();
                        (*iter)[_columns.is_visible] = !group->is_hidden();
-                       (*iter)[_columns.gdkcolor] = GroupTabs::group_color (group);
+
+                       Gdk::Color c;
+                       set_color_from_rgba (c, GroupTabs::group_color (group));
+                       (*iter)[_columns.gdkcolor] = c;
 
                        break;
                }
index 38706a652587e2afa76d6018e2457ce0cb0be1b5..025633b0909af55e7cf73aaf7dcc50ec99ae313a 100644 (file)
@@ -53,6 +53,7 @@
 
 using namespace std;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtk;
 using namespace Gtkmm2ext;
@@ -70,6 +71,7 @@ EditorRoutes::EditorRoutes (Editor* e)
         , _ignore_reorder (false)
         , _no_redisplay (false)
         , _adding_routes (false)
+        , _route_deletion_in_progress (false)
         , _menu (0)
         , old_focus (0)
         , selection_countdown (0)
@@ -271,7 +273,7 @@ EditorRoutes::EditorRoutes (Editor* e)
        active_col->set_fixed_width (30);
        active_col->set_alignment (ALIGN_CENTER);
        
-       _model->signal_row_deleted().connect (sigc::mem_fun (*this, &EditorRoutes::route_deleted));
+       _model->signal_row_deleted().connect (sigc::mem_fun (*this, &EditorRoutes::row_deleted));
        _model->signal_rows_reordered().connect (sigc::mem_fun (*this, &EditorRoutes::reordered));
 
        _display.signal_button_press_event().connect (sigc::mem_fun (*this, &EditorRoutes::button_press), false);
@@ -519,7 +521,6 @@ EditorRoutes::redisplay ()
                /* show or hide the TimeAxisView */
                if (visible) {
                        position += tv->show_at (position, n, &_editor->edit_controls_vbox);
-                       // SHOWTRACKS
                } else {
                        tv->hide ();
                }
@@ -547,18 +548,31 @@ EditorRoutes::redisplay ()
 }
 
 void
-EditorRoutes::route_deleted (Gtk::TreeModel::Path const &)
+EditorRoutes::row_deleted (Gtk::TreeModel::Path const &)
 {
-       /* this happens as the second step of a DnD within the treeview as well
-          as when a row/route is actually deleted.
+       /* this happens as the second step of a DnD within the treeview, and
+          when a route is actually removed. we don't differentiate between
+          the two cases.
+          
+          note that the sync_orders_keys() step may not actually change any
+          RID's (e.g. the last track may be removed, so all other tracks keep
+          the same RID), which means that no redisplay would happen. so we 
+           have to force a redisplay.
        */
+
        DEBUG_TRACE (DEBUG::OrderKeys, "editor routes treeview row deleted\n");
+
+       DisplaySuspender ds;
        sync_order_keys_from_treeview ();
 }
 
 void
 EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, int* /*what*/)
 {
+       /* reordering implies that RID's will change, so sync_order_keys() will
+          cause a redisplay.
+       */
+
        DEBUG_TRACE (DEBUG::OrderKeys, "editor routes treeview reordered\n");
        sync_order_keys_from_treeview ();
 }
@@ -570,6 +584,7 @@ EditorRoutes::visible_changed (std::string const & path)
                return;
        }
 
+       DisplaySuspender ds;
        TreeIter iter;
 
        if ((iter = _model->get_iter (path))) {
@@ -618,7 +633,7 @@ EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
                _editor->selection->tracks.clear();
        } 
 
-       suspend_redisplay ();
+       DisplaySuspender ds;
 
        _display.set_model (Glib::RefPtr<ListStore>());
 
@@ -687,7 +702,6 @@ EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
        update_input_active_display ();
        update_active_display ();
 
-       resume_redisplay ();
        _display.set_model (_model);
 
        /* now update route order keys from the treeview/track display order */
@@ -721,10 +735,13 @@ EditorRoutes::route_removed (TimeAxisView *tv)
 
        TreeModel::Children rows = _model->children();
        TreeModel::Children::iterator ri;
+       bool found = false;
 
        for (ri = rows.begin(); ri != rows.end(); ++ri) {
                if ((*ri)[_columns.tv] == tv) {
+                        PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
                        _model->erase (ri);
+                       found = true;
                        break;
                }
        }
@@ -779,7 +796,7 @@ EditorRoutes::update_visibility ()
        TreeModel::Children rows = _model->children();
        TreeModel::Children::iterator i;
 
-       suspend_redisplay ();
+       DisplaySuspender ds ();
 
        for (i = rows.begin(); i != rows.end(); ++i) {
                TimeAxisView *tv = (*i)[_columns.tv];
@@ -790,8 +807,6 @@ EditorRoutes::update_visibility ()
         */
 
        sync_order_keys_from_treeview ();
-
-       resume_redisplay ();
 }
 
 void
@@ -1009,7 +1024,7 @@ EditorRoutes::hide_all_tracks (bool /*with_select*/)
        TreeModel::Children rows = _model->children();
        TreeModel::Children::iterator i;
 
-       suspend_redisplay ();
+       DisplaySuspender ds;
 
        for (i = rows.begin(); i != rows.end(); ++i) {
 
@@ -1022,8 +1037,6 @@ EditorRoutes::hide_all_tracks (bool /*with_select*/)
 
                row[_columns.visible] = false;
        }
-
-       resume_redisplay ();
 }
 
 void
@@ -1032,7 +1045,7 @@ EditorRoutes::set_all_tracks_visibility (bool yn)
        TreeModel::Children rows = _model->children();
        TreeModel::Children::iterator i;
 
-       suspend_redisplay ();
+       DisplaySuspender ds;
 
        for (i = rows.begin(); i != rows.end(); ++i) {
 
@@ -1051,8 +1064,6 @@ EditorRoutes::set_all_tracks_visibility (bool yn)
         */
 
        sync_order_keys_from_treeview ();
-
-       resume_redisplay ();
 }
 
 void
@@ -1061,7 +1072,7 @@ EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
        TreeModel::Children rows = _model->children();
        TreeModel::Children::iterator i;
 
-       suspend_redisplay ();
+       DisplaySuspender ds;
 
        for (i = rows.begin(); i != rows.end(); ++i) {
 
@@ -1113,8 +1124,6 @@ EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
         */
 
        sync_order_keys_from_treeview ();
-
-       resume_redisplay ();
 }
 
 void
@@ -1359,11 +1368,10 @@ struct EditorOrderRouteSorter {
 void
 EditorRoutes::initial_display ()
 {
-       suspend_redisplay ();
+       DisplaySuspender ds;
        _model->clear ();
 
        if (!_session) {
-               resume_redisplay ();
                return;
        }
 
@@ -1390,8 +1398,6 @@ EditorRoutes::initial_display ()
                _editor->add_routes (r);
                
        }
-
-       resume_redisplay ();
 }
 
 void
@@ -1700,14 +1706,11 @@ EditorRoutes::show_tracks_with_regions_at_playhead ()
                }
        }
 
-       suspend_redisplay ();
+       DisplaySuspender ds;
 
        TreeModel::Children rows = _model->children ();
        for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
                TimeAxisView* tv = (*i)[_columns.tv];
                (*i)[_columns.visible] = (show.find (tv) != show.end());
        }
-
-       resume_redisplay ();
 }
-
index 9780307435adf3d3b38110a8961607cc06ea70e2..21a92fc423746252be3d1cf9423ec8f513e3d0f0 100644 (file)
@@ -42,10 +42,6 @@ public:
                _no_redisplay = true;
        }
 
-        void allow_redisplay () { 
-               _no_redisplay = false;
-       }
-
        void resume_redisplay () {
                _no_redisplay = false;
                redisplay ();
@@ -73,7 +69,7 @@ private:
        void build_menu ();
        void show_menu ();
         void sync_treeview_from_order_keys ();
-       void route_deleted (Gtk::TreeModel::Path const &);
+       void row_deleted (Gtk::TreeModel::Path const &);
        void visible_changed (std::string const &);
        void active_changed (std::string const &);
        void reordered (Gtk::TreeModel::Path const &, Gtk::TreeModel::iterator const &, int *);
@@ -156,6 +152,7 @@ private:
        bool _ignore_reorder;
        bool _no_redisplay;
         bool _adding_routes;
+        bool _route_deletion_in_progress;
 
        Gtk::Menu* _menu;
         Gtk::Widget* old_focus;
index 0a5ebf74102f45d1ff5d778e2ba80bebeb81a7f1..d1a002e60248271259b9aa574ae0fdfbe9f410f1 100644 (file)
 
 #include <gtk/gtkaction.h>
 
-#include "canvas/group.h"
+#include "canvas/container.h"
 #include "canvas/canvas.h"
+#include "canvas/ruler.h"
+#include "canvas/debug.h"
+#include "canvas/scroll_group.h"
 
 #include "ardour/session.h"
 #include "ardour/tempo.h"
 #include "ardour/profile.h"
 
 #include "gtkmm2ext/gtk_ui.h"
+#include "gtkmm2ext/keyboard.h"
 
+#include "ardour_ui.h"
 #include "editor.h"
 #include "editing.h"
 #include "actions.h"
-#include "gtk-custom-hruler.h"
 #include "gui_thread.h"
+#include "ruler_dialog.h"
 #include "time_axis_view.h"
 #include "editor_drag.h"
 #include "editor_cursors.h"
@@ -54,287 +59,137 @@ using namespace PBD;
 using namespace Gtk;
 using namespace Editing;
 
-Editor *Editor::ruler_editor;
-
 /* the order here must match the "metric" enums in editor.h */
 
-GtkCustomMetric Editor::ruler_metrics[4] = {
-       {1, Editor::_metric_get_timecode },
-       {1, Editor::_metric_get_bbt },
-       {1, Editor::_metric_get_samples },
-       {1, Editor::_metric_get_minsec }
-};
-
-void
-Editor::initialize_rulers ()
+class TimecodeMetric : public ArdourCanvas::Ruler::Metric
 {
-       ruler_editor = this;
-       ruler_grabbed_widget = 0;
-
-       _ruler_separator = new Gtk::HSeparator();
-       _ruler_separator->set_size_request(-1, 2);
-       _ruler_separator->set_name("TimebarPadding");
-       _ruler_separator->show();
-
-       _minsec_ruler = gtk_custom_hruler_new ();
-       minsec_ruler = Glib::wrap (_minsec_ruler);
-       minsec_ruler->set_name ("MinSecRuler");
-       minsec_ruler->set_size_request (-1, (int)timebar_height);
-       gtk_custom_ruler_set_metric (GTK_CUSTOM_RULER(_minsec_ruler), &ruler_metrics[ruler_metric_minsec]);
-       minsec_ruler->hide ();
-       minsec_ruler->set_no_show_all();
-
-       _timecode_ruler = gtk_custom_hruler_new ();
-       timecode_ruler = Glib::wrap (_timecode_ruler);
-       timecode_ruler->set_name ("TimecodeRuler");
-       timecode_ruler->set_size_request (-1, (int)timebar_height);
-       gtk_custom_ruler_set_metric (GTK_CUSTOM_RULER(_timecode_ruler), &ruler_metrics[ruler_metric_timecode]);
-       timecode_ruler->hide ();
-       timecode_ruler->set_no_show_all();
-       timecode_nmarks = 0;
-
-       _bbt_ruler = gtk_custom_hruler_new ();
-       bbt_ruler = Glib::wrap (_bbt_ruler);
-       bbt_ruler->set_name ("BBTRuler");
-       bbt_ruler->set_size_request (-1, (int)timebar_height);
-       gtk_custom_ruler_set_metric (GTK_CUSTOM_RULER(_bbt_ruler), &ruler_metrics[ruler_metric_bbt]);
-       bbt_ruler->hide ();
-       bbt_ruler->set_no_show_all();
-       bbt_nmarks = 0;
-
-       _samples_ruler = gtk_custom_hruler_new ();
-       samples_ruler = Glib::wrap (_samples_ruler);
-       samples_ruler->set_name ("SamplesRuler");
-       samples_ruler->set_size_request (-1, (int) timebar_height);
-       gtk_custom_ruler_set_metric (GTK_CUSTOM_RULER (_samples_ruler), &ruler_metrics[ruler_metric_samples]);
-       samples_ruler->hide ();
-       samples_ruler->set_no_show_all ();
-
-       _bbt_ruler = gtk_custom_hruler_new ();
-       bbt_ruler = Glib::wrap (_bbt_ruler);
-       bbt_ruler->set_name ("BBTRuler");
-       bbt_ruler->set_size_request (-1, (int)timebar_height);
-       gtk_custom_ruler_set_metric (GTK_CUSTOM_RULER(_bbt_ruler), &ruler_metrics[ruler_metric_bbt]);
-       bbt_ruler->hide ();
-       bbt_ruler->set_no_show_all();
-       minsec_ruler->hide ();
-       minsec_ruler->set_no_show_all();
-       minsec_nmarks = 0;
-
-       using namespace Box_Helpers;
-       BoxList & ruler_lab_children =  ruler_label_vbox.children();
-       BoxList & ruler_children =  time_canvas_vbox.children();
-       BoxList & lab_children =  time_bars_vbox.children();
-
-       BoxList::iterator canvaspos = ruler_children.begin();
+    public:
+       TimecodeMetric (Editor* e) : _editor (e) {}
 
-       lab_children.push_back (Element(meter_label, PACK_SHRINK, PACK_START));
-       lab_children.push_back (Element(tempo_label, PACK_SHRINK, PACK_START));
-       lab_children.push_back (Element(range_mark_label, PACK_SHRINK, PACK_START));
-       lab_children.push_back (Element(transport_mark_label, PACK_SHRINK, PACK_START));
-       lab_children.push_back (Element(cd_mark_label, PACK_SHRINK, PACK_START));
-       lab_children.push_back (Element(mark_label, PACK_SHRINK, PACK_START));
-       lab_children.push_back (Element(videotl_label, PACK_SHRINK, PACK_START));
-
-       ruler_lab_children.push_back (Element(minsec_label, PACK_SHRINK, PACK_START));
-       ruler_children.insert (canvaspos, Element(*minsec_ruler, PACK_SHRINK, PACK_START));
-       ruler_lab_children.push_back (Element(timecode_label, PACK_SHRINK, PACK_START));
-       ruler_children.insert (canvaspos, Element(*timecode_ruler, PACK_SHRINK, PACK_START));
-       ruler_lab_children.push_back (Element(samples_label, PACK_SHRINK, PACK_START));
-       ruler_children.insert (canvaspos, Element (*samples_ruler, PACK_SHRINK, PACK_START));
-       ruler_lab_children.push_back (Element(bbt_label, PACK_SHRINK, PACK_START));
-       ruler_children.insert (canvaspos, Element(*bbt_ruler, PACK_SHRINK, PACK_START));
-
-       timecode_ruler->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::SCROLL_MASK);
-       bbt_ruler->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::SCROLL_MASK);
-       samples_ruler->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::SCROLL_MASK);
-       minsec_ruler->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::SCROLL_MASK);
-
-       timecode_ruler->signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_release));
-       bbt_ruler->signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_release));
-       samples_ruler->signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_release));
-       minsec_ruler->signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_release));
-
-       timecode_ruler->signal_button_press_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_press));
-       bbt_ruler->signal_button_press_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_press));
-       samples_ruler->signal_button_press_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_press));
-       minsec_ruler->signal_button_press_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_press));
-
-       timecode_ruler->signal_motion_notify_event().connect (sigc::mem_fun(*this, &Editor::ruler_mouse_motion));
-       bbt_ruler->signal_motion_notify_event().connect (sigc::mem_fun(*this, &Editor::ruler_mouse_motion));
-       samples_ruler->signal_motion_notify_event().connect (sigc::mem_fun(*this, &Editor::ruler_mouse_motion));
-       minsec_ruler->signal_motion_notify_event().connect (sigc::mem_fun(*this, &Editor::ruler_mouse_motion));
-
-       timecode_ruler->signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::ruler_scroll));
-       bbt_ruler->signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::ruler_scroll));
-       samples_ruler->signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::ruler_scroll));
-       minsec_ruler->signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::ruler_scroll));
+       void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
+               _editor->metric_get_timecode (marks, lower, upper, maxchars);
+       }
 
-       visible_timebars = 0; /*this will be changed below */
-}
+    private:
+       Editor* _editor;
+};
 
-bool
-Editor::ruler_scroll (GdkEventScroll* event)
+class SamplesMetric : public ArdourCanvas::Ruler::Metric
 {
-       framepos_t xdelta;
-       int direction = event->direction;
-       bool handled = false;
-
-       switch (direction) {
-       case GDK_SCROLL_UP:
-               temporal_zoom_step (false);
-               handled = true;
-               break;
+    public:
+       SamplesMetric (Editor* e) : _editor (e) {}
 
-       case GDK_SCROLL_DOWN:
-               temporal_zoom_step (true);
-               handled = true;
-               break;
-
-       case GDK_SCROLL_LEFT:
-               xdelta = (current_page_samples() / 2);
-               if (leftmost_frame > xdelta) {
-                       reset_x_origin (leftmost_frame - xdelta);
-               } else {
-                       reset_x_origin (0);
-               }
-               handled = true;
-               break;
-
-       case GDK_SCROLL_RIGHT:
-               xdelta = (current_page_samples() / 2);
-               if (max_framepos - xdelta > leftmost_frame) {
-                       reset_x_origin (leftmost_frame + xdelta);
-               } else {
-                       reset_x_origin (max_framepos - current_page_samples());
-               }
-               handled = true;
-               break;
-
-       default:
-               /* what? */
-               break;
+       void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
+               _editor->metric_get_samples (marks, lower, upper, maxchars);
        }
 
-       return handled;
-}
-
+    private:
+       Editor* _editor;
+};
 
-bool
-Editor::ruler_button_press (GdkEventButton* ev)
+class BBTMetric : public ArdourCanvas::Ruler::Metric
 {
-       if (_session == 0) {
-               return false;
-       }
-
-       Widget * grab_widget = 0;
-
-       if (timecode_ruler->is_realized() && ev->window == timecode_ruler->get_window()->gobj()) {
-               grab_widget = timecode_ruler;
-       } else if (bbt_ruler->is_realized() && ev->window == bbt_ruler->get_window()->gobj()) {
-               grab_widget = bbt_ruler;
-       } else if (samples_ruler->is_realized() && ev->window == samples_ruler->get_window()->gobj()) {
-               grab_widget = samples_ruler;
-       } else if (minsec_ruler->is_realized() && ev->window == minsec_ruler->get_window()->gobj()) {
-               grab_widget = minsec_ruler;
-       }
+    public:
+       BBTMetric (Editor* e) : _editor (e) {}
 
-       if (grab_widget) {
-               grab_widget->add_modal_grab ();
-               ruler_grabbed_widget = grab_widget;
+       void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
+               _editor->metric_get_bbt (marks, lower, upper, maxchars);
        }
 
-       if (ev->button == 1) {
-               // Since we will locate the playhead on button release, cancel any running
-               // auditions.
-               if (_session->is_auditioning()) {
-                       _session->cancel_audition ();
-               }
-
-               /* playhead cursor drag: CursorDrag expects an event with
-                * canvas coordinates, so convert from window coordinates,
-                * since for now, rulers are still Gtk::Widgets.
-                */
+    private:
+       Editor* _editor;
+};
 
-               GdkEventButton canvas_ev = *ev;
-               ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (ev->x, ev->y));
-               canvas_ev.x = rint (d.x);
-               canvas_ev.y = rint (d.y);
+class MinsecMetric : public ArdourCanvas::Ruler::Metric
+{
+    public:
+       MinsecMetric (Editor* e) : _editor (e) {}
 
-               _drags->set (new CursorDrag (this, *playhead_cursor, false), reinterpret_cast<GdkEvent *> (&canvas_ev));
-               _dragging_playhead = true;
+       void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
+               _editor->metric_get_minsec (marks, lower, upper, maxchars);
        }
 
-       return true;
-}
+    private:
+       Editor* _editor;
+};
 
-bool
-Editor::ruler_button_release (GdkEventButton* ev)
+static ArdourCanvas::Ruler::Metric* _bbt_metric;
+static ArdourCanvas::Ruler::Metric* _timecode_metric;
+static ArdourCanvas::Ruler::Metric* _samples_metric;
+static ArdourCanvas::Ruler::Metric* _minsec_metric;
+
+void
+Editor::initialize_rulers ()
 {
-       if (_session == 0) {
-               return false;
-       }
+       ruler_grabbed_widget = 0;
+       Pango::FontDescription font (ARDOUR_UI::config()->get_canvasvar_SmallFont());
+
+       _timecode_metric = new TimecodeMetric (this);
+       _bbt_metric = new BBTMetric (this);
+       _minsec_metric = new MinsecMetric (this);
+       _samples_metric = new SamplesMetric (this);
+       
+       timecode_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_timecode_metric,
+                                                 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
+       timecode_ruler->set_font_description (font);
+       CANVAS_DEBUG_NAME (timecode_ruler, "timecode ruler");
+       timecode_nmarks = 0;
 
-       if (_drags->active ()) {
-               GdkEventButton canvas_ev = *ev;
-               ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (ev->x, ev->y));
-               canvas_ev.x = rint (d.x);
-               canvas_ev.x = rint (d.y);
-               _drags->end_grab (reinterpret_cast<GdkEvent*> (&canvas_ev));
-               _dragging_playhead = false;
-       }
+       samples_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_samples_metric,
+                                                ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
+       samples_ruler->set_font_description (font);
+       CANVAS_DEBUG_NAME (samples_ruler, "samples ruler");
 
-       if (ev->button == 3) {
-               
-               stop_canvas_autoscroll();
+       minsec_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_minsec_metric,
+                                               ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
+       minsec_ruler->set_font_description (font);
+       CANVAS_DEBUG_NAME (minsec_ruler, "minsec ruler");
+       minsec_nmarks = 0;
 
-               framepos_t where = window_event_sample ((GdkEvent*) ev);
+       bbt_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_bbt_metric,
+                                            ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
+       bbt_ruler->set_font_description (font);
+       CANVAS_DEBUG_NAME (bbt_ruler, "bbt ruler");
+       timecode_nmarks = 0;
 
-               snap_to (where);
-               popup_ruler_menu (where);
-       }
+       using namespace Box_Helpers;
+       BoxList & lab_children =  time_bars_vbox.children();
 
-       if (ruler_grabbed_widget) {
-               ruler_grabbed_widget->remove_modal_grab();
-               ruler_grabbed_widget = 0;
-       }
+       lab_children.push_back (Element(minsec_label, PACK_SHRINK, PACK_START));
+       lab_children.push_back (Element(timecode_label, PACK_SHRINK, PACK_START));
+       lab_children.push_back (Element(samples_label, PACK_SHRINK, PACK_START));
+       lab_children.push_back (Element(bbt_label, PACK_SHRINK, PACK_START));
+       lab_children.push_back (Element(meter_label, PACK_SHRINK, PACK_START));
+       lab_children.push_back (Element(tempo_label, PACK_SHRINK, PACK_START));
+       lab_children.push_back (Element(range_mark_label, PACK_SHRINK, PACK_START));
+       lab_children.push_back (Element(transport_mark_label, PACK_SHRINK, PACK_START));
+       lab_children.push_back (Element(cd_mark_label, PACK_SHRINK, PACK_START));
+       lab_children.push_back (Element(mark_label, PACK_SHRINK, PACK_START));
+       lab_children.push_back (Element(videotl_label, PACK_SHRINK, PACK_START));
 
-       return true;
+       /* 1 event handler to bind them all ... */
+
+       timecode_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), timecode_ruler, TimecodeRulerItem));
+       minsec_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), minsec_ruler, MinsecRulerItem));
+       bbt_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), bbt_ruler, BBTRulerItem));
+       samples_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), samples_ruler, SamplesRulerItem));
+       
+       visible_timebars = 0; /*this will be changed below */
 }
 
 bool
 Editor::ruler_label_button_release (GdkEventButton* ev)
 {
-       if (ev->button == 3) {
-               Gtk::Menu* m = dynamic_cast<Gtk::Menu*> (ActionManager::get_widget (X_("/RulerMenuPopup")));
-               if (m) {
-                       m->popup (1, ev->time);
+       if (Gtkmm2ext::Keyboard::is_context_menu_event (ev)) {
+               if (!ruler_dialog) {
+                       ruler_dialog = new RulerDialog ();
                }
+               ruler_dialog->present ();
        }
 
        return true;
 }
 
-
-bool
-Editor::ruler_mouse_motion (GdkEventMotion* ev)
-{
-       if (_session == 0) {
-               return false;
-       }
-
-       if (_drags->active ()) {
-               GdkEventMotion canvas_ev = *ev;
-               ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (ev->x, ev->y));
-               canvas_ev.x = rint (d.x);
-               canvas_ev.y = rint (d.y);
-               _drags->window_motion_handler (reinterpret_cast<GdkEvent*> (&canvas_ev), false);
-       }
-
-       return true;
-}
-
-
 void
 Editor::popup_ruler_menu (framepos_t where, ItemType t)
 {
@@ -376,12 +231,10 @@ Editor::popup_ruler_menu (framepos_t where, ItemType t)
 
        case TempoBarItem:
                ruler_items.push_back (MenuElem (_("New Tempo"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_tempo_event), where)));
-               ruler_items.push_back (SeparatorElem ());
                break;
 
        case MeterBarItem:
                ruler_items.push_back (MenuElem (_("New Meter"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_meter_event), where)));
-               ruler_items.push_back (SeparatorElem ());
                break;
 
        case VideoBarItem:
@@ -400,70 +253,19 @@ Editor::popup_ruler_menu (framepos_t where, ItemType t)
 
                ruler_items.push_back (CheckMenuElem (_("Lock")));
                {
-               Gtk::CheckMenuItem* vtl_lock = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
-               vtl_lock->set_active(is_video_timeline_locked());
-               vtl_lock->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_video_timeline_locked));
+                       Gtk::CheckMenuItem* vtl_lock = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
+                       vtl_lock->set_active(is_video_timeline_locked());
+                       vtl_lock->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_video_timeline_locked));
                }
-
-               ruler_items.push_back (SeparatorElem ());
                break;
 
        default:
                break;
        }
 
-       Glib::RefPtr<Action> action;
-
-       action = ActionManager::get_action ("Rulers", "toggle-minsec-ruler");
-       if (action) {
-               ruler_items.push_back (MenuElem (*action->create_menu_item()));
-       }
-       if (!Profile->get_sae()) {
-               action = ActionManager::get_action ("Rulers", "toggle-timecode-ruler");
-               if (action) {
-                       ruler_items.push_back (MenuElem (*action->create_menu_item()));
-               }
-       }
-       action = ActionManager::get_action ("Rulers", "toggle-samples-ruler");
-       if (action) {
-               ruler_items.push_back (MenuElem (*action->create_menu_item()));
-       }
-       action = ActionManager::get_action ("Rulers", "toggle-bbt-ruler");
-       if (action) {
-               ruler_items.push_back (MenuElem (*action->create_menu_item()));
-       }
-       action = ActionManager::get_action ("Rulers", "toggle-meter-ruler");
-       if (action) {
-               ruler_items.push_back (MenuElem (*action->create_menu_item()));
-       }
-       action = ActionManager::get_action ("Rulers", "toggle-tempo-ruler");
-       if (action) {
-               ruler_items.push_back (MenuElem (*action->create_menu_item()));
+       if (!ruler_items.empty()) {
+               editor_ruler_menu->popup (1, gtk_get_current_event_time());
        }
-       if (!Profile->get_sae()) {
-               action = ActionManager::get_action ("Rulers", "toggle-range-ruler");
-               if (action) {
-                       ruler_items.push_back (MenuElem (*action->create_menu_item()));
-               }
-       }
-       action = ActionManager::get_action ("Rulers", "toggle-loop-punch-ruler");
-       if (action) {
-               ruler_items.push_back (MenuElem (*action->create_menu_item()));
-       }
-       action = ActionManager::get_action ("Rulers", "toggle-cd-marker-ruler");
-       if (action) {
-               ruler_items.push_back (MenuElem (*action->create_menu_item()));
-       }
-       action = ActionManager::get_action ("Rulers", "toggle-marker-ruler");
-       if (action) {
-               ruler_items.push_back (MenuElem (*action->create_menu_item()));
-       }
-       action = ActionManager::get_action ("Rulers", "toggle-video-ruler");
-       if (action) {
-               ruler_items.push_back (MenuElem (*action->create_menu_item()));
-       }
-
-       editor_ruler_menu->popup (1, gtk_get_current_event_time());
 
        no_ruler_shown_update = false;
 }
@@ -601,81 +403,105 @@ Editor::restore_ruler_visibility ()
 void
 Editor::update_ruler_visibility ()
 {
-       int visible_rulers = 0;
+       int visible_timebars = 0;
 
        if (no_ruler_shown_update) {
                return;
        }
 
-       visible_timebars = 0;
+       /* the order of the timebars is fixed, so we have to go through each one
+        * and adjust its position depending on what is shown.
+        *
+        * Order: minsec, timecode, samples, bbt, meter, tempo, ranges,
+        * loop/punch, cd markers, location markers
+        */
+
+       double tbpos = 0.0;
+       double tbgpos = 0.0;
+       double old_unit_pos;
+
+#ifdef GTKOSX
+       /* gtk update probs require this (damn) */
+       meter_label.hide();
+       tempo_label.hide();
+       range_mark_label.hide();
+       transport_mark_label.hide();
+       cd_mark_label.hide();
+       mark_label.hide();
+       videotl_label.hide();
+#endif
 
        if (ruler_minsec_action->get_active()) {
-               visible_rulers++;
-               minsec_label.show ();
-               minsec_ruler->show ();
+               old_unit_pos = minsec_ruler->position().y;
+               if (tbpos != old_unit_pos) {
+                       minsec_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
+               }
+               minsec_ruler->show();
+               minsec_label.show();
+               tbpos += timebar_height;
+               tbgpos += timebar_height;
+               visible_timebars++;
        } else {
-               minsec_label.hide ();
-               minsec_ruler->hide ();
+               minsec_ruler->hide();
+               minsec_label.hide();
        }
 
        if (ruler_timecode_action->get_active()) {
-               visible_rulers++;
-               timecode_label.show ();
-               timecode_ruler->show ();
+               old_unit_pos = timecode_ruler->position().y;
+               if (tbpos != old_unit_pos) {
+                       timecode_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
+               }
+               timecode_ruler->show();
+               timecode_label.show();
+               tbpos += timebar_height;
+               tbgpos += timebar_height;
+               visible_timebars++;
        } else {
-               timecode_label.hide ();
-               timecode_ruler->hide ();
+               timecode_ruler->hide();
+               timecode_label.hide();
        }
 
        if (ruler_samples_action->get_active()) {
-               visible_rulers++;
-               samples_label.show ();
-               samples_ruler->show ();
+               old_unit_pos = samples_ruler->position().y;
+               if (tbpos != old_unit_pos) {
+                       samples_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
+               }
+               samples_ruler->show();
+               samples_label.show();
+               tbpos += timebar_height;
+               tbgpos += timebar_height;
+               visible_timebars++;
        } else {
-               samples_label.hide ();
-               samples_ruler->hide ();
+               samples_ruler->hide();
+               samples_label.hide();
        }
 
        if (ruler_bbt_action->get_active()) {
-               visible_rulers++;
-               bbt_label.show ();
-               bbt_ruler->show ();
+               old_unit_pos = bbt_ruler->position().y;
+               if (tbpos != old_unit_pos) {
+                       bbt_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
+               }
+               bbt_ruler->show();
+               bbt_label.show();
+               tbpos += timebar_height;
+               tbgpos += timebar_height;
+               visible_timebars++;
        } else {
-               bbt_label.hide ();
-               bbt_ruler->hide ();
+               bbt_ruler->hide();
+               bbt_label.hide();
        }
 
-       double tbpos = 0.0;
-       double tbgpos = 0.0;
-       double old_unit_pos;
-
-#ifdef GTKOSX
-       /* gtk update probs require this (damn) */
-       meter_label.hide();
-       tempo_label.hide();
-       range_mark_label.hide();
-       transport_mark_label.hide();
-       cd_mark_label.hide();
-       mark_label.hide();
-       videotl_label.hide();
-#endif
        if (ruler_meter_action->get_active()) {
                old_unit_pos = meter_group->position().y;
                if (tbpos != old_unit_pos) {
                        meter_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
                }
-               old_unit_pos = meter_bar_group->position().y;
-               if (tbgpos != old_unit_pos) {
-                       meter_bar_group->move (ArdourCanvas::Duple (0.0, tbgpos - old_unit_pos));
-               }
-               meter_bar_group->show();
                meter_group->show();
                meter_label.show();
                tbpos += timebar_height;
                tbgpos += timebar_height;
                visible_timebars++;
        } else {
-               meter_bar_group->hide();
                meter_group->hide();
                meter_label.hide();
        }
@@ -685,18 +511,12 @@ Editor::update_ruler_visibility ()
                if (tbpos != old_unit_pos) {
                        tempo_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
                }
-               old_unit_pos = tempo_bar_group->position().y;
-               if (tbgpos != old_unit_pos) {
-                       tempo_bar_group->move (ArdourCanvas::Duple (0.0, tbgpos - old_unit_pos));
-               }
-               tempo_bar_group->show();
                tempo_group->show();
                tempo_label.show();
                tbpos += timebar_height;
                tbgpos += timebar_height;
                visible_timebars++;
        } else {
-               tempo_bar_group->hide();
                tempo_group->hide();
                tempo_label.hide();
        }
@@ -706,11 +526,6 @@ Editor::update_ruler_visibility ()
                if (tbpos != old_unit_pos) {
                        range_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
                }
-               old_unit_pos = range_marker_bar_group->position().y;
-               if (tbgpos != old_unit_pos) {
-                       range_marker_bar_group->move (ArdourCanvas::Duple (0.0, tbgpos - old_unit_pos));
-               }
-               range_marker_bar_group->show();
                range_marker_group->show();
                range_mark_label.show();
 
@@ -718,7 +533,6 @@ Editor::update_ruler_visibility ()
                tbgpos += timebar_height;
                visible_timebars++;
        } else {
-               range_marker_bar_group->hide();
                range_marker_group->hide();
                range_mark_label.hide();
        }
@@ -728,18 +542,12 @@ Editor::update_ruler_visibility ()
                if (tbpos != old_unit_pos) {
                        transport_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
                }
-               old_unit_pos = transport_marker_bar_group->position().y;
-               if (tbgpos != old_unit_pos) {
-                       transport_marker_bar_group->move (ArdourCanvas::Duple (0.0, tbgpos - old_unit_pos));
-               }
-               transport_marker_bar_group->show();
                transport_marker_group->show();
                transport_mark_label.show();
                tbpos += timebar_height;
                tbgpos += timebar_height;
                visible_timebars++;
        } else {
-               transport_marker_bar_group->hide();
                transport_marker_group->hide();
                transport_mark_label.hide();
        }
@@ -749,11 +557,6 @@ Editor::update_ruler_visibility ()
                if (tbpos != old_unit_pos) {
                        cd_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
                }
-               old_unit_pos = cd_marker_bar_group->position().y;
-               if (tbgpos != old_unit_pos) {
-                       cd_marker_bar_group->move (ArdourCanvas::Duple (0.0, tbgpos - old_unit_pos));
-               }
-               cd_marker_bar_group->show();
                cd_marker_group->show();
                cd_mark_label.show();
                tbpos += timebar_height;
@@ -762,7 +565,6 @@ Editor::update_ruler_visibility ()
                // make sure all cd markers show up in their respective places
                update_cd_marker_display();
        } else {
-               cd_marker_bar_group->hide();
                cd_marker_group->hide();
                cd_mark_label.hide();
                // make sure all cd markers show up in their respective places
@@ -774,18 +576,12 @@ Editor::update_ruler_visibility ()
                if (tbpos != old_unit_pos) {
                        marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
                }
-               old_unit_pos = marker_bar_group->position().y;
-               if (tbgpos != old_unit_pos) {
-                       marker_bar_group->move (ArdourCanvas::Duple (0.0, tbgpos - old_unit_pos));
-               }
-               marker_bar_group->show();
                marker_group->show();
                mark_label.show();
                tbpos += timebar_height;
                tbgpos += timebar_height;
                visible_timebars++;
        } else {
-               marker_bar_group->hide();
                marker_group->hide();
                mark_label.hide();
        }
@@ -795,11 +591,6 @@ Editor::update_ruler_visibility ()
                if (tbpos != old_unit_pos) {
                        videotl_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
                }
-               old_unit_pos = videotl_group->position().y;
-               if (tbgpos != old_unit_pos) {
-                       videotl_group->move (ArdourCanvas::Duple (0.0, tbgpos - old_unit_pos));
-               }
-               videotl_group->show();
                videotl_group->show();
                videotl_label.show();
                tbpos += timebar_height * videotl_bar_height;
@@ -807,14 +598,17 @@ Editor::update_ruler_visibility ()
                visible_timebars+=videotl_bar_height;
                queue_visual_videotimeline_update();
        } else {
-               videotl_group->hide();
                videotl_group->hide();
                videotl_label.hide();
                update_video_timeline(true);
        }
 
-       ruler_label_vbox.set_size_request (-1, (int)(timebar_height * visible_rulers));
-       time_canvas_vbox.set_size_request (-1,-1);
+       time_bars_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars));
+
+       /* move hv_scroll_group (trackviews) to the end of the timebars
+        */
+
+       hv_scroll_group->set_y_position (timebar_height * visible_timebars);
 
        compute_fixed_ruler_scale ();
        update_fixed_rulers();
@@ -838,8 +632,7 @@ Editor::update_just_timecode ()
        framepos_t rightmost_frame = leftmost_frame + current_page_samples();
 
        if (ruler_timecode_action->get_active()) {
-               gtk_custom_ruler_set_range (GTK_CUSTOM_RULER(_timecode_ruler), leftmost_frame, rightmost_frame,
-                                           leftmost_frame, _session->current_end_frame());
+               timecode_ruler->set_range (leftmost_frame, rightmost_frame);
        }
 }
 
@@ -874,9 +667,9 @@ Editor::update_fixed_rulers ()
 
        compute_fixed_ruler_scale ();
 
-       ruler_metrics[ruler_metric_timecode].units_per_pixel = samples_per_pixel;
-       ruler_metrics[ruler_metric_samples].units_per_pixel = samples_per_pixel;
-       ruler_metrics[ruler_metric_minsec].units_per_pixel = samples_per_pixel;
+       _timecode_metric->units_per_pixel = samples_per_pixel;
+       _samples_metric->units_per_pixel = samples_per_pixel;
+       _minsec_metric->units_per_pixel = samples_per_pixel;
 
        rightmost_frame = leftmost_frame + current_page_samples();
 
@@ -885,18 +678,15 @@ Editor::update_fixed_rulers ()
        */
 
        if (ruler_timecode_action->get_active()) {
-               gtk_custom_ruler_set_range (GTK_CUSTOM_RULER(_timecode_ruler), leftmost_frame, rightmost_frame,
-                                           leftmost_frame, _session->current_end_frame());
+               timecode_ruler->set_range (leftmost_frame, rightmost_frame);
        }
 
        if (ruler_samples_action->get_active()) {
-               gtk_custom_ruler_set_range (GTK_CUSTOM_RULER (_samples_ruler), leftmost_frame, rightmost_frame,
-                                           leftmost_frame, _session->current_end_frame());
+               samples_ruler->set_range (leftmost_frame, rightmost_frame);
        }
 
        if (ruler_minsec_action->get_active()) {
-               gtk_custom_ruler_set_range (GTK_CUSTOM_RULER(_minsec_ruler), leftmost_frame, rightmost_frame,
-                                           leftmost_frame, _session->current_end_frame());
+               minsec_ruler->set_range (leftmost_frame, rightmost_frame);
        }
 }
 
@@ -911,40 +701,13 @@ Editor::update_tempo_based_rulers (ARDOUR::TempoMap::BBTPointList::const_iterato
        compute_bbt_ruler_scale (leftmost_frame, leftmost_frame+current_page_samples(),
                                 begin, end);
 
-       ruler_metrics[ruler_metric_bbt].units_per_pixel = samples_per_pixel;
+       _bbt_metric->units_per_pixel = samples_per_pixel;
 
        if (ruler_bbt_action->get_active()) {
-               gtk_custom_ruler_set_range (GTK_CUSTOM_RULER(_bbt_ruler), leftmost_frame, leftmost_frame+current_page_samples(),
-                                           leftmost_frame, _session->current_end_frame());
+               bbt_ruler->set_range (leftmost_frame, leftmost_frame+current_page_samples());
        }
 }
 
-/* Mark generation */
-
-gint
-Editor::_metric_get_timecode (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
-{
-       return ruler_editor->metric_get_timecode (marks, lower, upper, maxchars);
-}
-
-gint
-Editor::_metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
-{
-       return ruler_editor->metric_get_bbt (marks, lower, upper, maxchars);
-}
-
-gint
-Editor::_metric_get_samples (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
-{
-       return ruler_editor->metric_get_samples (marks, lower, upper, maxchars);
-}
-
-gint
-Editor::_metric_get_minsec (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
-{
-       return ruler_editor->metric_get_minsec (marks, lower, upper, maxchars);
-}
-
 void
 Editor::set_timecode_ruler_scale (framepos_t lower, framepos_t upper)
 {
@@ -1044,17 +807,18 @@ Editor::set_timecode_ruler_scale (framepos_t lower, framepos_t upper)
 
 }
 
-gint
-Editor::metric_get_timecode (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
+void
+Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
 {
        framepos_t pos;
        framecnt_t spacer;
        Timecode::Time timecode;
        gchar buf[16];
        gint n;
+       ArdourCanvas::Ruler::Mark mark;
 
        if (_session == 0) {
-               return 0;
+               return;
        }
 
        if (lower > (spacer = (framecnt_t)(128 * Editor::get_current_zoom ()))) {
@@ -1065,7 +829,6 @@ Editor::metric_get_timecode (GtkCustomRulerMark **marks, gdouble lower, gdouble
 
        pos = (framecnt_t) floor (lower);
 
-       *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * timecode_nmarks);
        switch (timecode_ruler_scale) {
        case timecode_show_bits:
 
@@ -1076,19 +839,21 @@ Editor::metric_get_timecode (GtkCustomRulerMark **marks, gdouble lower, gdouble
                        _session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */ );
                        if ((timecode.subframes % timecode_mark_modulo) == 0) {
                                if (timecode.subframes == 0) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                                        snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
                                } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                        snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
                                }
                        } else {
                                snprintf (buf, sizeof(buf)," ");
-                               (*marks)[n].style = GtkCustomRulerMarkMicro;
-
+                               mark.style = ArdourCanvas::Ruler::Mark::Micro;
+                               
                        }
-                       (*marks)[n].label = g_strdup (buf);
-                       (*marks)[n].position = pos;
+                       mark.label = buf;
+                       mark.position = pos;
+
+                       marks.push_back (mark);
 
                        // Increment subframes by one
                        Timecode::increment_subframes( timecode, _session->config.get_subframes_per_frame() );
@@ -1104,20 +869,22 @@ Editor::metric_get_timecode (GtkCustomRulerMark **marks, gdouble lower, gdouble
                        _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
                        if ((timecode.seconds % timecode_mark_modulo) == 0) {
                                if (timecode.seconds == 0) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
-                                       (*marks)[n].position = pos;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
+                                       mark.position = pos;
                                } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
-                                       (*marks)[n].position = pos;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
+                                       mark.position = pos;
                                }
                                snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
                        } else {
                                snprintf (buf, sizeof(buf)," ");
-                               (*marks)[n].style = GtkCustomRulerMarkMicro;
-                               (*marks)[n].position = pos;
+                               mark.style = ArdourCanvas::Ruler::Mark::Micro;
+                               mark.position = pos;
 
                        }
-                       (*marks)[n].label = g_strdup (buf);
+                       mark.label = buf;
+                       marks.push_back (mark);
+
                        Timecode::increment_seconds( timecode, _session->config.get_subframes_per_frame() );
                }
          break;
@@ -1131,19 +898,19 @@ Editor::metric_get_timecode (GtkCustomRulerMark **marks, gdouble lower, gdouble
                        _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
                        if ((timecode.minutes % timecode_mark_modulo) == 0) {
                                if (timecode.minutes == 0) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                                } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                }
                                snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
                        } else {
                                snprintf (buf, sizeof(buf)," ");
-                               (*marks)[n].style = GtkCustomRulerMarkMicro;
+                               mark.style = ArdourCanvas::Ruler::Mark::Micro;
 
                        }
-                       (*marks)[n].label = g_strdup (buf);
-                       (*marks)[n].position = pos;
-
+                       mark.label = buf;
+                       mark.position = pos;
+                       marks.push_back (mark);
                        Timecode::increment_minutes( timecode, _session->config.get_subframes_per_frame() );
                }
 
@@ -1157,16 +924,16 @@ Editor::metric_get_timecode (GtkCustomRulerMark **marks, gdouble lower, gdouble
                for (n = 0; n < timecode_nmarks; n++) {
                        _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
                        if ((timecode.hours % timecode_mark_modulo) == 0) {
-                               (*marks)[n].style = GtkCustomRulerMarkMajor;
+                               mark.style = ArdourCanvas::Ruler::Mark::Major;
                                snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
                        } else {
                                snprintf (buf, sizeof(buf)," ");
-                               (*marks)[n].style = GtkCustomRulerMarkMicro;
+                               mark.style = ArdourCanvas::Ruler::Mark::Micro;
 
                        }
-                       (*marks)[n].label = g_strdup (buf);
-                       (*marks)[n].position = pos;
-
+                       mark.label = buf;
+                       mark.position = pos;
+                       marks.push_back (mark);
                        Timecode::increment_hours( timecode, _session->config.get_subframes_per_frame() );
                }
          break;
@@ -1180,29 +947,29 @@ Editor::metric_get_timecode (GtkCustomRulerMark **marks, gdouble lower, gdouble
                        _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
                        if ((timecode.frames % timecode_mark_modulo) == 0)  {
                                if (timecode.frames == 0) {
-                                 (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                                } else {
-                                 (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                }
-                               (*marks)[n].position = pos;
+                               mark.position = pos;
                                snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
                        } else {
                                snprintf (buf, sizeof(buf)," ");
-                               (*marks)[n].style = GtkCustomRulerMarkMicro;
-                               (*marks)[n].position = pos;
+                               mark.style = ArdourCanvas::Ruler::Mark::Micro;
+                               mark.position = pos;
 
                        }
-                       (*marks)[n].label = g_strdup (buf);
+                       mark.label = buf;
+                       marks.push_back (mark);
                        Timecode::increment( timecode, _session->config.get_subframes_per_frame() );
                }
 
          break;
        }
-
-       return timecode_nmarks;
 }
 
 
+
 void
 Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper,
                                 ARDOUR::TempoMap::BBTPointList::const_iterator begin,
@@ -1332,17 +1099,26 @@ Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper,
        } else {
                bbt_ruler_scale =  bbt_show_ticks_detail;
        }
-
+       
        if ((bbt_ruler_scale == bbt_show_ticks_detail) && (lower_beat.beats == upper_beat.beats) && (upper_beat.ticks - lower_beat.ticks <= Timecode::BBT_Time::ticks_per_beat / 4)) {
                bbt_ruler_scale =  bbt_show_ticks_super_detail;
        }
 }
 
-gint
-Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint /*maxchars*/)
+static void 
+edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
+{
+       ArdourCanvas::Ruler::Mark copy = marks.back();
+       copy.label = newlabel;
+       marks.pop_back ();
+       marks.push_back (copy);
+}              
+
+void
+Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
 {
        if (_session == 0) {
-               return 0;
+               return;
        }
 
        TempoMap::BBTPointList::const_iterator i;
@@ -1353,7 +1129,6 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
        Timecode::BBT_Time next_beat;
        framepos_t next_beat_pos;
        uint32_t beats = 0;
-
        uint32_t tick = 0;
        uint32_t skip;
        uint32_t t;
@@ -1363,6 +1138,7 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
        double accumulated_error;
        bool i_am_accented = false;
        bool helper_active = false;
+       ArdourCanvas::Ruler::Mark mark;
 
        ARDOUR::TempoMap::BBTPointList::const_iterator begin;
        ARDOUR::TempoMap::BBTPointList::const_iterator end;
@@ -1370,7 +1146,7 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
        compute_current_bbt_points (lower, upper, begin, end);
 
        if (distance (begin, end) == 0) {
-               return 0;
+               return;
        }
 
        switch (bbt_ruler_scale) {
@@ -1379,32 +1155,32 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
                beats = distance (begin, end);
                bbt_nmarks = beats + 2;
 
-               *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
+               mark.label = "";
+               mark.position = lower;
+               mark.style = ArdourCanvas::Ruler::Mark::Micro;
+               marks.push_back (mark);
 
-               (*marks)[0].label = g_strdup(" ");
-               (*marks)[0].position = lower;
-               (*marks)[0].style = GtkCustomRulerMarkMicro;
-               
                for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
 
                        if ((*i).frame < lower && (bbt_bar_helper_on)) {
                                snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
-                               (*marks)[0].label = g_strdup (buf);
+                               edit_last_mark_label (marks, buf);
                                helper_active = true;
                        } else {
 
                                if ((*i).is_bar()) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                                        snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
                                } else if (((*i).beat % 2 == 1)) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
-                                       snprintf (buf, sizeof(buf), " ");
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
+                                       buf[0] = '\0';
                                } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMicro;
-                                       snprintf (buf, sizeof(buf), " ");
+                                       mark.style = ArdourCanvas::Ruler::Mark::Micro;
+                                       buf[0] = '\0';
                                }
-                               (*marks)[n].label =  g_strdup (buf);
-                               (*marks)[n].position = (*i).frame;
+                               mark.label = buf;
+                               mark.position = (*i).frame;
+                               marks.push_back (mark);
                                n++;
                        }
                }
@@ -1416,32 +1192,35 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
                bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
 
                bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
-               *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
+               
+               // could do marks.assign() here to preallocate
 
-               (*marks)[0].label = g_strdup(" ");
-               (*marks)[0].position = lower;
-               (*marks)[0].style = GtkCustomRulerMarkMicro;
+               mark.label = "";
+               mark.position = lower;
+               mark.style = ArdourCanvas::Ruler::Mark::Micro;
+               marks.push_back (mark);
 
                for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
 
                        if ((*i).frame < lower && (bbt_bar_helper_on)) {
                                snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
-                               (*marks)[0].label = g_strdup (buf);
+                               edit_last_mark_label (marks, buf);
                                helper_active = true;
                        } else {
 
                                if ((*i).is_bar()) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                                        snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
                                } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                        snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
                                }
                                if (((*i).frame < bbt_position_of_helper) && helper_active) {
-                                       snprintf (buf, sizeof(buf), " ");
+                                       buf[0] = '\0';
                                }
-                               (*marks)[n].label =  g_strdup (buf);
-                               (*marks)[n].position = (*i).frame;
+                               mark.label =  buf;
+                               mark.position = (*i).frame;
+                               marks.push_back (mark);
                                n++;
                        }
 
@@ -1476,8 +1255,7 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
                                        i_am_accented = true;
                                }
 
-                               snprintf (buf, sizeof(buf), " ");
-                               (*marks)[n].label = g_strdup (buf);
+                               mark.label = "";
 
                                /* Error compensation for float to framepos_t*/
                                accumulated_error += frame_skip_error;
@@ -1486,14 +1264,15 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
                                        accumulated_error -= 1.0f;
                                }
 
-                               (*marks)[n].position = pos;
+                               mark.position = pos;
 
                                if ((bbt_beat_subdivision > 4) && i_am_accented) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMicro;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Micro;
                                }
                                i_am_accented = false;
+                               marks.push_back (mark);
                                n++;
                        }
                }
@@ -1506,32 +1285,33 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
                bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
 
                bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
-               *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
 
-               (*marks)[0].label = g_strdup(" ");
-               (*marks)[0].position = lower;
-               (*marks)[0].style = GtkCustomRulerMarkMicro;
+               mark.label = "";
+               mark.position = lower;
+               mark.style = ArdourCanvas::Ruler::Mark::Micro;
+               marks.push_back (mark);
 
                for (n = 1,   i = begin; n < bbt_nmarks && i != end; ++i) {
 
                        if ((*i).frame < lower && (bbt_bar_helper_on)) {
                                snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
-                               (*marks)[0].label = g_strdup (buf);
+                               edit_last_mark_label (marks, buf);
                                helper_active = true;
                        } else {
 
                                if ((*i).is_bar()) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                                        snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
                                } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                        snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
                                }
                                if (((*i).frame < bbt_position_of_helper) && helper_active) {
-                                       snprintf (buf, sizeof(buf), " ");
+                                       buf[0] = '\0';
                                }
-                               (*marks)[n].label =  g_strdup (buf);
-                               (*marks)[n].position = (*i).frame;
+                               mark.label =  buf;
+                               mark.position = (*i).frame;
+                               marks.push_back (mark);
                                n++;
                        }
 
@@ -1569,10 +1349,10 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
                                if (i_am_accented && (pos > bbt_position_of_helper)){
                                        snprintf (buf, sizeof(buf), "%" PRIu32, tick);
                                } else {
-                                       snprintf (buf, sizeof(buf), " ");
+                                       buf[0] = '\0';
                                }
 
-                               (*marks)[n].label = g_strdup (buf);
+                               mark.label = buf;
 
                                /* Error compensation for float to framepos_t*/
                                accumulated_error += frame_skip_error;
@@ -1581,12 +1361,12 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
                                        accumulated_error -= 1.0f;
                                }
 
-                               (*marks)[n].position = pos;
+                               mark.position = pos;
 
                                if ((bbt_beat_subdivision > 4) && i_am_accented) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMicro;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Micro;
                                }
                                i_am_accented = false;
                                n++;
@@ -1601,32 +1381,33 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
                bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
 
                bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
-               *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
 
-               (*marks)[0].label = g_strdup(" ");
-               (*marks)[0].position = lower;
-               (*marks)[0].style = GtkCustomRulerMarkMicro;
+               mark.label = "";
+               mark.position = lower;
+               mark.style = ArdourCanvas::Ruler::Mark::Micro;
+               marks.push_back (mark);
 
                for (n = 1,   i = begin; n < bbt_nmarks && i != end; ++i) {
 
                        if ((*i).frame < lower && (bbt_bar_helper_on)) {
                                  snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
-                                 (*marks)[0].label = g_strdup (buf);
+                                 edit_last_mark_label (marks, buf);
                                  helper_active = true;
                        } else {
 
                                  if ((*i).is_bar()) {
-                                         (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
                                          snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
                                  } else {
-                                         (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                          snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
                                  }
                                  if (((*i).frame < bbt_position_of_helper) && helper_active) {
-                                         snprintf (buf, sizeof(buf), " ");
+                                         buf[0] = '\0';
                                  }
-                                 (*marks)[n].label =  g_strdup (buf);
-                                 (*marks)[n].position = (*i).frame;
+                                 mark.label =  buf;
+                                 mark.position = (*i).frame;
+                                 marks.push_back (mark);
                                  n++;
                        }
 
@@ -1664,10 +1445,10 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
                                  if (pos > bbt_position_of_helper) {
                                          snprintf (buf, sizeof(buf), "%" PRIu32, tick);
                                  } else {
-                                         snprintf (buf, sizeof(buf), " ");
+                                         buf[0] = '\0';
                                  }
 
-                                 (*marks)[n].label = g_strdup (buf);
+                                 mark.label = buf;
 
                                  /* Error compensation for float to framepos_t*/
                                  accumulated_error += frame_skip_error;
@@ -1676,14 +1457,15 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
                                          accumulated_error -= 1.0f;
                                  }
 
-                                 (*marks)[n].position = pos;
+                                 mark.position = pos;
 
                                  if ((bbt_beat_subdivision > 4) && i_am_accented) {
-                                         (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                  } else {
-                                         (*marks)[n].style = GtkCustomRulerMarkMicro;
+                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
                                  }
                                  i_am_accented = false;
+                                 marks.push_back (mark);
                                  n++;
                        }
                }
@@ -1692,35 +1474,34 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
 
        case bbt_over:
                        bbt_nmarks = 1;
-                       *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
                        snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
-                       (*marks)[0].style = GtkCustomRulerMarkMajor;
-                       (*marks)[0].label = g_strdup (buf);
-                       (*marks)[0].position = lower;
-                       n = 1;
+                       mark.style = ArdourCanvas::Ruler::Mark::Major;
+                       mark.label = buf;
+                       mark.position = lower;
+                       marks.push_back (mark);
 
          break;
 
        case bbt_show_64:
                        bbt_nmarks = (gint) (bbt_bars / 64) + 1;
-                       *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
                        for (n = 0,   i = begin; i != end && n < bbt_nmarks; i++) {
                                if ((*i).is_bar()) {
                                        if ((*i).bar % 64 == 1) {
                                                if ((*i).bar % 256 == 1) {
                                                        snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
-                                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                                                } else {
-                                                       snprintf (buf, sizeof(buf), " ");
+                                                       buf[0] = '\0';
                                                        if ((*i).bar % 256 == 129)  {
-                                                               (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                                               mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                                        } else {
-                                                               (*marks)[n].style = GtkCustomRulerMarkMicro;
+                                                               mark.style = ArdourCanvas::Ruler::Mark::Micro;
                                                        }
                                                }
-                                               (*marks)[n].label = g_strdup (buf);
-                                               (*marks)[n].position = (*i).frame;
-                                               n++;
+                                               mark.label = buf;
+                                               mark.position = (*i).frame;
+                                               marks.push_back (mark);
+                                               ++n;
                                        }
                                }
                        }
@@ -1728,24 +1509,24 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
 
        case bbt_show_16:
                        bbt_nmarks = (bbt_bars / 16) + 1;
-               *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
                for (n = 0,  i = begin; i != end && n < bbt_nmarks; i++) {
                        if ((*i).is_bar()) {
                          if ((*i).bar % 16 == 1) {
                                if ((*i).bar % 64 == 1) {
                                        snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                                } else {
-                                       snprintf (buf, sizeof(buf), " ");
+                                       buf[0] = '\0';
                                        if ((*i).bar % 64 == 33)  {
-                                               (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                               mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                        } else {
-                                               (*marks)[n].style = GtkCustomRulerMarkMicro;
+                                               mark.style = ArdourCanvas::Ruler::Mark::Micro;
                                        }
                                }
-                               (*marks)[n].label = g_strdup (buf);
-                               (*marks)[n].position = (*i).frame;
-                               n++;
+                               mark.label = buf;
+                               mark.position = (*i).frame;
+                               marks.push_back (mark);
+                               ++n;
                          }
                        }
                }
@@ -1753,58 +1534,54 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
 
        case bbt_show_4:
                bbt_nmarks = (bbt_bars / 4) + 1;
-               *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
                for (n = 0,   i = begin; i != end && n < bbt_nmarks; ++i) {
                        if ((*i).is_bar()) {
                          if ((*i).bar % 4 == 1) {
                                if ((*i).bar % 16 == 1) {
                                        snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                                } else {
-                                       snprintf (buf, sizeof(buf), " ");
+                                       buf[0] = '\0';
                                        if ((*i).bar % 16 == 9)  {
-                                               (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                               mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                        } else {
-                                               (*marks)[n].style = GtkCustomRulerMarkMicro;
+                                               mark.style = ArdourCanvas::Ruler::Mark::Micro;
                                        }
                                }
-                               (*marks)[n].label = g_strdup (buf);
-                               (*marks)[n].position = (*i).frame;
-                               n++;
+                               mark.label = buf;
+                               mark.position = (*i).frame;
+                               marks.push_back (mark);
+                               ++n;
                          }
                        }
                }
          break;
 
        case bbt_show_1:
-  //   default:
+//     default:
                bbt_nmarks = bbt_bars + 2;
-               *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks );
-               for (n = 0,  i = begin; i != end && n < bbt_nmarks; i++) {
+               for (n = 0,  i = begin; i != end && n < bbt_nmarks; ++i) {
                        if ((*i).is_bar()) {
                          if ((*i).bar % 4 == 1) {
                                        snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                          } else {
-                               snprintf (buf, sizeof(buf), " ");
-                               if ((*i).bar % 4 == 3)  {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
-                               } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMicro;
-                               }
+                                 buf[0] = '\0';
+                                 if ((*i).bar % 4 == 3)  {
+                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
+                                 } else {
+                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
+                                 }
                          }
-                       (*marks)[n].label = g_strdup (buf);
-                       (*marks)[n].position = (*i).frame;
-                       n++;
+                         mark.label = buf;
+                         mark.position = (*i).frame;
+                         marks.push_back (mark);
+                         ++n;
                        }
                }
-
-       break;
+               break;
 
        }
-
-       return n; //return the actual number of marks made, since we might have skipped some from fractional time signatures
-
 }
 
 void
@@ -1813,29 +1590,28 @@ Editor::set_samples_ruler_scale (framepos_t lower, framepos_t upper)
        _samples_ruler_interval = (upper - lower) / 5;
 }
 
-gint
-Editor::metric_get_samples (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
+void
+Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
 {
        framepos_t pos;
        framepos_t const ilower = (framepos_t) floor (lower);
        gchar buf[16];
        gint nmarks;
        gint n;
+       ArdourCanvas::Ruler::Mark mark;
 
        if (_session == 0) {
-               return 0;
+               return;
        }
 
        nmarks = 5;
-       *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * nmarks);
        for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
                snprintf (buf, sizeof(buf), "%" PRIi64, pos);
-               (*marks)[n].label = g_strdup (buf);
-               (*marks)[n].position = pos;
-               (*marks)[n].style = GtkCustomRulerMarkMajor;
+               mark.label = buf;
+               mark.position = pos;
+               mark.style = ArdourCanvas::Ruler::Mark::Major;
+               marks.push_back (mark);
        }
-
-       return nmarks;
 }
 
 static void
@@ -1854,13 +1630,13 @@ sample_to_clock_parts ( framepos_t sample,
        long millisecs;
 
        left = sample;
-       hrs = left / (sample_rate * 60 * 60);
-       left -= hrs * sample_rate * 60 * 60;
-       mins = left / (sample_rate * 60);
-       left -= mins * sample_rate * 60;
-       secs = left / sample_rate;
-       left -= secs * sample_rate;
-       millisecs = left * 1000 / sample_rate;
+       hrs = left / (sample_rate * 60 * 60 * 1000);
+       left -= hrs * sample_rate * 60 * 60 * 1000;
+       mins = left / (sample_rate * 60 * 1000);
+       left -= mins * sample_rate * 60 * 1000;
+       secs = left / (sample_rate * 1000);
+       left -= secs * sample_rate * 1000;
+       millisecs = left / sample_rate;
 
        *millisecs_p = millisecs;
        *secs_p = secs;
@@ -1880,7 +1656,7 @@ Editor::set_minsec_ruler_scale (framepos_t lower, framepos_t upper)
                return;
        }
 
-       fr = _session->frame_rate();
+       fr = _session->frame_rate() * 1000;
 
        /* to prevent 'flashing' */
        if (lower > (spacer = (framepos_t)(128 * Editor::get_current_zoom ()))) {
@@ -1889,7 +1665,7 @@ Editor::set_minsec_ruler_scale (framepos_t lower, framepos_t upper)
                lower = 0;
        }
        upper += spacer;
-       framecnt_t const range = upper - lower;
+       framecnt_t const range = (upper - lower) * 1000;
 
        if (range <  (fr / 50)) {
                minsec_mark_interval =  fr / 1000; /* show 1/1000 seconds */
@@ -1968,17 +1744,18 @@ Editor::set_minsec_ruler_scale (framepos_t lower, framepos_t upper)
        minsec_nmarks = 2 + (range / minsec_mark_interval);
 }
 
-gint
-Editor::metric_get_minsec (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
+void
+Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
 {
        framepos_t pos;
        framepos_t spacer;
        long hrs, mins, secs, millisecs;
        gchar buf[16];
        gint n;
+       ArdourCanvas::Ruler::Mark mark;
 
        if (_session == 0) {
-               return 0;
+               return;
        }
 
        /* to prevent 'flashing' */
@@ -1988,25 +1765,25 @@ Editor::metric_get_minsec (GtkCustomRulerMark **marks, gdouble lower, gdouble /*
                lower = 0;
        }
 
-       *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * minsec_nmarks);
-       pos = ((((framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
+       pos = (((1000 * (framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
        switch (minsec_ruler_scale) {
        case minsec_show_seconds:
                for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
                        sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
                        if (secs % minsec_mark_modulo == 0) {
                                if (secs == 0) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                                } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                }
                                snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
                        } else {
-                               snprintf (buf, sizeof(buf), " ");
-                               (*marks)[n].style = GtkCustomRulerMarkMicro;
+                               buf[0] = '\0';
+                               mark.style = ArdourCanvas::Ruler::Mark::Micro;
                        }
-                       (*marks)[n].label = g_strdup (buf);
-                       (*marks)[n].position = pos;
+                       mark.label = buf;
+                       mark.position = pos/1000.0;
+                       marks.push_back (mark);
                }
          break;
        case minsec_show_minutes:
@@ -2014,52 +1791,53 @@ Editor::metric_get_minsec (GtkCustomRulerMark **marks, gdouble lower, gdouble /*
                         sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
                         if (mins % minsec_mark_modulo == 0) {
                                 if (mins == 0) {
-                                        (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                        mark.style = ArdourCanvas::Ruler::Mark::Major;
                                 } else {
-                                        (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                        mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                 }
                                snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
                         } else {
-                                snprintf (buf, sizeof(buf), " ");
-                                (*marks)[n].style = GtkCustomRulerMarkMicro;
+                               buf[0] = '\0';
+                                mark.style = ArdourCanvas::Ruler::Mark::Micro;
                         }
-                        (*marks)[n].label = g_strdup (buf);
-                        (*marks)[n].position = pos;
+                        mark.label = buf;
+                        mark.position = pos/1000.0;
+                       marks.push_back (mark);
                 }
          break;
        case minsec_show_hours:
                 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
                         sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
                         if (hrs % minsec_mark_modulo == 0) {
-                                (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                mark.style = ArdourCanvas::Ruler::Mark::Major;
                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
                         } else {
-                                snprintf (buf, sizeof(buf), " ");
-                                (*marks)[n].style = GtkCustomRulerMarkMicro;
+                               buf[0] = '\0';
+                                mark.style = ArdourCanvas::Ruler::Mark::Micro;
                         }
-                        (*marks)[n].label = g_strdup (buf);
-                        (*marks)[n].position = pos;
+                        mark.label = buf;
+                        mark.position = pos/1000.0;
+                       marks.push_back (mark);
                 }
              break;
        case minsec_show_frames:
                for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
                        sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
                        if (millisecs % minsec_mark_modulo == 0) {
-                               if (secs == 0) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                               if (millisecs == 0) {
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                                } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                }
                                snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
                        } else {
-                               snprintf (buf, sizeof(buf), " ");
-                               (*marks)[n].style = GtkCustomRulerMarkMicro;
+                               buf[0] = '\0';
+                               mark.style = ArdourCanvas::Ruler::Mark::Micro;
                        }
-                       (*marks)[n].label = g_strdup (buf);
-                       (*marks)[n].position = pos;
+                       mark.label = buf;
+                       mark.position = pos/1000.0;
+                       marks.push_back (mark);
                }
          break;
        }
-
-       return minsec_nmarks;
 }
index e1027e4e9bd0418018e59340b16d2277d6aab233..3ad49e9edc8a3d1e318be86fc86669d4c4705aa6 100644 (file)
@@ -991,6 +991,15 @@ Editor::time_selection_changed ()
                return;
        }
 
+       /* XXX this is superficially inefficient. Hide the selection in all
+        * tracks, then show it in all selected tracks.
+        *
+        * However, if you investigate what this actually does, it isn't
+        * anywhere nearly as bad as it may appear. Remember: nothing is
+        * redrawn or even recomputed during these two loops - that only
+        * happens when we next render ...
+        */
+
        for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
                (*i)->hide_selection ();
        }
index 08b5acddc82c19305dfd73af474aa304941555e1..ed49ef35b3c3dca02bf4b4dc6cf4a05509437a65 100644 (file)
@@ -32,6 +32,7 @@ using namespace std;
 using namespace PBD;
 using namespace Gtk;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 
 EditorSnapshots::EditorSnapshots (Editor* e)
        : EditorComponent (e)
index 0e14ad2f08da3f452c77be19abd5d06a43108213..10a05943186b3fecab321ccc947f68b114b929ab 100644 (file)
@@ -54,16 +54,25 @@ EditorSummary::EditorSummary (Editor* e)
          _view_rectangle_x (0, 0),
          _view_rectangle_y (0, 0),
          _zoom_dragging (false),
-         _old_follow_playhead (false)
+         _old_follow_playhead (false),
+         _background_dirty (true)
 {
-       Region::RegionPropertyChanged.connect (region_property_connection, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context());
-       Route::RemoteControlIDChange.connect (route_ctrl_id_connection, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context());
-       _editor->playhead_cursor->PositionChanged.connect (position_connection, invalidator (*this), boost::bind (&EditorSummary::playhead_position_changed, this, _1), gui_context());
-
        add_events (Gdk::POINTER_MOTION_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
        set_flags (get_flags() | Gtk::CAN_FOCUS);
 }
 
+/** Handle a size allocation.
+ *  @param alloc GTK allocation.
+ */
+void
+EditorSummary::on_size_allocate (Gtk::Allocation& alloc)
+{
+       Gtk::EventBox::on_size_allocate (alloc);
+       _background_dirty = true;
+       set_dirty ();
+}
+
+
 /** Connect to a session.
  *  @param s Session.
  */
@@ -80,27 +89,32 @@ EditorSummary::set_session (Session* s)
         */
 
        if (_session) {
-               _session->StartTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context());
-               _session->EndTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context());
+               Region::RegionPropertyChanged.connect (region_property_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
+               Route::RemoteControlIDChange.connect (route_ctrl_id_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
+               _editor->playhead_cursor->PositionChanged.connect (position_connection, invalidator (*this), boost::bind (&EditorSummary::playhead_position_changed, this, _1), gui_context());
+               _session->StartTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
+               _session->EndTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
+               _editor->selection->RegionsChanged.connect (sigc::mem_fun(*this, &EditorSummary::set_background_dirty));
        }
 }
 
-/** Render the required regions to a cairo context.
- *  @param cr Context.
- */
 void
-EditorSummary::render (cairo_t* cr, cairo_rectangle_t*)
+EditorSummary::render_background_image ()
 {
-       /* background (really just the dividing lines between tracks */
+       int stride;
+       unsigned char *data;
+       stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, get_width ());
+       data = (unsigned char*) malloc (stride * get_height ());
+       _image = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24, get_width (), get_height (), stride);
+
+       cairo_t* cr = cairo_create (_image);
+
+       /* background (really just the dividing lines between tracks */
 
        cairo_set_source_rgb (cr, 0, 0, 0);
        cairo_rectangle (cr, 0, 0, get_width(), get_height());
        cairo_fill (cr);
 
-       if (_session == 0) {
-               return;
-       }
-
        /* compute start and end points for the summary */
 
        framecnt_t const session_length = _session->current_end_frame() - _session->current_start_frame ();
@@ -169,13 +183,39 @@ EditorSummary::render (cairo_t* cr, cairo_rectangle_t*)
        const double p = (_session->current_start_frame() - _start) * _x_scale;
        cairo_move_to (cr, p, 0);
        cairo_line_to (cr, p, get_height());
-       cairo_stroke (cr);
 
        double const q = (_session->current_end_frame() - _start) * _x_scale;
        cairo_move_to (cr, q, 0);
        cairo_line_to (cr, q, get_height());
        cairo_stroke (cr);
 
+       cairo_destroy (cr);
+}
+
+/** Render the required regions to a cairo context.
+ *  @param cr Context.
+ */
+void
+EditorSummary::render (cairo_t* cr, cairo_rectangle_t*)
+{
+
+       if (_session == 0) {
+               return;
+       }
+
+       if (!_image || _background_dirty) {
+               render_background_image ();
+               _background_dirty = false;
+       }
+
+       cairo_push_group (cr);
+       
+       /* Fill with the background image */
+
+       cairo_rectangle (cr, 0, 0, get_width(), get_height());
+       cairo_set_source_surface (cr, _image, 0, 0);
+       cairo_fill (cr);
+
        /* Render the view rectangle.  If there is an editor visual pending, don't update
           the view rectangle now --- wait until the expose event that we'll get after
           the visual change.  This prevents a flicker.
@@ -185,11 +225,9 @@ EditorSummary::render (cairo_t* cr, cairo_rectangle_t*)
                get_editor (&_view_rectangle_x, &_view_rectangle_y);
        }
 
-       cairo_move_to (cr, _view_rectangle_x.first, _view_rectangle_y.first);
-       cairo_line_to (cr, _view_rectangle_x.second, _view_rectangle_y.first);
-       cairo_line_to (cr, _view_rectangle_x.second, _view_rectangle_y.second);
-       cairo_line_to (cr, _view_rectangle_x.first, _view_rectangle_y.second);
-       cairo_line_to (cr, _view_rectangle_x.first, _view_rectangle_y.first);
+       int32_t width = _view_rectangle_x.second - _view_rectangle_x.first;
+       int32_t height = _view_rectangle_y.second - _view_rectangle_y.first;
+       cairo_rectangle (cr, _view_rectangle_x.first, _view_rectangle_y.first, width, height); 
        cairo_set_source_rgba (cr, 1, 1, 1, 0.25);
        cairo_fill_preserve (cr);
        cairo_set_line_width (cr, 1);
@@ -206,6 +244,8 @@ EditorSummary::render (cairo_t* cr, cairo_rectangle_t*)
        cairo_move_to (cr, ph, 0);
        cairo_line_to (cr, ph, get_height());
        cairo_stroke (cr);
+       cairo_pop_group_to_source (cr);
+       cairo_paint (cr);
        _last_playhead = ph;
 
 }
@@ -236,6 +276,13 @@ EditorSummary::render_region (RegionView* r, cairo_t* cr, double y) const
        cairo_stroke (cr);
 }
 
+void
+EditorSummary::set_background_dirty ()
+{
+       _background_dirty = true;
+       set_dirty ();
+}
+
 /** Set the summary so that just the overlays (viewbox, playhead etc.) will be re-rendered */
 void
 EditorSummary::set_overlays_dirty ()
@@ -981,10 +1028,11 @@ EditorSummary::routes_added (list<RouteTimeAxisView*> const & r)
                (*i)->route()->gui_changed.connect (*this, invalidator (*this), boost::bind (&EditorSummary::route_gui_changed, this, _1), gui_context ());
                boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> ((*i)->route ());
                if (tr) {
-                       tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context ());
+                       tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context ());
                }
        }
 
+       _background_dirty = true;
        set_dirty ();
 }
 
@@ -992,6 +1040,7 @@ void
 EditorSummary::route_gui_changed (string c)
 {
        if (c == "color") {
+               _background_dirty = true;
                set_dirty ();
        }
 }
index 87474188b1275b90e8deb3de817612eb89279a18..6f9f78d712ac5b1edb713cb103b0b2de36adf601 100644 (file)
@@ -39,9 +39,11 @@ public:
 
        void set_session (ARDOUR::Session *);
        void set_overlays_dirty ();
+       void set_background_dirty ();
        void routes_added (std::list<RouteTimeAxisView*> const &);
 
 private:
+       void on_size_allocate (Gtk::Allocation& alloc);
 
        enum Position {
                LEFT,
@@ -120,6 +122,9 @@ private:
        Position _zoom_position;
 
        bool _old_follow_playhead;
+       cairo_surface_t* _image;
+       void render_background_image ();
+       bool _background_dirty;
 
        PBD::ScopedConnectionList position_connection;
        PBD::ScopedConnection route_ctrl_id_connection;
index 10333f1eecb8f1b750180af821d033e6f7b648ca..44d0154dd48ad493bd83dbaf3c9e7a5d58b6ebd8 100644 (file)
@@ -40,6 +40,7 @@
 
 #include "canvas/canvas.h"
 #include "canvas/item.h"
+#include "canvas/line_set.h"
 
 #include "editor.h"
 #include "marker.h"
@@ -49,7 +50,6 @@
 #include "time_axis_view.h"
 #include "ardour_ui.h"
 #include "tempo_lines.h"
-#include "utils.h"
 
 #include "i18n.h"
 
@@ -176,7 +176,7 @@ Editor::draw_measures (ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
        }
 
        if (tempo_lines == 0) {
-               tempo_lines = new TempoLines (*_track_canvas, time_line_group, ArdourCanvas::COORD_MAX);
+               tempo_lines = new TempoLines (time_line_group, ArdourCanvas::LineSet::Vertical);
        }
        
        tempo_lines->draw (begin, end);
index 2528c823b7996ac9f75346ece542476c47378f87..765030507b649adaf16d916d36ca97fada65f1f8 100644 (file)
@@ -403,7 +403,7 @@ Editor::timefx_thread (void *arg)
         */
 
 #ifdef PLATFORM_WINDOWS
-       Sleep(2000);
+       Glib::usleep(2 * G_USEC_PER_SEC);
 #else
         struct timespec t = { 2, 0 };
         nanosleep (&t, 0);
index 715083698940ff8b60113327eff086aaca785b4f..27cf9b771990a58f931c05058ead443a3d1ae3be 100644 (file)
@@ -40,6 +40,7 @@
 #include "ardour/audio_backend.h"
 #include "ardour/audioengine.h"
 #include "ardour/mtdm.h"
+#include "ardour/mididm.h"
 #include "ardour/rc_configuration.h"
 #include "ardour/types.h"
 
@@ -57,11 +58,12 @@ using namespace Gtk;
 using namespace Gtkmm2ext;
 using namespace PBD;
 using namespace Glib;
+using namespace ARDOUR_UI_UTILS;
 
-static const unsigned int midi_tab = -1; /* not currently in use */
+static const unsigned int midi_tab = 2;
 static const unsigned int latency_tab = 1; /* zero-based, page zero is the main setup page */
 
-static const char* results_markup = X_("<span foreground=\"red\" style=\"italic\" size=\"larger\">%1</span>");
+static const char* results_markup = X_("<span weight=\"bold\" size=\"larger\">%1</span>");
 
 EngineControl::EngineControl ()
        : ArdourDialog (_("Audio/MIDI Setup"))
@@ -77,14 +79,15 @@ EngineControl::EngineControl ()
        , ports_adjustment (128, 8, 1024, 1, 16)
        , ports_spinner (ports_adjustment)
        , control_app_button (_("Device Control Panel"))
+       , midi_devices_button (_("Midi Device Setup"))
        , lm_measure_label (_("Measure"))
        , lm_use_button (_("Use results"))
        , lm_back_button (_("Back to settings ... (ignore results)"))
-       , lm_button (_("Calibrate..."))
+       , lm_button_audio (_("Calibrate Audio"))
        , lm_table (12, 3)
        , have_lm_results (false)
        , lm_running (false)
-       , midi_refresh_button (_("Refresh list"))
+       , midi_back_button (_("Back to settings"))
        , ignore_changes (0)
        , _desired_sample_rate (0)
        , started_at_least_once (false)
@@ -97,8 +100,7 @@ EngineControl::EngineControl ()
 
        set_name (X_("AudioMIDISetup"));
 
-       /* the backend combo is the one thing that is ALWAYS visible 
-        */
+       /* the backend combo is the one thing that is ALWAYS visible */
 
        vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
 
@@ -128,30 +130,26 @@ EngineControl::EngineControl ()
 
        basic_hbox.pack_start (basic_packer, false, false);
 
-       /* latency tab */
-
        /* latency measurement tab */
-       
+
        lm_title.set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Latency Measurement Tool")));
-       
+
        row = 0;
        lm_table.set_row_spacings (12);
        lm_table.set_col_spacings (6);
        lm_table.set_homogeneous (false);
-       
+
        lm_table.attach (lm_title, 0, 3, row, row+1, xopt, (AttachOptions) 0);
        row++;
 
-       Gtk::Label* preamble;
-
-       preamble = manage (new Label);
-       preamble->set_width_chars (60);
-       preamble->set_line_wrap (true);
-       preamble->set_markup (_("<span weight=\"bold\">Turn down the volume on your audio equipment to a very low level.</span>"));
+       lm_preamble.set_width_chars (60);
+       lm_preamble.set_line_wrap (true);
+       lm_preamble.set_markup (_("<span weight=\"bold\">Turn down the volume on your audio equipment to a very low level.</span>"));
 
-       lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
+       lm_table.attach (lm_preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
        row++;
 
+       Gtk::Label* preamble;
        preamble = manage (new Label);
        preamble->set_width_chars (60);
        preamble->set_line_wrap (true);
@@ -182,8 +180,8 @@ EngineControl::EngineControl ()
        lm_measure_button.add (lm_measure_label);
        lm_measure_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::latency_button_clicked));
        lm_use_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::use_latency_button_clicked));
-       lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
-       
+       lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
+
        lm_use_button.set_sensitive (false);
 
        /* Increase the default spacing around the labels of these three
@@ -213,13 +211,13 @@ EngineControl::EngineControl ()
        preamble->set_markup (_("When satisfied with the results, click the \"Use results\" button."));
        lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
 
-       ++row; // skip a row in the table 
-       ++row; // skip a row in the table 
+       ++row; // skip a row in the table
+       ++row; // skip a row in the table
 
        lm_table.attach (lm_results, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
 
-       ++row; // skip a row in the table 
-       ++row; // skip a row in the table 
+       ++row; // skip a row in the table
+       ++row; // skip a row in the table
 
        lm_table.attach (lm_measure_button, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
        lm_table.attach (lm_use_button, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
@@ -230,11 +228,13 @@ EngineControl::EngineControl ()
        lm_vbox.set_border_width (12);
        lm_vbox.pack_start (lm_table, false, false);
 
+       midi_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
+
        /* pack it all up */
 
        notebook.pages().push_back (TabElem (basic_vbox, _("Audio")));
-       // notebook.pages().push_back (TabElem (midi_vbox, _("MIDI")));
        notebook.pages().push_back (TabElem (lm_vbox, _("Latency")));
+       notebook.pages().push_back (TabElem (midi_vbox, _("MIDI")));
        notebook.set_border_width (12);
 
        notebook.set_show_tabs (false);
@@ -254,6 +254,11 @@ EngineControl::EngineControl ()
        input_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &input_channels));
        output_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &output_channels));
 
+       midi_devices_button.signal_clicked.connect (mem_fun (*this, &EngineControl::configure_midi_devices));
+       midi_devices_button.set_sensitive (false);
+       midi_devices_button.set_name ("generic button");
+       midi_devices_button.set_can_focus(true);
+
        control_app_button.signal_clicked().connect (mem_fun (*this, &EngineControl::control_app_button_clicked));
        manage_control_app_sensitivity ();
 
@@ -269,11 +274,15 @@ EngineControl::EngineControl ()
        ARDOUR::AudioEngine::instance()->Stopped.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
        ARDOUR::AudioEngine::instance()->Halted.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
 
-       backend_changed ();
-
-       if (audio_setup) {
+       if (audio_setup)
+       {
                set_state (*audio_setup);
        }
+       {
+               /* ignore: don't save state */
+               PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
+               backend_changed ();
+       }
 
        /* Connect to signals */
 
@@ -289,1533 +298,1806 @@ EngineControl::EngineControl ()
        output_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
 
        notebook.signal_switch_page().connect (sigc::mem_fun (*this, &EngineControl::on_switch_page));
- }
-
- void
- EngineControl::on_response (int response_id)
- {
-        ArdourDialog::on_response (response_id);
-
-        switch (response_id) {
-        case RESPONSE_APPLY:
-                push_state_to_backend (true);
-                break;
-        case RESPONSE_OK:
-                push_state_to_backend (true);
-                hide ();
-                break;
-        case RESPONSE_DELETE_EVENT: {
-                GdkEventButton ev;
-                ev.type = GDK_BUTTON_PRESS;
-                ev.button = 1;
-                on_delete_event ((GdkEventAny*) &ev);
-                break;
-        }
-        default:
-                hide ();
-        }
- }
-
- void
- EngineControl::build_notebook ()
- {
-        Label* label;
-        AttachOptions xopt = AttachOptions (FILL|EXPAND);
-
-        /* clear the table */
-
-        Gtkmm2ext::container_clear (basic_vbox);
-        Gtkmm2ext::container_clear (basic_packer);
-
-        if (control_app_button.get_parent()) {
-                control_app_button.get_parent()->remove (control_app_button);
-        }
-
-        label = manage (left_aligned_label (_("Audio System:")));
-        basic_packer.attach (*label, 0, 1, 0, 1, xopt, (AttachOptions) 0);
-        basic_packer.attach (backend_combo, 1, 2, 0, 1, xopt, (AttachOptions) 0);
-
-        lm_button.signal_clicked.connect (sigc::mem_fun (*this, &EngineControl::calibrate_latency));
-        lm_button.set_name ("generic button");
-        if (_have_control) {
-                build_full_control_notebook ();
-        } else {
-                build_no_control_notebook ();
-        }
-
-        basic_vbox.pack_start (basic_hbox, false, false);
-
-        if (_have_control) {
-                Gtk::HBox* hpacker = manage (new HBox);
-                hpacker->set_border_width (12);
-                hpacker->pack_start (control_app_button, false, false);
-                hpacker->show ();
-                control_app_button.show();
-                basic_vbox.pack_start (*hpacker);
-        }
-
-        basic_vbox.show_all ();
- }
-
- void
- EngineControl::build_full_control_notebook ()
- {
-        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
-        assert (backend);
-
-        using namespace Notebook_Helpers;
-        Label* label;
-        vector<string> strings;
-        AttachOptions xopt = AttachOptions (FILL|EXPAND);
-        int row = 1; // row zero == backend combo
-
-        /* start packing it up */
-
-        if (backend->requires_driver_selection()) {
-                label = manage (left_aligned_label (_("Driver:")));
-                basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
-                basic_packer.attach (driver_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
-                row++;
-        }
-
-        label = manage (left_aligned_label (_("Device:")));
-        basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
-        basic_packer.attach (device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
-        row++;
-
-        label = manage (left_aligned_label (_("Sample rate:")));
-        basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
-        basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
-        row++;
-
-
-        label = manage (left_aligned_label (_("Buffer size:")));
-        basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
-        basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
-        buffer_size_duration_label.set_alignment (0.0); /* left-align */
-        basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
-        row++;
-
-        input_channels.set_name ("InputChannels");
-        input_channels.set_flags(Gtk::CAN_FOCUS);
-        input_channels.set_digits(0);
-        input_channels.set_wrap(false);
-        output_channels.set_editable (true);
-
-        label = manage (left_aligned_label (_("Input Channels:")));
-        basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
-        basic_packer.attach (input_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
-        ++row;
-
-        output_channels.set_name ("OutputChannels");
-        output_channels.set_flags(Gtk::CAN_FOCUS);
-        output_channels.set_digits(0);
-        output_channels.set_wrap(false);
-        output_channels.set_editable (true);
-
-        label = manage (left_aligned_label (_("Output Channels:")));
-        basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
-        basic_packer.attach (output_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
-        ++row;
-
-        input_latency.set_name ("InputLatency");
-        input_latency.set_flags(Gtk::CAN_FOCUS);
-        input_latency.set_digits(0);
-        input_latency.set_wrap(false);
-        input_latency.set_editable (true);
-
-        label = manage (left_aligned_label (_("Hardware input latency:")));
-        basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
-        basic_packer.attach (input_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
-        label = manage (left_aligned_label (_("samples")));
-        basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
-        ++row;
-
-        output_latency.set_name ("OutputLatency");
-        output_latency.set_flags(Gtk::CAN_FOCUS);
-        output_latency.set_digits(0);
-        output_latency.set_wrap(false);
-        output_latency.set_editable (true);
-
-        label = manage (left_aligned_label (_("Hardware output latency:")));
-        basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
-        basic_packer.attach (output_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
-        label = manage (left_aligned_label (_("samples")));
-        basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
-
-        /* button spans 2 rows */
-
-        basic_packer.attach (lm_button, 3, 4, row-1, row+1, xopt, xopt);
-        ++row;
-
-        label = manage (left_aligned_label (_("MIDI System")));
-        basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
-        basic_packer.attach (midi_option_combo, 1, 2, row, row + 1, SHRINK, (AttachOptions) 0);
-        row++;
- }
-
- void
- EngineControl::build_no_control_notebook ()
- {
-        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
-        assert (backend);
-
-        using namespace Notebook_Helpers;
-        Label* label;
-        vector<string> strings;
-        AttachOptions xopt = AttachOptions (FILL|EXPAND);
-        int row = 1; // row zero == backend combo
-        const string msg = string_compose (_("The %1 audio backend was configured and started externally.\nThis limits your control over it."), backend->name());
-
-        label = manage (new Label);
-        label->set_markup (string_compose ("<span weight=\"bold\" foreground=\"red\">%1</span>", msg));
-        basic_packer.attach (*label, 0, 2, row, row + 1, xopt, (AttachOptions) 0);
-        row++;
-
-        if (backend->can_change_sample_rate_when_running()) {
-                label = manage (left_aligned_label (_("Sample rate:")));
-                basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
-                basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
-                row++;
-        }
-
-        if (backend->can_change_buffer_size_when_running()) {
-                label = manage (left_aligned_label (_("Buffer size:")));
-                basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
-                basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
-                buffer_size_duration_label.set_alignment (0.0); /* left-align */
-                basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
-                row++;
-        }
-
-        connect_disconnect_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::connect_disconnect_click));
-
-        basic_packer.attach (connect_disconnect_button, 0, 2, row, row+1, FILL, AttachOptions (0));
-        row++;
- }
-
- EngineControl::~EngineControl ()
- {
-        ignore_changes = true;
- }
-
- void
- EngineControl::disable_latency_tab ()
- {
-        vector<string> empty;
-        set_popdown_strings (lm_output_channel_combo, empty);
-        set_popdown_strings (lm_input_channel_combo, empty);
-        lm_measure_button.set_sensitive (false);
-        lm_use_button.set_sensitive (false);
- }
-
- void
- EngineControl::enable_latency_tab ()
- {
-        vector<string> outputs;
-        vector<string> inputs;
-
-        ARDOUR::AudioEngine::instance()->get_physical_outputs (ARDOUR::DataType::AUDIO, outputs);
-        ARDOUR::AudioEngine::instance()->get_physical_inputs (ARDOUR::DataType::AUDIO, inputs);
-
-        if (inputs.empty() || outputs.empty()) {
-                MessageDialog msg (_("Your selected audio configuration is playback- or capture-only.\n\nLatency calibration requires playback and capture"));
-                lm_measure_button.set_sensitive (false);
-                notebook.set_current_page (0);
-                msg.run ();
-                return;
-        }
-
-        if (!outputs.empty()) {
-                set_popdown_strings (lm_output_channel_combo, outputs);
-                lm_output_channel_combo.set_active_text (outputs.front());
-                lm_output_channel_combo.set_sensitive (true);
-        } else {
-                lm_output_channel_combo.set_sensitive (false);
-        }
-
-        if (!inputs.empty()) {
-                set_popdown_strings (lm_input_channel_combo, inputs);
-                lm_input_channel_combo.set_active_text (inputs.front());
-                lm_input_channel_combo.set_sensitive (true);
-        } else {
-                lm_input_channel_combo.set_sensitive (false);
-        }
-
-        lm_measure_button.set_sensitive (true);
- }
-
- void
- EngineControl::setup_midi_tab_for_backend ()
- {
-        string backend = backend_combo.get_active_text ();
-
-        Gtkmm2ext::container_clear (midi_vbox);
-
-        midi_vbox.set_border_width (12);
-        midi_device_table.set_border_width (12);
-
-        if (backend == "JACK") {
-                setup_midi_tab_for_jack ();
-        }
-
-        midi_vbox.pack_start (midi_device_table, true, true);
-        midi_vbox.pack_start (midi_refresh_button, false, false);
-        midi_vbox.show_all ();
-
-        midi_refresh_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::refresh_midi_display));
- }
-
- void
- EngineControl::setup_midi_tab_for_jack ()
- {
- }     
-
- void
- EngineControl::refresh_midi_display ()
- {
-        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
-        assert (backend);
-
-        vector<string> midi_inputs;
-        vector<string> midi_outputs;
-        int row  = 0;
-        AttachOptions xopt = AttachOptions (FILL|EXPAND);
-        Gtk::Label* l;
-
-        Gtkmm2ext::container_clear (midi_device_table);
-
-        backend->get_physical_inputs (ARDOUR::DataType::MIDI, midi_inputs);
-        backend->get_physical_outputs (ARDOUR::DataType::MIDI, midi_outputs);
-
-        midi_device_table.set_spacings (6);
-        midi_device_table.set_homogeneous (true);
-        midi_device_table.resize (midi_inputs.size() + midi_outputs.size() + 3, 1);
-
-        l = manage (new Label);
-        l->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("MIDI Inputs")));
-        midi_device_table.attach (*l, 0, 1, row, row + 1, xopt, AttachOptions (0));
-        l->set_alignment (0, 0.5);
-        row++;
-        l->show ();
-
-        for (vector<string>::iterator p = midi_inputs.begin(); p != midi_inputs.end(); ++p) {
-                l = manage (new Label ((*p).substr ((*p).find_last_of (':') + 1)));
-                l->set_alignment (0, 0.5);
-                midi_device_table.attach (*l, 0, 1, row, row + 1, xopt, AttachOptions (0));
-                l->show ();
-                row++;
-        }
-
-        row++; // extra row of spacing
-
-        l = manage (new Label);
-        l->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("MIDI Outputs")));
-        midi_device_table.attach (*l, 0, 1, row, row + 1, xopt, AttachOptions (0));
-        l->set_alignment (0, 0.5);
-        row++;
-        l->show ();
-
-        for (vector<string>::iterator p = midi_outputs.begin(); p != midi_outputs.end(); ++p) {
-                l = manage (new Label ((*p).substr ((*p).find_last_of (':') + 1)));
-                l->set_alignment (0, 0.5);
-                midi_device_table.attach (*l, 0, 1, row, row + 1, xopt, AttachOptions (0));
-                l->show ();
-                row++;
-        }
- }
-
- void
- EngineControl::update_sensitivity ()
- {
- }
-
- void
- EngineControl::backend_changed ()
- {
-        if (ignore_changes) {
-                return;
-        }
-
-        string backend_name = backend_combo.get_active_text();
-        boost::shared_ptr<ARDOUR::AudioBackend> backend;
-
-        if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
-                /* eh? setting the backend failed... how ? */
-                return;
-        }
-
-        _have_control = ARDOUR::AudioEngine::instance()->setup_required ();
-
-        build_notebook ();
-        setup_midi_tab_for_backend ();
-
-        if (backend->requires_driver_selection()) {
-                vector<string> drivers = backend->enumerate_drivers();
-
-                if (!drivers.empty()) {
-                        {
-                                PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
-                                set_popdown_strings (driver_combo, drivers);
-                                driver_combo.set_active_text (drivers.front());
-                        }
-
-                        driver_changed ();
-                }
-
-        } else {
-                driver_combo.set_sensitive (false);
-                /* this will change the device text which will cause a call to
-                 * device changed which will set up parameters
-                 */
-                list_devices ();
-        }
-
-        vector<string> midi_options = backend->enumerate_midi_options();
-
-        if (midi_options.size() == 1) {
-                /* only contains the "none" option */
-                midi_option_combo.set_sensitive (false);
-        } else {
-                if (_have_control) {
-                        set_popdown_strings (midi_option_combo, midi_options);
-                        midi_option_combo.set_active_text (midi_options.front());
-                        midi_option_combo.set_sensitive (true);
-                } else {
-                        midi_option_combo.set_sensitive (false);
-                }
-        }
-
-        maybe_display_saved_state ();
- }
-
- bool
- EngineControl::print_channel_count (Gtk::SpinButton* sb)
- {
-        uint32_t cnt = (uint32_t) sb->get_value();
-        if (cnt == 0) {
-                sb->set_text (_("all available channels"));
-        } else {
-                char buf[32];
-                snprintf (buf, sizeof (buf), "%d", cnt);
-                sb->set_text (buf);
-        }
-        return true;
- }
-
- void
- EngineControl::list_devices ()
- {
-        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
-        assert (backend);
-
-        /* now fill out devices, mark sample rates, buffer sizes insensitive */
-
-        vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
-
-        /* NOTE: Ardour currently does not display the "available" field of the
-         * returned devices.
-         *
-         * Doing so would require a different GUI widget than the combo
-         * box/popdown that we currently use, since it has no way to list
-         * items that are not selectable. Something more like a popup menu,
-         * which could have unselectable items, would be appropriate.
-         */
-
-        vector<string> available_devices;
-
-        for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
-                available_devices.push_back (i->name);
-        }
-
-        if (!available_devices.empty()) {
-
-                update_sensitivity ();
-
-                {
-                        PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
-                        set_popdown_strings (device_combo, available_devices);
-                        device_combo.set_active_text (available_devices.front());
-                }
-
-                device_changed ();
-
-                ok_button->set_sensitive (true);
-                apply_button->set_sensitive (true);
-
-        } else {
-                sample_rate_combo.set_sensitive (false);
-                buffer_size_combo.set_sensitive (false);
-                input_latency.set_sensitive (false);
-                output_latency.set_sensitive (false);
-                input_channels.set_sensitive (false);
-                output_channels.set_sensitive (false);
-                ok_button->set_sensitive (false);
-                apply_button->set_sensitive (false);
-        }
- }
-
- void
- EngineControl::driver_changed ()
- {
-        if (ignore_changes) {
-                return;
-        }
-
-        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
-        assert (backend);
-
-        backend->set_driver (driver_combo.get_active_text());
-        list_devices ();
-
-        maybe_display_saved_state ();
- }
-
- void
- EngineControl::device_changed ()
- {
-        if (ignore_changes) {
-                return;
-        }
-
-        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
-        assert (backend);
-        string device_name = device_combo.get_active_text ();
-        vector<string> s;
-
-        {
-                PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
-
-                /* don't allow programmatic change to combos to cause a
-                   recursive call to this method.
-                */
-
-                /* sample rates */
-
-                string desired;
-
-                vector<float> sr;
-
-                if (_have_control) {
-                        sr = backend->available_sample_rates (device_name);
-                } else {
-
-                        sr.push_back (8000.0f);
-                        sr.push_back (16000.0f);
-                        sr.push_back (32000.0f);
-                        sr.push_back (44100.0f);
-                        sr.push_back (48000.0f);
-                        sr.push_back (88200.0f);
-                        sr.push_back (96000.0f);
-                        sr.push_back (192000.0f);
-                        sr.push_back (384000.0f);
-                }
-
-                for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
-                        s.push_back (rate_as_string (*x));
-                        if (*x == _desired_sample_rate) {
-                                desired = s.back();
-                        }
-                }
-
-                if (!s.empty()) {
-                        sample_rate_combo.set_sensitive (true);
-                        set_popdown_strings (sample_rate_combo, s);
-
-                        if (desired.empty()) {
-                                sample_rate_combo.set_active_text (rate_as_string (backend->default_sample_rate()));
-                        } else {
-                                sample_rate_combo.set_active_text (desired);
-                        }
-
-                } else {
-                        sample_rate_combo.set_sensitive (false);
-                }
-
-                /* buffer sizes */
-
-                vector<uint32_t> bs;
-
-                if (_have_control) {
-                        bs = backend->available_buffer_sizes(device_name);
-                } else if (backend->can_change_buffer_size_when_running()) {
-                        bs.push_back (8);
-                        bs.push_back (16);
-                        bs.push_back (32);
-                        bs.push_back (64);
-                        bs.push_back (128);
-                        bs.push_back (256);
-                        bs.push_back (512);
-                        bs.push_back (1024);
-                        bs.push_back (2048);
-                        bs.push_back (4096);
-                        bs.push_back (8192);
-                }
-                s.clear ();
-                for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
-                        s.push_back (bufsize_as_string (*x));
-                }
-
-                if (!s.empty()) {
-                        buffer_size_combo.set_sensitive (true);
-                        set_popdown_strings (buffer_size_combo, s);
-
-                        buffer_size_combo.set_active_text (bufsize_as_string (backend->default_buffer_size()));
-                        show_buffer_duration ();
-                } else {
-                        buffer_size_combo.set_sensitive (false);
-                }
-
-                /* XXX theoretically need to set min + max channel counts here
-                 */
-
-                manage_control_app_sensitivity ();
-        }
-
-        /* pick up any saved state for this device */
-
-        maybe_display_saved_state ();
- }     
-
- string
- EngineControl::bufsize_as_string (uint32_t sz)
- {
-        /* Translators: "samples" is always plural here, so no
-           need for plural+singular forms.
-        */
-        char buf[32];
-        snprintf (buf, sizeof (buf), _("%u samples"), sz);
-        return buf;
- }
-
- void 
- EngineControl::sample_rate_changed ()
- {
-        if (ignore_changes) {
-                return;
-        }
-
-        /* reset the strings for buffer size to show the correct msec value
-           (reflecting the new sample rate).
-        */
+}
 
-        show_buffer_duration ();
-        save_state ();
+void
+EngineControl::on_response (int response_id)
+{
+       ArdourDialog::on_response (response_id);
+
+       switch (response_id) {
+               case RESPONSE_APPLY:
+                       push_state_to_backend (true);
+                       break;
+               case RESPONSE_OK:
+                       push_state_to_backend (true);
+                       hide ();
+                       break;
+               case RESPONSE_DELETE_EVENT:
+                       {
+                               GdkEventButton ev;
+                               ev.type = GDK_BUTTON_PRESS;
+                               ev.button = 1;
+                               on_delete_event ((GdkEventAny*) &ev);
+                               break;
+                       }
+               default:
+                       hide ();
+       }
+}
 
- }
+void
+EngineControl::build_notebook ()
+{
+       Label* label;
+       AttachOptions xopt = AttachOptions (FILL|EXPAND);
 
- void 
- EngineControl::buffer_size_changed ()
- {
-        if (ignore_changes) {
-                return;
-        }
+       /* clear the table */
 
-        show_buffer_duration ();
-        save_state ();
- }
+       Gtkmm2ext::container_clear (basic_vbox);
+       Gtkmm2ext::container_clear (basic_packer);
 
- void
- EngineControl::show_buffer_duration ()
- {
+       if (control_app_button.get_parent()) {
+               control_app_button.get_parent()->remove (control_app_button);
+       }
 
-        /* buffer sizes  - convert from just samples to samples + msecs for
-         * the displayed string
-         */
+       label = manage (left_aligned_label (_("Audio System:")));
+       basic_packer.attach (*label, 0, 1, 0, 1, xopt, (AttachOptions) 0);
+       basic_packer.attach (backend_combo, 1, 2, 0, 1, xopt, (AttachOptions) 0);
 
-        string bs_text = buffer_size_combo.get_active_text ();
-        uint32_t samples = atoi (bs_text); /* will ignore trailing text */
-        uint32_t rate = get_rate();
+       lm_button_audio.signal_clicked.connect (sigc::mem_fun (*this, &EngineControl::calibrate_audio_latency));
+       lm_button_audio.set_name ("generic button");
+       lm_button_audio.set_can_focus(true);
 
-        /* Translators: "msecs" is ALWAYS plural here, so we do not
-           need singular form as well.
-        */
-        /* Developers: note the hard-coding of a double buffered model
-           in the (2 * samples) computation of latency. we always start
-           the audiobackend in this configuration.
-        */
-        char buf[32];
-        snprintf (buf, sizeof (buf), _("(%.1f msecs)"), (2 * samples) / (rate/1000.0));
-        buffer_size_duration_label.set_text (buf);
- }
-
- void
- EngineControl::midi_option_changed ()
- {
-        if (!ignore_changes) {
-                save_state ();
-        }
- }
-
- void
- EngineControl::parameter_changed ()
- {
-        if (!ignore_changes) {
-                save_state ();
-        }
- }
-
- EngineControl::State*
- EngineControl::get_matching_state (const string& backend,
-                                   const string& driver,
-                                   const string& device)
- {
-        for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
-                if ((*i).backend == backend &&
-                    (*i).driver == driver &&
-                    (*i).device == device) {
-                        return &(*i);
-                }
-        }
-        return 0;
- }
-
- EngineControl::State*
- EngineControl::get_saved_state_for_currently_displayed_backend_and_device ()
- {
-        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
-
-        if (backend) {
-                return get_matching_state (backend_combo.get_active_text(),
-                                           (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
-                                           device_combo.get_active_text());
-        }
-
-
-        return get_matching_state (backend_combo.get_active_text(),
-                                   string(),
-                                   device_combo.get_active_text());
- }
-
- EngineControl::State*
- EngineControl::save_state ()
- {
-        if (!_have_control) {
-                return 0;
-        }
-
-        bool existing = true;
-        State* state = get_saved_state_for_currently_displayed_backend_and_device ();
-
-        if (!state) {
-                existing = false;
-                state = new State;
-        }
-
-        store_state (*state);
-
-        if (!existing) {
-                states.push_back (*state);
-        }
-
-        return state;
- }
-
- void
- EngineControl::store_state (State& state)
- {
-        state.backend = get_backend ();
-        state.driver = get_driver ();
-        state.device = get_device_name ();
-        state.sample_rate = get_rate ();
-        state.buffer_size = get_buffer_size ();
-        state.input_latency = get_input_latency ();
-        state.output_latency = get_output_latency ();
-        state.input_channels = get_input_channels ();
-        state.output_channels = get_output_channels ();
-        state.midi_option = get_midi_option ();
- }
-
- void
- EngineControl::maybe_display_saved_state ()
- {
-        if (!_have_control) {
-                return;
-        }
-
-        State* state = get_saved_state_for_currently_displayed_backend_and_device ();
-
-        if (state) {
-                PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
-
-                if (!_desired_sample_rate) {
-                        sample_rate_combo.set_active_text (rate_as_string (state->sample_rate));
-                }
-                buffer_size_combo.set_active_text (bufsize_as_string (state->buffer_size));
-                /* call this explicitly because we're ignoring changes to
-                   the controls at this point.
-                */
-                show_buffer_duration ();
-                input_latency.set_value (state->input_latency);
-                output_latency.set_value (state->output_latency);
-
-                if (!state->midi_option.empty()) {
-                        midi_option_combo.set_active_text (state->midi_option);
-                }
-        }
- }
+       if (_have_control) {
+               build_full_control_notebook ();
+       } else {
+               build_no_control_notebook ();
+       }
 
- XMLNode&
- EngineControl::get_state ()
- {
-        XMLNode* root = new XMLNode ("AudioMIDISetup");
-        std::string path;
-
-        if (!states.empty()) {
-                XMLNode* state_nodes = new XMLNode ("EngineStates");
-
-                for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
-
-                        XMLNode* node = new XMLNode ("State");
-
-                        node->add_property ("backend", (*i).backend);
-                        node->add_property ("driver", (*i).driver);
-                        node->add_property ("device", (*i).device);
-                        node->add_property ("sample-rate", (*i).sample_rate);
-                        node->add_property ("buffer-size", (*i).buffer_size);
-                        node->add_property ("input-latency", (*i).input_latency);
-                        node->add_property ("output-latency", (*i).output_latency);
-                        node->add_property ("input-channels", (*i).input_channels);
-                        node->add_property ("output-channels", (*i).output_channels);
-                        node->add_property ("active", (*i).active ? "yes" : "no");
-                        node->add_property ("midi-option", (*i).midi_option);
-
-                        state_nodes->add_child_nocopy (*node);
-                }
-
-                root->add_child_nocopy (*state_nodes);
-        }
-
-        return *root;
- }
-
- void
- EngineControl::set_state (const XMLNode& root)
- {
-        XMLNodeList          clist, cclist;
-        XMLNodeConstIterator citer, cciter;
-        XMLNode* child;
-        XMLNode* grandchild;
-        XMLProperty* prop = NULL;
-
-        if (root.name() != "AudioMIDISetup") {
-                return;
-        }
-
-        clist = root.children();
-
-        states.clear ();
-
-        for (citer = clist.begin(); citer != clist.end(); ++citer) {
-
-                child = *citer;
-
-                if (child->name() != "EngineStates") {
-                        continue;
-                }
-
-                cclist = child->children();
-
-                for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
-                        State state;
-
-                        grandchild = *cciter;
-
-                        if (grandchild->name() != "State") {
-                                continue;
-                        }
-
-                        if ((prop = grandchild->property ("backend")) == 0) {
-                                continue;
-                        }
-                        state.backend = prop->value ();
-
-                        if ((prop = grandchild->property ("driver")) == 0) {
-                                continue;
-                        }
-                        state.driver = prop->value ();
-
-                        if ((prop = grandchild->property ("device")) == 0) {
-                                continue;
-                        }
-                        state.device = prop->value ();
-
-                        if ((prop = grandchild->property ("sample-rate")) == 0) {
-                                continue;
-                        }
-                        state.sample_rate = atof (prop->value ());
-
-                        if ((prop = grandchild->property ("buffer-size")) == 0) {
-                                continue;
-                        }
-                        state.buffer_size = atoi (prop->value ());
-
-                        if ((prop = grandchild->property ("input-latency")) == 0) {
-                                continue;
-                        }
-                        state.input_latency = atoi (prop->value ());
-
-                        if ((prop = grandchild->property ("output-latency")) == 0) {
-                                continue;
-                        }
-                        state.output_latency = atoi (prop->value ());
-
-                        if ((prop = grandchild->property ("input-channels")) == 0) {
-                                continue;
-                        }
-                        state.input_channels = atoi (prop->value ());
-
-                        if ((prop = grandchild->property ("output-channels")) == 0) {
-                                continue;
-                        }
-                        state.output_channels = atoi (prop->value ());
-
-                        if ((prop = grandchild->property ("active")) == 0) {
-                                continue;
-                        }
-                        state.active = string_is_affirmative (prop->value ());
-
-                        if ((prop = grandchild->property ("midi-option")) == 0) {
-                                continue;
-                        }
-                        state.midi_option = prop->value ();
-
-                        states.push_back (state);
-                }
-        }
-
-        /* now see if there was an active state and switch the setup to it */
-
-        for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
-
-                if ((*i).active) {
-                        ignore_changes++;
-                        backend_combo.set_active_text ((*i).backend);
-                        driver_combo.set_active_text ((*i).driver);
-                        device_combo.set_active_text ((*i).device);
-                        sample_rate_combo.set_active_text (rate_as_string ((*i).sample_rate));
-                        buffer_size_combo.set_active_text (bufsize_as_string ((*i).buffer_size));
-                        input_latency.set_value ((*i).input_latency);
-                        output_latency.set_value ((*i).output_latency);
-                        midi_option_combo.set_active_text ((*i).midi_option);
-                        ignore_changes--;
-                        break;
-                }
-        }
- }
-
- int
- EngineControl::push_state_to_backend (bool start)
- {
-        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
-
-        if (!backend) {
-                return 0;
-        }
-
-        /* figure out what is going to change */
-
-        bool restart_required = false;
-        bool was_running = ARDOUR::AudioEngine::instance()->running();
-        bool change_driver = false;
-        bool change_device = false;
-        bool change_rate = false;
-        bool change_bufsize = false;
-        bool change_latency = false;
-        bool change_channels = false;
-        bool change_midi = false;
-
-        uint32_t ochan = get_output_channels ();
-        uint32_t ichan = get_input_channels ();
-
-        if (_have_control) {
-
-                if (started_at_least_once) {
-
-                        /* we can control the backend */
-
-                        if (backend->requires_driver_selection()) {
-                                if (get_driver() != backend->driver_name()) {
-                                        change_driver = true;
-                                }
-                        }
-
-                        if (get_device_name() != backend->device_name()) {
-                                change_device = true;
-                        }
-
-                        if (get_rate() != backend->sample_rate()) {
-                                change_rate = true;
-                        }
-
-                        if (get_buffer_size() != backend->buffer_size()) {
-                                change_bufsize = true;
-                        }
-
-                        if (get_midi_option() != backend->midi_option()) {
-                                change_midi = true;
-                        }
-
-                        /* zero-requested channels means "all available" */
-
-                        if (ichan == 0) {
-                                ichan = backend->input_channels();
-                        }
-
-                        if (ochan == 0) {
-                                ochan = backend->output_channels();
-                        }
-
-                        if (ichan != backend->input_channels()) {
-                                change_channels = true;
-                        }
-
-                        if (ochan != backend->output_channels()) {
-                                change_channels = true;
-                        }
-
-                        if (get_input_latency() != backend->systemic_input_latency() ||
-                            get_output_latency() != backend->systemic_output_latency()) {
-                                change_latency = true;
-                        }
-                } else {
-                        /* backend never started, so we have to force a group
-                           of settings.
-                        */
-                        change_device = true;
-                        if (backend->requires_driver_selection()) {
-                                change_driver = true;
-                        }
-                        change_rate = true;
-                        change_bufsize = true;
-                        change_channels = true;
-                        change_latency = true;
-                        change_midi = true;
-                }
-
-        } else {
-
-                /* we have no control over the backend, meaning that we can
-                 * only possibly change sample rate and buffer size.
-                 */
-
-
-                if (get_rate() != backend->sample_rate()) {
-                        change_bufsize = true;
-                }
-
-                if (get_buffer_size() != backend->buffer_size()) {
-                        change_bufsize = true;
-                }
-        }
-
-        if (!_have_control) {
-
-                /* We do not have control over the backend, so the best we can
-                 * do is try to change the sample rate and/or bufsize and get
-                 * out of here.
-                 */
-
-                if (change_rate && !backend->can_change_sample_rate_when_running()) {
-                        return 1;
-                }
-
-                if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
-                        return 1;
-                }
-
-                if (change_rate) {
-                        backend->set_sample_rate (get_rate());
-                }
-
-                if (change_bufsize) {
-                        backend->set_buffer_size (get_buffer_size());
-                }
-
-                if (start) {
-                        if (ARDOUR::AudioEngine::instance()->start ()) {
-                                error << string_compose (_("Could not start backend engine %1"), backend->name()) << endmsg;
-                                return -1;
-                        }
-                }
-
-                post_push ();
-
-                return 0;
-        } 
-
-        /* determine if we need to stop the backend before changing parameters */
-
-        if (change_driver || change_device || change_channels || change_latency ||
-            (change_rate && !backend->can_change_sample_rate_when_running()) ||
-            change_midi ||
-            (change_bufsize && !backend->can_change_buffer_size_when_running())) {
-                restart_required = true;
-        } else {
-                restart_required = false;
-        }
-
-        if (was_running) {
-
-                if (!change_driver && !change_device && !change_channels && !change_latency && !change_midi) {
-                        /* no changes in any parameters that absolutely require a
-                         * restart, so check those that might be changeable without a
-                         * restart
-                         */
-
-                        if (change_rate && !backend->can_change_sample_rate_when_running()) {
-                                /* can't do this while running ... */
-                                restart_required = true;
-                        }
-
-                        if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
-                                /* can't do this while running ... */
-                                restart_required = true;
-                        }
-                }
-        }
-
-        if (was_running) {
-                if (restart_required) {
-                        if (ARDOUR_UI::instance()->disconnect_from_engine ()) {
-                                return -1;
-                        }
-                }
-        }
-
-
-        if (change_driver && backend->set_driver (get_driver())) {
-                error << string_compose (_("Cannot set driver to %1"), get_driver()) << endmsg;
-                return -1;
-        }
-        if (change_device && backend->set_device_name (get_device_name())) {
-                error << string_compose (_("Cannot set device name to %1"), get_device_name()) << endmsg;
-                return -1;
-        }
-        if (change_rate && backend->set_sample_rate (get_rate())) {
-                error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
-                return -1;
-        }
-        if (change_bufsize && backend->set_buffer_size (get_buffer_size())) {
-                error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
-                return -1;
-        }
-
-        if (change_channels || get_input_channels() == 0 || get_output_channels() == 0) {
-                if (backend->set_input_channels (get_input_channels())) {
-                        error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
-                        return -1;
-                }
-                if (backend->set_output_channels (get_output_channels())) {
-                        error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
-                        return -1;
-                }
-        }
-        if (change_latency) {
-                if (backend->set_systemic_input_latency (get_input_latency())) {
-                        error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
-                        return -1;
-                }
-                if (backend->set_systemic_output_latency (get_output_latency())) {
-                        error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
-                        return -1;
-                }
-        }
-
-        if (change_midi) {
-                backend->set_midi_option (get_midi_option());
-        }
-
-        if (start || (was_running && restart_required)) {
-                if (ARDOUR_UI::instance()->reconnect_to_engine()) {
-                        return -1;
-                }
-        }
-
-        post_push ();
-
-        return 0;
- }
-
- void
- EngineControl::post_push ()
- {
-        /* get a pointer to the current state object, creating one if
-         * necessary
-         */
-
-        if (_have_control) {
-                State* state = get_saved_state_for_currently_displayed_backend_and_device ();
-
-                if (!state) {
-                        state = save_state ();
-                        assert (state);
-                }
-
-                /* all off */
-
-                for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
-                        (*i).active = false;
-                }
-
-                /* mark this one active (to be used next time the dialog is
-                 * shown)
-                 */
-
-                state->active = true;
-
-                manage_control_app_sensitivity ();
-        }
-
-        /* schedule a redisplay of MIDI ports */
-
-        Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &EngineControl::refresh_midi_display), false), 1000);
- }
-
-
- float
- EngineControl::get_rate () const
- {
-        float r = atof (sample_rate_combo.get_active_text ());
-        /* the string may have been translated with an abbreviation for
-         * thousands, so use a crude heuristic to fix this.
-         */
-        if (r < 1000.0) {
-                r *= 1000.0;
-        }
-        return r;
- }
-
-
- uint32_t
- EngineControl::get_buffer_size () const
- {
-        string txt = buffer_size_combo.get_active_text ();
-        uint32_t samples;
-
-        if (sscanf (txt.c_str(), "%d", &samples) != 1) {
-                throw exception ();
-        }
-
-        return samples;
- }
-
- string
- EngineControl::get_midi_option () const
- {
-        return midi_option_combo.get_active_text();
- }
-
- uint32_t
- EngineControl::get_input_channels() const
- {
-        return (uint32_t) input_channels_adjustment.get_value();
- }
-
- uint32_t
- EngineControl::get_output_channels() const
- {
-        return (uint32_t) output_channels_adjustment.get_value();
- }
-
- uint32_t
- EngineControl::get_input_latency() const
- {
-        return (uint32_t) input_latency_adjustment.get_value();
- }
-
- uint32_t
- EngineControl::get_output_latency() const
- {
-        return (uint32_t) output_latency_adjustment.get_value();
- }
-
- string
- EngineControl::get_backend () const
- {
-        return backend_combo.get_active_text ();
- }
-
- string
- EngineControl::get_driver () const
- {
-        return driver_combo.get_active_text ();
- }
-
- string
- EngineControl::get_device_name () const
- {
-        return device_combo.get_active_text ();
- }
-
- void
- EngineControl::control_app_button_clicked ()
- {
-        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
-
-        if (!backend) {
-                return;
-        }
-
-        backend->launch_control_app ();
- }
-
- void
- EngineControl::manage_control_app_sensitivity ()
- {
-        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
-
-        if (!backend) {
-                return;
-        }
-
-        string appname = backend->control_app_name();
-
-        if (appname.empty()) {
-                control_app_button.set_sensitive (false);
-        } else {
-                control_app_button.set_sensitive (true);
-        }
- }
-
- void
- EngineControl::set_desired_sample_rate (uint32_t sr)
- {
-        _desired_sample_rate = sr;
-        device_changed ();
- }
-
- void
- EngineControl::on_switch_page (GtkNotebookPage*, guint page_num)
- {
-        if (page_num == 0) {
-                cancel_button->set_sensitive (true);
-                ok_button->set_sensitive (true);
-                apply_button->set_sensitive (true);
-        } else {
-                cancel_button->set_sensitive (false);
-                ok_button->set_sensitive (false);
-                apply_button->set_sensitive (false);
-        }
-
-        if (page_num == midi_tab) {
-                /* MIDI tab */
-                refresh_midi_display ();
-        }
-
-        if (page_num == latency_tab) {
-                /* latency tab */
-
-                if (!ARDOUR::AudioEngine::instance()->running()) {
-
-                        PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
-
-                        /* save any existing latency values */
-
-                        uint32_t il = (uint32_t) input_latency.get_value ();
-                        uint32_t ol = (uint32_t) input_latency.get_value ();
-
-                        /* reset to zero so that our new test instance
-                           will be clean of any existing latency measures.
-                        */
+       basic_vbox.pack_start (basic_hbox, false, false);
 
-                        input_latency.set_value (0);
-                        output_latency.set_value (0);
+       if (_have_control) {
+               Gtk::HBox* hpacker = manage (new HBox);
+               hpacker->set_border_width (12);
+               hpacker->pack_start (control_app_button, false, false);
+               hpacker->show ();
+               control_app_button.show();
+               basic_vbox.pack_start (*hpacker);
+       }
 
-                        /* reset control */
+       basic_vbox.show_all ();
+}
 
-                        input_latency.set_value (il);
-                        output_latency.set_value (ol);
+void
+EngineControl::build_full_control_notebook ()
+{
+       boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+       assert (backend);
 
-                } 
+       using namespace Notebook_Helpers;
+       Label* label;
+       vector<string> strings;
+       AttachOptions xopt = AttachOptions (FILL|EXPAND);
+       int row = 1; // row zero == backend combo
 
-                if (ARDOUR::AudioEngine::instance()->prepare_for_latency_measurement()) {
-                        disable_latency_tab ();
-                }
+       /* start packing it up */
 
-                enable_latency_tab ();
+       if (backend->requires_driver_selection()) {
+               label = manage (left_aligned_label (_("Driver:")));
+               basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
+               basic_packer.attach (driver_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
+               row++;
+       }
 
-        } else {
-                if (lm_running) {
-                        ARDOUR::AudioEngine::instance()->stop_latency_detection();
-                }
-        }
- }
+       label = manage (left_aligned_label (_("Device:")));
+       basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
+       basic_packer.attach (device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
+       row++;
 
- /* latency measurement */
+       label = manage (left_aligned_label (_("Sample rate:")));
+       basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
+       basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
+       row++;
 
- bool
- EngineControl::check_latency_measurement ()
- {
-        MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
 
-        if (mtdm->resolve () < 0) {
-                lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
-                return true;
-        }
+       label = manage (left_aligned_label (_("Buffer size:")));
+       basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
+       basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
+       buffer_size_duration_label.set_alignment (0.0); /* left-align */
+       basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
+       row++;
 
-        if (mtdm->err () > 0.3) {
-                mtdm->invert ();
-                mtdm->resolve ();
-        }
+       input_channels.set_name ("InputChannels");
+       input_channels.set_flags (Gtk::CAN_FOCUS);
+       input_channels.set_digits (0);
+       input_channels.set_wrap (false);
+       output_channels.set_editable (true);
 
-        char buf[128];
-        ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
+       label = manage (left_aligned_label (_("Input Channels:")));
+       basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
+       basic_packer.attach (input_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
+       ++row;
 
-        if (sample_rate == 0) {
-                lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
-                ARDOUR::AudioEngine::instance()->stop_latency_detection ();
-                return false;
-        }
+       output_channels.set_name ("OutputChannels");
+       output_channels.set_flags (Gtk::CAN_FOCUS);
+       output_channels.set_digits (0);
+       output_channels.set_wrap (false);
+       output_channels.set_editable (true);
 
-        uint32_t frames_total = mtdm->del();
-        uint32_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
+       label = manage (left_aligned_label (_("Output Channels:")));
+       basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
+       basic_packer.attach (output_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
+       ++row;
 
-        snprintf (buf, sizeof (buf), "%u samples / %.3lf ms", extra, extra * 1000.0f/sample_rate);
+       input_latency.set_name ("InputLatency");
+       input_latency.set_flags (Gtk::CAN_FOCUS);
+       input_latency.set_digits (0);
+       input_latency.set_wrap (false);
+       input_latency.set_editable (true);
+
+       label = manage (left_aligned_label (_("Hardware input latency:")));
+       basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
+       basic_packer.attach (input_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
+       label = manage (left_aligned_label (_("samples")));
+       basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
+       ++row;
 
-        bool solid = true;
+       output_latency.set_name ("OutputLatency");
+       output_latency.set_flags (Gtk::CAN_FOCUS);
+       output_latency.set_digits (0);
+       output_latency.set_wrap (false);
+       output_latency.set_editable (true);
 
-        if (mtdm->err () > 0.2) {
-                strcat (buf, " ");
-                strcat (buf, _("(signal detection error)"));
-                solid = false;
-        }
+       label = manage (left_aligned_label (_("Hardware output latency:")));
+       basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
+       basic_packer.attach (output_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
+       label = manage (left_aligned_label (_("samples")));
+       basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
 
-        if (mtdm->inv ()) {
-                strcat (buf, " ");
-                strcat (buf, _("(inverted - bad wiring)"));
-                solid = false;
-        }
+       /* button spans 2 rows */
 
-        if (solid) {
-                end_latency_detection ();
-                lm_use_button.set_sensitive (true);
-                have_lm_results = true;
-        }
-       
-        lm_results.set_markup (string_compose (results_markup, string_compose (_("Detected roundtrip latency: %1"), buf)));
+       basic_packer.attach (lm_button_audio, 3, 4, row-1, row+1, xopt, xopt);
+       ++row;
 
-        return true;
+       label = manage (left_aligned_label (_("MIDI System")));
+       basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
+       basic_packer.attach (midi_option_combo, 1, 2, row, row + 1, SHRINK, (AttachOptions) 0);
+       basic_packer.attach (midi_devices_button, 3, 4, row, row+1, xopt, xopt);
+       row++;
 }
 
 void
-EngineControl::start_latency_detection ()
+EngineControl::build_no_control_notebook ()
 {
-       ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
-       ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
+       boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+       assert (backend);
 
-       if (ARDOUR::AudioEngine::instance()->start_latency_detection () == 0) {
-               lm_results.set_markup (string_compose (results_markup, _("Detecting ...")));
-               latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_latency_measurement), 100);
-               lm_measure_label.set_text (_("Cancel"));
-               have_lm_results = false;
-               lm_use_button.set_sensitive (false);
-               lm_input_channel_combo.set_sensitive (false);
-               lm_output_channel_combo.set_sensitive (false);
-               lm_running = true;
+       using namespace Notebook_Helpers;
+       Label* label;
+       vector<string> strings;
+       AttachOptions xopt = AttachOptions (FILL|EXPAND);
+       int row = 1; // row zero == backend combo
+       const string msg = string_compose (_("The %1 audio backend was configured and started externally.\nThis limits your control over it."), backend->name());
+
+       label = manage (new Label);
+       label->set_markup (string_compose ("<span weight=\"bold\" foreground=\"red\">%1</span>", msg));
+       basic_packer.attach (*label, 0, 2, row, row + 1, xopt, (AttachOptions) 0);
+       row++;
+
+       if (backend->can_change_sample_rate_when_running()) {
+               label = manage (left_aligned_label (_("Sample rate:")));
+               basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
+               basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
+               row++;
        }
+
+       if (backend->can_change_buffer_size_when_running()) {
+               label = manage (left_aligned_label (_("Buffer size:")));
+               basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
+               basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
+               buffer_size_duration_label.set_alignment (0.0); /* left-align */
+               basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
+               row++;
+       }
+
+       connect_disconnect_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::connect_disconnect_click));
+
+       basic_packer.attach (connect_disconnect_button, 0, 2, row, row+1, FILL, AttachOptions (0));
+       row++;
+}
+
+EngineControl::~EngineControl ()
+{
+       ignore_changes = true;
 }
 
 void
-EngineControl::end_latency_detection ()
+EngineControl::disable_latency_tab ()
 {
-       latency_timeout.disconnect ();
-       ARDOUR::AudioEngine::instance()->stop_latency_detection ();
-       lm_measure_label.set_text (_("Measure"));
-       if (!have_lm_results) {
-               lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
+       vector<string> empty;
+       set_popdown_strings (lm_output_channel_combo, empty);
+       set_popdown_strings (lm_input_channel_combo, empty);
+       lm_measure_button.set_sensitive (false);
+       lm_use_button.set_sensitive (false);
+}
+
+void
+EngineControl::enable_latency_tab ()
+{
+       vector<string> outputs;
+       vector<string> inputs;
+
+       ARDOUR::DataType const type = _measure_midi ? ARDOUR::DataType::MIDI : ARDOUR::DataType::AUDIO;
+       ARDOUR::AudioEngine::instance()->get_physical_outputs (type, outputs);
+       ARDOUR::AudioEngine::instance()->get_physical_inputs (type, inputs);
+
+       if (!ARDOUR::AudioEngine::instance()->running()) {
+               MessageDialog msg (_("Failed to start or connect to audio-engine.\n\nLatency calibration requires a working audio interface."));
+               notebook.set_current_page (0);
+               msg.run ();
+               return;
+       }
+       else if (inputs.empty() || outputs.empty()) {
+               MessageDialog msg (_("Your selected audio configuration is playback- or capture-only.\n\nLatency calibration requires playback and capture"));
+               notebook.set_current_page (0);
+               msg.run ();
+               return;
+       }
+
+       lm_back_button_signal.disconnect();
+       if (_measure_midi) {
+               lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), midi_tab));
+               lm_preamble.set_markup (_(""));
        } else {
-               lm_use_button.set_sensitive (false);
+               lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
+               lm_preamble.set_markup (_("<span weight=\"bold\">Turn down the volume on your audio equipment to a very low level.</span>"));
        }
-       lm_input_channel_combo.set_sensitive (true);
+
+       set_popdown_strings (lm_output_channel_combo, outputs);
+       lm_output_channel_combo.set_active_text (outputs.front());
        lm_output_channel_combo.set_sensitive (true);
-       lm_running = false;
+
+       set_popdown_strings (lm_input_channel_combo, inputs);
+       lm_input_channel_combo.set_active_text (inputs.front());
+       lm_input_channel_combo.set_sensitive (true);
+
+       lm_measure_button.set_sensitive (true);
 }
 
 void
-EngineControl::latency_button_clicked ()
+EngineControl::setup_midi_tab_for_backend ()
 {
-       if (!lm_running) {
-               start_latency_detection ();
+       string backend = backend_combo.get_active_text ();
+
+       Gtkmm2ext::container_clear (midi_vbox);
+
+       midi_vbox.set_border_width (12);
+       midi_device_table.set_border_width (12);
+
+       if (backend == "JACK") {
+               setup_midi_tab_for_jack ();
+       }
+
+       midi_vbox.pack_start (midi_device_table, true, true);
+       midi_vbox.pack_start (midi_back_button, false, false);
+       midi_vbox.show_all ();
+}
+
+void
+EngineControl::setup_midi_tab_for_jack ()
+{
+}
+
+void
+EngineControl::midi_latency_adjustment_changed (Gtk::Adjustment *a, MidiDeviceSettings device, bool for_input) {
+       if (for_input) {
+               device->input_latency = a->get_value();
        } else {
-               end_latency_detection ();
-        }
+               device->output_latency = a->get_value();
+       }
 }
 
 void
-EngineControl::use_latency_button_clicked ()
+EngineControl::midi_device_enabled_toggled (ArdourButton *b, MidiDeviceSettings device) {
+       b->set_active (!b->get_active());
+       device->enabled = b->get_active();
+       refresh_midi_display(device->name);
+}
+
+void
+EngineControl::refresh_midi_display (std::string focus)
+{
+       boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+       assert (backend);
+
+       int row  = 0;
+       AttachOptions xopt = AttachOptions (FILL|EXPAND);
+       Gtk::Label* l;
+
+       Gtkmm2ext::container_clear (midi_device_table);
+
+       midi_device_table.set_spacings (6);
+
+       l = manage (new Label);
+       l->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("MIDI Devices")));
+       midi_device_table.attach (*l, 0, 4, row, row + 1, xopt, AttachOptions (0));
+       l->set_alignment (0.5, 0.5);
+       row++;
+       l->show ();
+
+       l = manage (new Label (_("Device"))); l->show (); l->set_alignment (0.5, 0.5);
+       midi_device_table.attach (*l, 0, 1, row, row + 2, xopt, AttachOptions (0));
+       l = manage (new Label (_("Hardware Latencies"))); l->show (); l->set_alignment (0.5, 0.5);
+       midi_device_table.attach (*l, 1, 3, row, row + 1, xopt, AttachOptions (0));
+       row++;
+       l = manage (new Label (_("Input"))); l->show (); l->set_alignment (0.5, 0.5);
+       midi_device_table.attach (*l, 1, 2, row, row + 1, xopt, AttachOptions (0));
+       l = manage (new Label (_("Output"))); l->show (); l->set_alignment (0.5, 0.5);
+       midi_device_table.attach (*l, 2, 3, row, row + 1, xopt, AttachOptions (0));
+       row++;
+
+       for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
+               ArdourButton *m;
+               Gtk::Button* b;
+               Gtk::Adjustment *a;
+               Gtk::SpinButton *s;
+               bool enabled = (*p)->enabled;
+
+               m = manage (new ArdourButton ((*p)->name, ArdourButton::led_default_elements));
+               m->set_name ("midi device");
+               m->set_can_focus (Gtk::CAN_FOCUS);
+               m->add_events (Gdk::BUTTON_RELEASE_MASK);
+               m->set_active (enabled);
+               m->signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_device_enabled_toggled), m, *p));
+               midi_device_table.attach (*m, 0, 1, row, row + 1, xopt, AttachOptions (0)); m->show ();
+               if ((*p)->name == focus) {
+                       m->grab_focus();
+               }
+
+               a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
+               s = manage (new Gtk::SpinButton (*a));
+               a->set_value ((*p)->input_latency);
+               s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, true));
+               s->set_sensitive (_can_set_midi_latencies && enabled);
+               midi_device_table.attach (*s, 1, 2, row, row + 1, xopt, AttachOptions (0)); s->show ();
+
+               a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
+               s = manage (new Gtk::SpinButton (*a));
+               a->set_value ((*p)->output_latency);
+               s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, false));
+               s->set_sensitive (_can_set_midi_latencies && enabled);
+               midi_device_table.attach (*s, 2, 3, row, row + 1, xopt, AttachOptions (0)); s->show ();
+
+               b = manage (new Button (_("Calibrate")));
+               b->signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::calibrate_midi_latency), *p));
+               b->set_sensitive (_can_set_midi_latencies && enabled);
+               midi_device_table.attach (*b, 3, 4, row, row + 1, xopt, AttachOptions (0)); b->show ();
+
+               row++;
+       }
+}
+
+void
+EngineControl::update_sensitivity ()
+{
+}
+
+void
+EngineControl::backend_changed ()
 {
-        MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
+       string backend_name = backend_combo.get_active_text();
+       boost::shared_ptr<ARDOUR::AudioBackend> backend;
 
-       if (!mtdm) {
+       if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
+               /* eh? setting the backend failed... how ? */
                return;
        }
 
-       uint32_t frames_total = mtdm->del();
-       uint32_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
-       uint32_t one_way = extra/2;
+       _have_control = ARDOUR::AudioEngine::instance()->setup_required ();
+
+       build_notebook ();
+       setup_midi_tab_for_backend ();
+       _midi_devices.clear();
+
+       if (backend->requires_driver_selection()) {
+               vector<string> drivers = backend->enumerate_drivers();
+               driver_combo.set_sensitive (true);
+
+               if (!drivers.empty()) {
+                       {
+                               PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
+                               set_popdown_strings (driver_combo, drivers);
+                               driver_combo.set_active_text (drivers.front());
+                       }
+
+                       driver_changed ();
+               }
+
+       } else {
+               driver_combo.set_sensitive (false);
+               /* this will change the device text which will cause a call to
+                * device changed which will set up parameters
+                */
+               list_devices ();
+       }
+
+       vector<string> midi_options = backend->enumerate_midi_options();
+
+       if (midi_options.size() == 1) {
+               /* only contains the "none" option */
+               midi_option_combo.set_sensitive (false);
+       } else {
+               if (_have_control) {
+                       set_popdown_strings (midi_option_combo, midi_options);
+                       midi_option_combo.set_active_text (midi_options.front());
+                       midi_option_combo.set_sensitive (true);
+               } else {
+                       midi_option_combo.set_sensitive (false);
+               }
+       }
 
-       input_latency_adjustment.set_value (one_way);
-       output_latency_adjustment.set_value (one_way);
+       midi_option_changed();
 
-       /* back to settings page */
+       started_at_least_once = false;
 
-       notebook.set_current_page (0);
+       if (!ignore_changes) {
+               maybe_display_saved_state ();
+       }
 }
 
 bool
-EngineControl::on_delete_event (GdkEventAny* ev)
+EngineControl::print_channel_count (Gtk::SpinButton* sb)
 {
-       if (notebook.get_current_page() == 2) {
-               /* currently on latency tab - be sure to clean up */
-               end_latency_detection ();
+       uint32_t cnt = (uint32_t) sb->get_value();
+       if (cnt == 0) {
+               sb->set_text (_("all available channels"));
+       } else {
+               char buf[32];
+               snprintf (buf, sizeof (buf), "%d", cnt);
+               sb->set_text (buf);
        }
-       return ArdourDialog::on_delete_event (ev);
+       return true;
 }
 
 void
-EngineControl::engine_running ()
+EngineControl::list_devices ()
 {
        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
        assert (backend);
 
-       buffer_size_combo.set_active_text (bufsize_as_string (backend->buffer_size()));
-       sample_rate_combo.set_active_text (rate_as_string (backend->sample_rate()));
+       /* now fill out devices, mark sample rates, buffer sizes insensitive */
 
-       buffer_size_combo.set_sensitive (true);
-       sample_rate_combo.set_sensitive (true);
+       vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
 
-       connect_disconnect_button.set_label (string_compose (_("Disconnect from %1"), backend->name()));
+       /* NOTE: Ardour currently does not display the "available" field of the
+        * returned devices.
+        *
+        * Doing so would require a different GUI widget than the combo
+        * box/popdown that we currently use, since it has no way to list
+        * items that are not selectable. Something more like a popup menu,
+        * which could have unselectable items, would be appropriate.
+        */
 
-       started_at_least_once = true;
+       vector<string> available_devices;
+
+       for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
+               available_devices.push_back (i->name);
+       }
+
+       if (!available_devices.empty()) {
+
+               update_sensitivity ();
+
+               {
+                       PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
+                       set_popdown_strings (device_combo, available_devices);
+                       device_combo.set_active_text (available_devices.front());
+               }
+
+               device_changed ();
+
+               ok_button->set_sensitive (true);
+               apply_button->set_sensitive (true);
+
+       } else {
+               device_combo.clear();
+               sample_rate_combo.set_sensitive (false);
+               buffer_size_combo.set_sensitive (false);
+               input_latency.set_sensitive (false);
+               output_latency.set_sensitive (false);
+               input_channels.set_sensitive (false);
+               output_channels.set_sensitive (false);
+               if (_have_control) {
+                       ok_button->set_sensitive (false);
+                       apply_button->set_sensitive (false);
+               } else {
+                       ok_button->set_sensitive (true);
+                       apply_button->set_sensitive (true);
+               }
+       }
 }
 
 void
-EngineControl::engine_stopped ()
+EngineControl::driver_changed ()
 {
        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
        assert (backend);
 
-       buffer_size_combo.set_sensitive (false);
-       connect_disconnect_button.set_label (string_compose (_("Connect to %1"), backend->name()));
+       backend->set_driver (driver_combo.get_active_text());
+       list_devices ();
 
-       sample_rate_combo.set_sensitive (true);
-       buffer_size_combo.set_sensitive (true);
+       if (!ignore_changes) {
+               maybe_display_saved_state ();
+       }
 }
-       
+
 void
-EngineControl::connect_disconnect_click() 
+EngineControl::device_changed ()
 {
-       if (ARDOUR::AudioEngine::instance()->running()) {
-               ARDOUR_UI::instance()->disconnect_from_engine ();
+
+       boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+       assert (backend);
+       string device_name = device_combo.get_active_text ();
+       vector<string> s;
+
+       {
+               PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
+
+               /* don't allow programmatic change to combos to cause a
+                  recursive call to this method.
+                */
+
+               /* sample rates */
+
+               string desired;
+
+               vector<float> sr;
+
+               if (_have_control) {
+                       sr = backend->available_sample_rates (device_name);
+               } else {
+
+                       sr.push_back (8000.0f);
+                       sr.push_back (16000.0f);
+                       sr.push_back (32000.0f);
+                       sr.push_back (44100.0f);
+                       sr.push_back (48000.0f);
+                       sr.push_back (88200.0f);
+                       sr.push_back (96000.0f);
+                       sr.push_back (192000.0f);
+                       sr.push_back (384000.0f);
+               }
+
+               for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
+                       s.push_back (rate_as_string (*x));
+                       if (*x == _desired_sample_rate) {
+                               desired = s.back();
+                       }
+               }
+
+               if (!s.empty()) {
+                       sample_rate_combo.set_sensitive (true);
+                       set_popdown_strings (sample_rate_combo, s);
+
+                       if (desired.empty()) {
+                               sample_rate_combo.set_active_text (rate_as_string (backend->default_sample_rate()));
+                       } else {
+                               sample_rate_combo.set_active_text (desired);
+                       }
+
+               } else {
+                       sample_rate_combo.set_sensitive (false);
+               }
+
+               /* buffer sizes */
+
+               vector<uint32_t> bs;
+
+               if (_have_control) {
+                       bs = backend->available_buffer_sizes (device_name);
+               } else if (backend->can_change_buffer_size_when_running()) {
+                       bs.push_back (8);
+                       bs.push_back (16);
+                       bs.push_back (32);
+                       bs.push_back (64);
+                       bs.push_back (128);
+                       bs.push_back (256);
+                       bs.push_back (512);
+                       bs.push_back (1024);
+                       bs.push_back (2048);
+                       bs.push_back (4096);
+                       bs.push_back (8192);
+               }
+               s.clear ();
+               for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
+                       s.push_back (bufsize_as_string (*x));
+               }
+
+               if (!s.empty()) {
+                       buffer_size_combo.set_sensitive (true);
+                       set_popdown_strings (buffer_size_combo, s);
+
+                       buffer_size_combo.set_active_text (bufsize_as_string (backend->default_buffer_size()));
+                       show_buffer_duration ();
+               } else {
+                       buffer_size_combo.set_sensitive (false);
+               }
+
+               /* XXX theoretically need to set min + max channel counts here
+               */
+
+               manage_control_app_sensitivity ();
+       }
+
+       /* pick up any saved state for this device */
+
+       if (!ignore_changes) {
+               maybe_display_saved_state ();
+       }
+}
+
+string
+EngineControl::bufsize_as_string (uint32_t sz)
+{
+       /* Translators: "samples" is always plural here, so no
+          need for plural+singular forms.
+        */
+       char buf[32];
+       snprintf (buf, sizeof (buf), _("%u samples"), sz);
+       return buf;
+}
+
+void
+EngineControl::sample_rate_changed ()
+{
+       /* reset the strings for buffer size to show the correct msec value
+          (reflecting the new sample rate).
+        */
+
+       show_buffer_duration ();
+       if (!ignore_changes) {
+               save_state ();
+       }
+
+}
+
+void
+EngineControl::buffer_size_changed ()
+{
+       show_buffer_duration ();
+       if (!ignore_changes) {
+               save_state ();
+       }
+}
+
+void
+EngineControl::show_buffer_duration ()
+{
+
+       /* buffer sizes  - convert from just samples to samples + msecs for
+        * the displayed string
+        */
+
+       string bs_text = buffer_size_combo.get_active_text ();
+       uint32_t samples = atoi (bs_text); /* will ignore trailing text */
+       uint32_t rate = get_rate();
+
+       /* Translators: "msecs" is ALWAYS plural here, so we do not
+          need singular form as well.
+        */
+       /* Developers: note the hard-coding of a double buffered model
+          in the (2 * samples) computation of latency. we always start
+          the audiobackend in this configuration.
+        */
+       char buf[32];
+       snprintf (buf, sizeof (buf), _("(%.1f msecs)"), (2 * samples) / (rate/1000.0));
+       buffer_size_duration_label.set_text (buf);
+}
+
+void
+EngineControl::midi_option_changed ()
+{
+       boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+       assert (backend);
+
+       backend->set_midi_option (get_midi_option());
+
+       vector<ARDOUR::AudioBackend::DeviceStatus> midi_devices = backend->enumerate_midi_devices();
+
+       //_midi_devices.clear(); // TODO merge with state-saved settings..
+       _can_set_midi_latencies = backend->can_set_systemic_midi_latencies();
+       std::vector<MidiDeviceSettings> new_devices;
+
+       for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = midi_devices.begin(); i != midi_devices.end(); ++i) {
+               MidiDeviceSettings mds = find_midi_device (i->name);
+               if (i->available && !mds) {
+                       uint32_t input_latency = 0;
+                       uint32_t output_latency = 0;
+                       if (_can_set_midi_latencies) {
+                               input_latency = backend->systemic_midi_input_latency (i->name);
+                               output_latency = backend->systemic_midi_output_latency (i->name);
+                       }
+                       bool enabled = backend->midi_device_enabled (i->name);
+                       MidiDeviceSettings ptr (new MidiDeviceSetting (i->name, enabled, input_latency, output_latency));
+                       new_devices.push_back (ptr);
+               } else if (i->available) {
+                       new_devices.push_back (mds);
+               }
+       }
+       _midi_devices = new_devices;
+
+       if (_midi_devices.empty()) {
+               midi_devices_button.set_sensitive (false);
        } else {
-               ARDOUR_UI::instance()->reconnect_to_engine ();
+               midi_devices_button.set_sensitive (true);
+       }
+
+       if (!ignore_changes) {
+               save_state ();
        }
 }
 
 void
-EngineControl::calibrate_latency ()
+EngineControl::parameter_changed ()
 {
-       notebook.set_current_page (latency_tab);
+       if (!ignore_changes) {
+               save_state ();
+       }
+}
+
+EngineControl::State
+EngineControl::get_matching_state (
+               const string& backend,
+               const string& driver,
+               const string& device)
+{
+       for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
+               if ((*i)->backend == backend &&
+                               (!_have_control || ((*i)->driver == driver && (*i)->device == device)))
+               {
+                       return (*i);
+               }
+       }
+       return State();
 }
 
+EngineControl::State
+EngineControl::get_saved_state_for_currently_displayed_backend_and_device ()
+{
+       boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+
+       if (backend) {
+               return get_matching_state (backend_combo.get_active_text(),
+                               (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
+                               device_combo.get_active_text());
+       }
+
+
+       return get_matching_state (backend_combo.get_active_text(),
+                       string(),
+                       device_combo.get_active_text());
+}
+
+EngineControl::State
+EngineControl::save_state ()
+{
+       State state;
+
+       if (!_have_control) {
+               state = get_matching_state (backend_combo.get_active_text(), string(), string());
+               if (state) {
+                       return state;
+               }
+               state.reset(new StateStruct);
+               state->backend = get_backend ();
+       } else {
+               state.reset(new StateStruct);
+               store_state (state);
+       }
+
+       for (StateList::iterator i = states.begin(); i != states.end();) {
+               if ((*i)->backend == state->backend &&
+                               (*i)->driver == state->driver &&
+                               (*i)->device == state->device) {
+                       i =  states.erase(i);
+               } else {
+                       ++i;
+               }
+       }
+
+       states.push_back (state);
+
+       return state;
+}
+
+void
+EngineControl::store_state (State state)
+{
+       state->backend = get_backend ();
+       state->driver = get_driver ();
+       state->device = get_device_name ();
+       state->sample_rate = get_rate ();
+       state->buffer_size = get_buffer_size ();
+       state->input_latency = get_input_latency ();
+       state->output_latency = get_output_latency ();
+       state->input_channels = get_input_channels ();
+       state->output_channels = get_output_channels ();
+       state->midi_option = get_midi_option ();
+       state->midi_devices = _midi_devices;
+}
+
+void
+EngineControl::maybe_display_saved_state ()
+{
+       if (!_have_control) {
+               return;
+       }
+
+       State state = get_saved_state_for_currently_displayed_backend_and_device ();
+
+       if (state) {
+               PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
+
+               if (!_desired_sample_rate) {
+                       sample_rate_combo.set_active_text (rate_as_string (state->sample_rate));
+               }
+               buffer_size_combo.set_active_text (bufsize_as_string (state->buffer_size));
+               /* call this explicitly because we're ignoring changes to
+                  the controls at this point.
+                */
+               show_buffer_duration ();
+               input_latency.set_value (state->input_latency);
+               output_latency.set_value (state->output_latency);
+
+               if (!state->midi_option.empty()) {
+                       midi_option_combo.set_active_text (state->midi_option);
+                       _midi_devices = state->midi_devices;
+               }
+       }
+}
+
+XMLNode&
+EngineControl::get_state ()
+{
+       XMLNode* root = new XMLNode ("AudioMIDISetup");
+       std::string path;
+
+       if (!states.empty()) {
+               XMLNode* state_nodes = new XMLNode ("EngineStates");
+
+               for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
+
+                       XMLNode* node = new XMLNode ("State");
+
+                       node->add_property ("backend", (*i)->backend);
+                       node->add_property ("driver", (*i)->driver);
+                       node->add_property ("device", (*i)->device);
+                       node->add_property ("sample-rate", (*i)->sample_rate);
+                       node->add_property ("buffer-size", (*i)->buffer_size);
+                       node->add_property ("input-latency", (*i)->input_latency);
+                       node->add_property ("output-latency", (*i)->output_latency);
+                       node->add_property ("input-channels", (*i)->input_channels);
+                       node->add_property ("output-channels", (*i)->output_channels);
+                       node->add_property ("active", (*i)->active ? "yes" : "no");
+                       node->add_property ("midi-option", (*i)->midi_option);
+
+                       XMLNode* midi_devices = new XMLNode ("MIDIDevices");
+                       for (std::vector<MidiDeviceSettings>::const_iterator p = (*i)->midi_devices.begin(); p != (*i)->midi_devices.end(); ++p) {
+                               XMLNode* midi_device_stuff = new XMLNode ("MIDIDevice");
+                               midi_device_stuff->add_property (X_("name"), (*p)->name);
+                               midi_device_stuff->add_property (X_("enabled"), (*p)->enabled);
+                               midi_device_stuff->add_property (X_("input-latency"), (*p)->input_latency);
+                               midi_device_stuff->add_property (X_("output-latency"), (*p)->output_latency);
+                               midi_devices->add_child_nocopy (*midi_device_stuff);
+                       }
+                       node->add_child_nocopy (*midi_devices);
+
+                       state_nodes->add_child_nocopy (*node);
+               }
+
+               root->add_child_nocopy (*state_nodes);
+       }
+
+       return *root;
+}
+
+void
+EngineControl::set_state (const XMLNode& root)
+{
+       XMLNodeList          clist, cclist;
+       XMLNodeConstIterator citer, cciter;
+       XMLNode* child;
+       XMLNode* grandchild;
+       XMLProperty* prop = NULL;
+
+       if (root.name() != "AudioMIDISetup") {
+               return;
+       }
+
+       clist = root.children();
+
+       states.clear ();
+
+       for (citer = clist.begin(); citer != clist.end(); ++citer) {
+
+               child = *citer;
+
+               if (child->name() != "EngineStates") {
+                       continue;
+               }
+
+               cclist = child->children();
+
+               for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
+                       State state (new StateStruct);
+
+                       grandchild = *cciter;
+
+                       if (grandchild->name() != "State") {
+                               continue;
+                       }
+
+                       if ((prop = grandchild->property ("backend")) == 0) {
+                               continue;
+                       }
+                       state->backend = prop->value ();
+
+                       if ((prop = grandchild->property ("driver")) == 0) {
+                               continue;
+                       }
+                       state->driver = prop->value ();
+
+                       if ((prop = grandchild->property ("device")) == 0) {
+                               continue;
+                       }
+                       state->device = prop->value ();
+
+                       if ((prop = grandchild->property ("sample-rate")) == 0) {
+                               continue;
+                       }
+                       state->sample_rate = atof (prop->value ());
+
+                       if ((prop = grandchild->property ("buffer-size")) == 0) {
+                               continue;
+                       }
+                       state->buffer_size = atoi (prop->value ());
+
+                       if ((prop = grandchild->property ("input-latency")) == 0) {
+                               continue;
+                       }
+                       state->input_latency = atoi (prop->value ());
+
+                       if ((prop = grandchild->property ("output-latency")) == 0) {
+                               continue;
+                       }
+                       state->output_latency = atoi (prop->value ());
+
+                       if ((prop = grandchild->property ("input-channels")) == 0) {
+                               continue;
+                       }
+                       state->input_channels = atoi (prop->value ());
+
+                       if ((prop = grandchild->property ("output-channels")) == 0) {
+                               continue;
+                       }
+                       state->output_channels = atoi (prop->value ());
+
+                       if ((prop = grandchild->property ("active")) == 0) {
+                               continue;
+                       }
+                       state->active = string_is_affirmative (prop->value ());
+
+                       if ((prop = grandchild->property ("midi-option")) == 0) {
+                               continue;
+                       }
+                       state->midi_option = prop->value ();
+
+                       state->midi_devices.clear();
+                       XMLNode* midinode;
+                       if ((midinode = ARDOUR::find_named_node (*grandchild, "MIDIDevices")) != 0) {
+                               const XMLNodeList mnc = midinode->children();
+                               for (XMLNodeList::const_iterator n = mnc.begin(); n != mnc.end(); ++n) {
+                                       if ((*n)->property (X_("name")) == 0
+                                                       || (*n)->property (X_("enabled")) == 0
+                                                       || (*n)->property (X_("input-latency")) == 0
+                                                       || (*n)->property (X_("output-latency")) == 0
+                                                ) {
+                                               continue;
+                                       }
+
+                                       MidiDeviceSettings ptr (new MidiDeviceSetting(
+                                                               (*n)->property (X_("name"))->value (),
+                                                               string_is_affirmative ((*n)->property (X_("enabled"))->value ()),
+                                                               atoi ((*n)->property (X_("input-latency"))->value ()),
+                                                               atoi ((*n)->property (X_("output-latency"))->value ())
+                                                               ));
+                                       state->midi_devices.push_back (ptr);
+                               }
+                       }
+
+#if 1
+                       /* remove accumulated duplicates (due to bug in ealier version)
+                        * this can be removed again before release
+                        */
+                       for (StateList::iterator i = states.begin(); i != states.end();) {
+                               if ((*i)->backend == state->backend &&
+                                               (*i)->driver == state->driver &&
+                                               (*i)->device == state->device) {
+                                       i =  states.erase(i);
+                               } else {
+                                       ++i;
+                               }
+                       }
+#endif
+
+                       states.push_back (state);
+               }
+       }
+
+       /* now see if there was an active state and switch the setup to it */
+
+       for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
+
+               if ((*i)->active) {
+                       ignore_changes++;
+                       backend_combo.set_active_text ((*i)->backend);
+                       driver_combo.set_active_text ((*i)->driver);
+                       device_combo.set_active_text ((*i)->device);
+                       sample_rate_combo.set_active_text (rate_as_string ((*i)->sample_rate));
+                       buffer_size_combo.set_active_text (bufsize_as_string ((*i)->buffer_size));
+                       input_latency.set_value ((*i)->input_latency);
+                       output_latency.set_value ((*i)->output_latency);
+                       midi_option_combo.set_active_text ((*i)->midi_option);
+                       ignore_changes--;
+                       break;
+               }
+       }
+}
+
+int
+EngineControl::push_state_to_backend (bool start)
+{
+       boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+
+       if (!backend) {
+               return 0;
+       }
+
+       /* figure out what is going to change */
+
+       bool restart_required = false;
+       bool was_running = ARDOUR::AudioEngine::instance()->running();
+       bool change_driver = false;
+       bool change_device = false;
+       bool change_rate = false;
+       bool change_bufsize = false;
+       bool change_latency = false;
+       bool change_channels = false;
+       bool change_midi = false;
+
+       uint32_t ochan = get_output_channels ();
+       uint32_t ichan = get_input_channels ();
+
+       if (_have_control) {
+
+               if (started_at_least_once) {
+
+                       /* we can control the backend */
+
+                       if (backend->requires_driver_selection()) {
+                               if (get_driver() != backend->driver_name()) {
+                                       change_driver = true;
+                               }
+                       }
+
+                       if (get_device_name() != backend->device_name()) {
+                               change_device = true;
+                       }
+
+                       if (get_rate() != backend->sample_rate()) {
+                               change_rate = true;
+                       }
+
+                       if (get_buffer_size() != backend->buffer_size()) {
+                               change_bufsize = true;
+                       }
+
+                       if (get_midi_option() != backend->midi_option()) {
+                               change_midi = true;
+                       }
+
+                       /* zero-requested channels means "all available" */
+
+                       if (ichan == 0) {
+                               ichan = backend->input_channels();
+                       }
+
+                       if (ochan == 0) {
+                               ochan = backend->output_channels();
+                       }
+
+                       if (ichan != backend->input_channels()) {
+                               change_channels = true;
+                       }
+
+                       if (ochan != backend->output_channels()) {
+                               change_channels = true;
+                       }
+
+                       if (get_input_latency() != backend->systemic_input_latency() ||
+                                       get_output_latency() != backend->systemic_output_latency()) {
+                               change_latency = true;
+                       }
+               } else {
+                       /* backend never started, so we have to force a group
+                          of settings.
+                        */
+                       change_device = true;
+                       if (backend->requires_driver_selection()) {
+                               change_driver = true;
+                       }
+                       change_rate = true;
+                       change_bufsize = true;
+                       change_channels = true;
+                       change_latency = true;
+                       change_midi = true;
+               }
+
+       } else {
+
+               /* we have no control over the backend, meaning that we can
+                * only possibly change sample rate and buffer size.
+                */
+
+
+               if (get_rate() != backend->sample_rate()) {
+                       change_bufsize = true;
+               }
+
+               if (get_buffer_size() != backend->buffer_size()) {
+                       change_bufsize = true;
+               }
+       }
+
+       if (!_have_control) {
+
+               /* We do not have control over the backend, so the best we can
+                * do is try to change the sample rate and/or bufsize and get
+                * out of here.
+                */
+
+               if (change_rate && !backend->can_change_sample_rate_when_running()) {
+                       return 1;
+               }
+
+               if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
+                       return 1;
+               }
+
+               if (change_rate) {
+                       backend->set_sample_rate (get_rate());
+               }
+
+               if (change_bufsize) {
+                       backend->set_buffer_size (get_buffer_size());
+               }
+
+               if (start) {
+                       if (ARDOUR::AudioEngine::instance()->start ()) {
+                               error << string_compose (_("Could not start backend engine %1"), backend->name()) << endmsg;
+                               return -1;
+                       }
+               }
+
+               post_push ();
+
+               return 0;
+       }
+
+       /* determine if we need to stop the backend before changing parameters */
+
+       if (change_driver || change_device || change_channels || change_latency ||
+                       (change_rate && !backend->can_change_sample_rate_when_running()) ||
+                       change_midi ||
+                       (change_bufsize && !backend->can_change_buffer_size_when_running())) {
+               restart_required = true;
+       } else {
+               restart_required = false;
+       }
+
+       if (was_running) {
+
+               if (!change_driver && !change_device && !change_channels && !change_latency && !change_midi) {
+                       /* no changes in any parameters that absolutely require a
+                        * restart, so check those that might be changeable without a
+                        * restart
+                        */
+
+                       if (change_rate && !backend->can_change_sample_rate_when_running()) {
+                               /* can't do this while running ... */
+                               restart_required = true;
+                       }
+
+                       if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
+                               /* can't do this while running ... */
+                               restart_required = true;
+                       }
+               }
+       }
+
+       if (was_running) {
+               if (restart_required) {
+                       if (ARDOUR_UI::instance()->disconnect_from_engine ()) {
+                               return -1;
+                       }
+               }
+       }
+
+
+       if (change_driver && backend->set_driver (get_driver())) {
+               error << string_compose (_("Cannot set driver to %1"), get_driver()) << endmsg;
+               return -1;
+       }
+       if (change_device && backend->set_device_name (get_device_name())) {
+               error << string_compose (_("Cannot set device name to %1"), get_device_name()) << endmsg;
+               return -1;
+       }
+       if (change_rate && backend->set_sample_rate (get_rate())) {
+               error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
+               return -1;
+       }
+       if (change_bufsize && backend->set_buffer_size (get_buffer_size())) {
+               error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
+               return -1;
+       }
+
+       if (change_channels || get_input_channels() == 0 || get_output_channels() == 0) {
+               if (backend->set_input_channels (get_input_channels())) {
+                       error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
+                       return -1;
+               }
+               if (backend->set_output_channels (get_output_channels())) {
+                       error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
+                       return -1;
+               }
+       }
+       if (change_latency) {
+               if (backend->set_systemic_input_latency (get_input_latency())) {
+                       error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
+                       return -1;
+               }
+               if (backend->set_systemic_output_latency (get_output_latency())) {
+                       error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
+                       return -1;
+               }
+       }
+
+       if (change_midi) {
+               backend->set_midi_option (get_midi_option());
+       }
+
+       if (1 /* TODO */) {
+               for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
+                       if (_measure_midi) {
+                               if (*p == _measure_midi) {
+                                       backend->set_midi_device_enabled ((*p)->name, true);
+                               } else {
+                                       backend->set_midi_device_enabled ((*p)->name, false);
+                               }
+                               continue;
+                       }
+                       backend->set_midi_device_enabled ((*p)->name, (*p)->enabled);
+                       if (backend->can_set_systemic_midi_latencies()) {
+                               backend->set_systemic_midi_input_latency ((*p)->name, (*p)->input_latency);
+                               backend->set_systemic_midi_output_latency ((*p)->name, (*p)->output_latency);
+                       }
+               }
+       }
+
+       if (start || (was_running && restart_required)) {
+               if (ARDOUR_UI::instance()->reconnect_to_engine()) {
+                       return -1;
+               }
+       }
+
+       post_push ();
+
+       return 0;
+}
+
+void
+EngineControl::post_push ()
+{
+       /* get a pointer to the current state object, creating one if
+        * necessary
+        */
+
+       State state = get_saved_state_for_currently_displayed_backend_and_device ();
+
+       if (!state) {
+               state = save_state ();
+               assert (state);
+       }
+
+       /* all off */
+
+       for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
+               (*i)->active = false;
+       }
+
+       /* mark this one active (to be used next time the dialog is
+        * shown)
+        */
+
+       state->active = true;
+
+       if (_have_control) { // XXX
+               manage_control_app_sensitivity ();
+       }
+
+       /* schedule a redisplay of MIDI ports */
+       //Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &EngineControl::refresh_midi_display), false), 1000);
+}
+
+
+float
+EngineControl::get_rate () const
+{
+       float r = atof (sample_rate_combo.get_active_text ());
+       /* the string may have been translated with an abbreviation for
+        * thousands, so use a crude heuristic to fix this.
+        */
+       if (r < 1000.0) {
+               r *= 1000.0;
+       }
+       return r;
+}
+
+
+uint32_t
+EngineControl::get_buffer_size () const
+{
+       string txt = buffer_size_combo.get_active_text ();
+       uint32_t samples;
+
+       if (sscanf (txt.c_str(), "%d", &samples) != 1) {
+               throw exception ();
+       }
+
+       return samples;
+}
+
+string
+EngineControl::get_midi_option () const
+{
+       return midi_option_combo.get_active_text();
+}
+
+uint32_t
+EngineControl::get_input_channels() const
+{
+       return (uint32_t) input_channels_adjustment.get_value();
+}
+
+uint32_t
+EngineControl::get_output_channels() const
+{
+       return (uint32_t) output_channels_adjustment.get_value();
+}
+
+uint32_t
+EngineControl::get_input_latency() const
+{
+       return (uint32_t) input_latency_adjustment.get_value();
+}
+
+uint32_t
+EngineControl::get_output_latency() const
+{
+       return (uint32_t) output_latency_adjustment.get_value();
+}
+
+string
+EngineControl::get_backend () const
+{
+       return backend_combo.get_active_text ();
+}
+
+string
+EngineControl::get_driver () const
+{
+       if (driver_combo.get_sensitive() && driver_combo.get_parent()) {
+               return driver_combo.get_active_text ();
+       } else {
+               return "";
+       }
+}
+
+string
+EngineControl::get_device_name () const
+{
+       return device_combo.get_active_text ();
+}
+
+void
+EngineControl::control_app_button_clicked ()
+{
+       boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+
+       if (!backend) {
+               return;
+       }
+
+       backend->launch_control_app ();
+}
+
+void
+EngineControl::manage_control_app_sensitivity ()
+{
+       boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+
+       if (!backend) {
+               return;
+       }
+
+       string appname = backend->control_app_name();
+
+       if (appname.empty()) {
+               control_app_button.set_sensitive (false);
+       } else {
+               control_app_button.set_sensitive (true);
+       }
+}
+
+void
+EngineControl::set_desired_sample_rate (uint32_t sr)
+{
+       _desired_sample_rate = sr;
+       device_changed ();
+}
+
+void
+EngineControl::on_switch_page (GtkNotebookPage*, guint page_num)
+{
+       if (page_num == 0) {
+               cancel_button->set_sensitive (true);
+               ok_button->set_sensitive (true);
+               apply_button->set_sensitive (true);
+               _measure_midi.reset();
+       } else {
+               cancel_button->set_sensitive (false);
+               ok_button->set_sensitive (false);
+               apply_button->set_sensitive (false);
+       }
+
+       if (page_num == midi_tab) {
+               /* MIDI tab */
+               refresh_midi_display ();
+       }
+
+       if (page_num == latency_tab) {
+               /* latency tab */
+
+               if (ARDOUR::AudioEngine::instance()->running()) {
+                       // TODO - mark as 'stopped for latency
+                       ARDOUR_UI::instance()->disconnect_from_engine ();
+               }
+
+               {
+                       PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
+
+                       /* save any existing latency values */
+
+                       uint32_t il = (uint32_t) input_latency.get_value ();
+                       uint32_t ol = (uint32_t) input_latency.get_value ();
+
+                       /* reset to zero so that our new test instance
+                          will be clean of any existing latency measures.
+
+                          NB. this should really be done by the backend
+                          when stated for latency measurement.
+                       */
+
+                       input_latency.set_value (0);
+                       output_latency.set_value (0);
+
+                       push_state_to_backend (false);
+
+                       /* reset control */
+
+                       input_latency.set_value (il);
+                       output_latency.set_value (ol);
+
+               }
+               // This should be done in push_state_to_backend()
+               if (ARDOUR::AudioEngine::instance()->prepare_for_latency_measurement()) {
+                       disable_latency_tab ();
+               }
+
+               enable_latency_tab ();
+
+       } else {
+               if (lm_running) {
+                       end_latency_detection ();
+                       ARDOUR::AudioEngine::instance()->stop_latency_detection();
+               }
+       }
+}
+
+/* latency measurement */
+
+bool
+EngineControl::check_audio_latency_measurement ()
+{
+       MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
+
+       if (mtdm->resolve () < 0) {
+               lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
+               return true;
+       }
+
+       if (mtdm->err () > 0.3) {
+               mtdm->invert ();
+               mtdm->resolve ();
+       }
+
+       char buf[256];
+       ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
+
+       if (sample_rate == 0) {
+               lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
+               ARDOUR::AudioEngine::instance()->stop_latency_detection ();
+               return false;
+       }
+
+       int frames_total = mtdm->del();
+       int extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
+
+       snprintf (buf, sizeof (buf), "%s%d samples (%.3lf ms)\n%s%d samples (%.3lf ms)",
+                       _("Detected roundtrip latency: "),
+                       frames_total, frames_total * 1000.0f/sample_rate,
+                       _("Systemic latency: "),
+                       extra, extra * 1000.0f/sample_rate);
+
+       bool solid = true;
+
+       if (mtdm->err () > 0.2) {
+               strcat (buf, " ");
+               strcat (buf, _("(signal detection error)"));
+               solid = false;
+       }
+
+       if (mtdm->inv ()) {
+               strcat (buf, " ");
+               strcat (buf, _("(inverted - bad wiring)"));
+               solid = false;
+       }
+
+       if (solid) {
+               have_lm_results = true;
+               end_latency_detection ();
+               lm_use_button.set_sensitive (true);
+               return false;
+       }
+
+       lm_results.set_markup (string_compose (results_markup, buf));
+
+       return true;
+}
+
+bool
+EngineControl::check_midi_latency_measurement ()
+{
+       ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
+
+       if (!mididm->have_signal () || mididm->latency () == 0) {
+               lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
+               return true;
+       }
+
+       char buf[256];
+       ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
+
+       if (sample_rate == 0) {
+               lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
+               ARDOUR::AudioEngine::instance()->stop_latency_detection ();
+               return false;
+       }
+
+       ARDOUR::framecnt_t frames_total = mididm->latency();
+       ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
+       snprintf (buf, sizeof (buf), "%s%" PRId64" samples (%.1lf ms) dev: %.2f[spl]\n%s%" PRId64" samples (%.1lf ms)",
+                       _("Detected roundtrip latency: "),
+                       frames_total, frames_total * 1000.0f / sample_rate, mididm->deviation (),
+                       _("Systemic latency: "),
+                       extra, extra * 1000.0f / sample_rate);
+
+       bool solid = true;
+
+       if (!mididm->ok ()) {
+               strcat (buf, " ");
+               strcat (buf, _("(averaging)"));
+               solid = false;
+       }
+
+       if (mididm->deviation () > 50.0) {
+               strcat (buf, " ");
+               strcat (buf, _("(too large jitter)"));
+               solid = false;
+       } else if (mididm->deviation () > 10.0) {
+               strcat (buf, " ");
+               strcat (buf, _("(large jitter)"));
+       }
+
+       if (solid) {
+               have_lm_results = true;
+               end_latency_detection ();
+               lm_use_button.set_sensitive (true);
+               return false;
+       } else if (mididm->processed () > 400) {
+               have_lm_results = false;
+               end_latency_detection ();
+               lm_results.set_markup (string_compose (results_markup, _("Timeout - large MIDI jitter.")));
+               return false;
+       }
+
+       lm_results.set_markup (string_compose (results_markup, buf));
+
+       return true;
+}
+
+void
+EngineControl::start_latency_detection ()
+{
+       ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
+       ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
+
+       if (ARDOUR::AudioEngine::instance()->start_latency_detection (_measure_midi ? true : false) == 0) {
+               lm_results.set_markup (string_compose (results_markup, _("Detecting ...")));
+               if (_measure_midi) {
+                       latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_midi_latency_measurement), 100);
+               } else {
+                       latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_audio_latency_measurement), 100);
+               }
+               lm_measure_label.set_text (_("Cancel"));
+               have_lm_results = false;
+               lm_use_button.set_sensitive (false);
+               lm_input_channel_combo.set_sensitive (false);
+               lm_output_channel_combo.set_sensitive (false);
+               lm_running = true;
+       }
+}
+
+void
+EngineControl::end_latency_detection ()
+{
+       latency_timeout.disconnect ();
+       ARDOUR::AudioEngine::instance()->stop_latency_detection ();
+       lm_measure_label.set_text (_("Measure"));
+       if (!have_lm_results) {
+               lm_use_button.set_sensitive (false);
+       }
+       lm_input_channel_combo.set_sensitive (true);
+       lm_output_channel_combo.set_sensitive (true);
+       lm_running = false;
+}
+
+void
+EngineControl::latency_button_clicked ()
+{
+       if (!lm_running) {
+               start_latency_detection ();
+       } else {
+               end_latency_detection ();
+       }
+}
+
+void
+EngineControl::use_latency_button_clicked ()
+{
+       if (_measure_midi) {
+               ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
+               if (!mididm) {
+                       return;
+               }
+               ARDOUR::framecnt_t frames_total = mididm->latency();
+               ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
+               uint32_t one_way = max ((ARDOUR::framecnt_t) 0, extra / 2);
+               _measure_midi->input_latency = one_way;
+               _measure_midi->output_latency = one_way;
+               notebook.set_current_page (midi_tab);
+       } else {
+               MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
+
+               if (!mtdm) {
+                       return;
+               }
+
+               double one_way = rint ((mtdm->del() - ARDOUR::AudioEngine::instance()->latency_signal_delay()) / 2.0);
+               one_way = std::max (0., one_way);
+
+               input_latency_adjustment.set_value (one_way);
+               output_latency_adjustment.set_value (one_way);
+
+               /* back to settings page */
+               notebook.set_current_page (0);
+}
+       }
+
+
+bool
+EngineControl::on_delete_event (GdkEventAny* ev)
+{
+       if (notebook.get_current_page() == 2) {
+               /* currently on latency tab - be sure to clean up */
+               end_latency_detection ();
+       }
+       return ArdourDialog::on_delete_event (ev);
+}
+
+void
+EngineControl::engine_running ()
+{
+       boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+       assert (backend);
+
+       buffer_size_combo.set_active_text (bufsize_as_string (backend->buffer_size()));
+       sample_rate_combo.set_active_text (rate_as_string (backend->sample_rate()));
+
+       buffer_size_combo.set_sensitive (true);
+       sample_rate_combo.set_sensitive (true);
+
+       connect_disconnect_button.set_label (string_compose (_("Disconnect from %1"), backend->name()));
+
+       started_at_least_once = true;
+}
+
+void
+EngineControl::engine_stopped ()
+{
+       boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+       assert (backend);
+
+       buffer_size_combo.set_sensitive (false);
+       connect_disconnect_button.set_label (string_compose (_("Connect to %1"), backend->name()));
+
+       sample_rate_combo.set_sensitive (true);
+       buffer_size_combo.set_sensitive (true);
+}
+
+void
+EngineControl::connect_disconnect_click()
+{
+       if (ARDOUR::AudioEngine::instance()->running()) {
+               ARDOUR_UI::instance()->disconnect_from_engine ();
+       } else {
+               ARDOUR_UI::instance()->reconnect_to_engine ();
+       }
+}
+
+void
+EngineControl::calibrate_audio_latency ()
+{
+       _measure_midi.reset ();
+       have_lm_results = false;
+       lm_use_button.set_sensitive (false);
+       lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
+       notebook.set_current_page (latency_tab);
+}
+
+void
+EngineControl::calibrate_midi_latency (MidiDeviceSettings s)
+{
+       _measure_midi = s;
+       have_lm_results = false;
+       lm_use_button.set_sensitive (false);
+       lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
+       notebook.set_current_page (latency_tab);
+}
+
+void
+EngineControl::configure_midi_devices ()
+{
+       notebook.set_current_page (midi_tab);
+}
index 6d10eb76ac7b7264d7b62009770ddb034e77338b..16de4c27664df3923e6901f3f8fcd9c00fdc423e 100644 (file)
@@ -42,14 +42,14 @@ class EngineControl : public ArdourDialog, public PBD::ScopedConnectionList {
   public:
     EngineControl ();
     ~EngineControl ();
-    
+
     static bool need_setup ();
-    
+
     XMLNode& get_state ();
     void set_state (const XMLNode&);
-    
+
     void set_desired_sample_rate (uint32_t);
-    
+
   private:
     Gtk::Notebook notebook;
 
@@ -79,6 +79,7 @@ class EngineControl : public ArdourDialog, public PBD::ScopedConnectionList {
 
     Gtk::Label      have_control_text;
     Gtk::Button     control_app_button;
+    ArdourButton    midi_devices_button;
 
     Gtk::Button     connect_disconnect_button;
 
@@ -90,8 +91,9 @@ class EngineControl : public ArdourDialog, public PBD::ScopedConnectionList {
     Gtk::Button       lm_measure_button;
     Gtk::Button       lm_use_button;
     Gtk::Button       lm_back_button;
-    ArdourButton      lm_button;
+    ArdourButton      lm_button_audio;
     Gtk::Label        lm_title;
+    Gtk::Label        lm_preamble;
     Gtk::Label        lm_results;
     Gtk::Table        lm_table;
     Gtk::VBox         lm_vbox;
@@ -105,13 +107,13 @@ class EngineControl : public ArdourDialog, public PBD::ScopedConnectionList {
     /* MIDI Tab */
 
     Gtk::VBox midi_vbox;
-    Gtk::Button midi_refresh_button;
+    Gtk::Button midi_back_button;
     Gtk::Table midi_device_table;
 
     /* MIDI ... JACK */
-    
+
     Gtk::CheckButton aj_button;
-    
+
     uint32_t ignore_changes;
     uint32_t _desired_sample_rate;
     bool     started_at_least_once;
@@ -125,8 +127,8 @@ class EngineControl : public ArdourDialog, public PBD::ScopedConnectionList {
 
     void setup_midi_tab_for_backend ();
     void setup_midi_tab_for_jack ();
-    void refresh_midi_display ();
-    
+    void refresh_midi_display (std::string focus = "");
+
     std::string bufsize_as_string (uint32_t);
 
     float get_rate() const;
@@ -144,7 +146,36 @@ class EngineControl : public ArdourDialog, public PBD::ScopedConnectionList {
     void list_devices ();
     void show_buffer_duration ();
 
-    struct State {
+    void configure_midi_devices ();
+
+    struct MidiDeviceSetting {
+       std::string name;
+       bool enabled;
+       uint32_t input_latency;
+       uint32_t output_latency;
+
+       MidiDeviceSetting (std::string n, bool en = true, uint32_t inl = 0, uint32_t oul = 0)
+           : name (n)
+           , enabled (en)
+           , input_latency (inl)
+           , output_latency (oul)
+       {}
+    };
+
+    typedef boost::shared_ptr<MidiDeviceSetting> MidiDeviceSettings;
+    bool _can_set_midi_latencies;
+    std::vector<MidiDeviceSettings> _midi_devices;
+
+    MidiDeviceSettings find_midi_device(std::string devicename) const {
+       for (std::vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
+           if ((*p)->name == devicename) {
+               return *p;
+           }
+       }
+       return MidiDeviceSettings();
+    }
+
+    struct StateStruct {
        std::string backend;
        std::string driver;
        std::string device;
@@ -156,27 +187,31 @@ class EngineControl : public ArdourDialog, public PBD::ScopedConnectionList {
        uint32_t output_channels;
        bool active;
        std::string midi_option;
+       std::vector<MidiDeviceSettings> midi_devices;
 
-       State() 
-               : input_latency (0)
+       StateStruct()
+               : sample_rate (48000)
+               , buffer_size (1024)
+               , input_latency (0)
                , output_latency (0)
                , input_channels (0)
                , output_channels (0)
                , active (false) {}
 
     };
-    
+
+    typedef boost::shared_ptr<StateStruct> State;
     typedef std::list<State> StateList;
 
     StateList states;
 
-    State* get_matching_state (const std::string& backend,
+    State get_matching_state (const std::string& backend,
                               const std::string& driver,
                               const std::string& device);
-    State* get_saved_state_for_currently_displayed_backend_and_device ();
+    State get_saved_state_for_currently_displayed_backend_and_device ();
     void maybe_display_saved_state ();
-    State* save_state ();
-    void store_state (State&);
+    State save_state ();
+    void store_state (State);
 
     bool  _have_control;
 
@@ -196,13 +231,14 @@ class EngineControl : public ArdourDialog, public PBD::ScopedConnectionList {
 
     /* latency measurement */
     void latency_button_clicked ();
-    bool check_latency_measurement ();
+    bool check_audio_latency_measurement ();
+    bool check_midi_latency_measurement ();
     sigc::connection latency_timeout;
     void enable_latency_tab ();
     void disable_latency_tab ();
     void start_latency_detection ();
     void end_latency_detection ();
-    
+
     void on_switch_page (GtkNotebookPage*, guint page_num);
     bool on_delete_event (GdkEventAny*);
 
@@ -212,7 +248,13 @@ class EngineControl : public ArdourDialog, public PBD::ScopedConnectionList {
     PBD::ScopedConnection stopped_connection;
 
     void connect_disconnect_click ();
-    void calibrate_latency ();
+    void calibrate_audio_latency ();
+    void calibrate_midi_latency (MidiDeviceSettings);
+
+    MidiDeviceSettings _measure_midi;
+    void midi_latency_adjustment_changed(Gtk::Adjustment *, MidiDeviceSettings, bool);
+    void midi_device_enabled_toggled(ArdourButton *, MidiDeviceSettings);
+    sigc::connection lm_back_button_signal;
 };
 
 #endif /* __gtk2_ardour_engine_dialog_h__ */
index 75e2cc9df57d2c9d0f04e0994f9bba01ccf7c1ef..d2f0f23284c69e05b7bb91abcc454d8795bc5ab6 100644 (file)
@@ -135,6 +135,7 @@ setup_gtk_ardour_enums ()
        REGISTER (zoom_focus);
 
        REGISTER_ENUM (RegionItem);
+       REGISTER_ENUM (WaveItem);
        REGISTER_ENUM (StreamItem);
        REGISTER_ENUM (PlayheadCursorItem);
        REGISTER_ENUM (MarkerItem);
@@ -167,5 +168,9 @@ setup_gtk_ardour_enums ()
        REGISTER_ENUM (StartCrossFadeItem);
        REGISTER_ENUM (EndCrossFadeItem);
        REGISTER_ENUM (CrossfadeViewItem);
+       REGISTER_ENUM (TimecodeRulerItem);
+       REGISTER_ENUM (MinsecRulerItem);
+       REGISTER_ENUM (BBTRulerItem);
+       REGISTER_ENUM (SamplesRulerItem);
        REGISTER (item_type);
 }
index 10e3135b53b7b8f7de3774e7f99cdecef1c9416f..20155471af11c5d87edab625d15c65066a238045 100644 (file)
@@ -458,15 +458,15 @@ RegionExportChannelSelector::RegionExportChannelSelector (ARDOUR::Session * _ses
 
        raw_button.set_label (string_compose (_("Region contents without fades nor region gain (channels: %1)"), region_chans));
        raw_button.signal_toggled ().connect (sigc::mem_fun (*this, &RegionExportChannelSelector::handle_selection));
-       vbox.pack_start (raw_button);
+       vbox.pack_start (raw_button, false, false);
 
        fades_button.set_label (string_compose (_("Region contents with fades and region gain (channels: %1)"), region_chans));
        fades_button.signal_toggled ().connect (sigc::mem_fun (*this, &RegionExportChannelSelector::handle_selection));
-       vbox.pack_start (fades_button);
+       vbox.pack_start (fades_button, false, false);
 
        processed_button.set_label (string_compose (_("Track output (channels: %1)"), track_chans));
        processed_button.signal_toggled ().connect (sigc::mem_fun (*this, &RegionExportChannelSelector::handle_selection));
-       vbox.pack_start (processed_button);
+       vbox.pack_start (processed_button, false, false);
 
        sync_with_manager();
        vbox.show_all_children ();
@@ -541,7 +541,7 @@ TrackExportChannelSelector::TrackExportChannelSelector (ARDOUR::Session * sessio
        // Options
        options_box.pack_start(region_contents_button);
        options_box.pack_start(track_output_button);
-       main_layout.pack_start(options_box);
+       main_layout.pack_start(options_box, false, false);
 
        // Track scroller
        track_scroller.add (track_view);
index bc165273c73f1b66e81d32a6303253bae6348e68..3dbb9b8265494b553c1db24675dde6cc26a0e706 100644 (file)
@@ -126,7 +126,7 @@ class PortExportChannelSelector : public ExportChannelSelector
                typedef Gtk::TreeModelColumn<Glib::RefPtr<Gtk::ListStore> > ComboCol;
                ComboCol                             port_list_col;
 
-               /* Channel struct, that represents the selected port and it's name */
+               /* Channel struct, that represents the selected port and its name */
 
                struct Channel {
                  public:
index 6351c512c19cc1d0dee757e3fd72e50a0cfa9396..4ee9ddf8edd0864d8cf449102f11263007ee0b78 100644 (file)
@@ -134,75 +134,35 @@ ExportDialog::init ()
        cancel_button->signal_clicked().connect (sigc::mem_fun (*this, &ExportDialog::close_dialog));
        export_button->signal_clicked().connect (sigc::mem_fun (*this, &ExportDialog::do_export));
 
+       file_notebook->soundcloud_export_selector = soundcloud_selector;
+
        /* Done! */
 
        show_all_children ();
        progress_widget.hide_all();
 }
 
-void
-ExportDialog::expanded_changed ()
-{
-       set_resizable(advanced->get_expanded());
-}
-
 void
 ExportDialog::init_gui ()
 {
        Gtk::Alignment * preset_align = Gtk::manage (new Gtk::Alignment());
        preset_align->add (*preset_selector);
        preset_align->set_padding (0, 12, 0, 0);
-       get_vbox()->pack_start (*preset_align, false, false, 0);
-
-       Gtk::VPaned * advanced_paned = Gtk::manage (new Gtk::VPaned());
-
-       Gtk::VBox* timespan_vbox = Gtk::manage (new Gtk::VBox());
-       timespan_vbox->set_spacing (12);
-       timespan_vbox->set_border_width (12);
-
-       Gtk::Alignment * timespan_align = Gtk::manage (new Gtk::Alignment());
-       timespan_label = Gtk::manage (new Gtk::Label (_("Time Span"), Gtk::ALIGN_LEFT));
-       timespan_align->add (*timespan_selector);
-       timespan_align->set_padding (0, 0, 18, 0);
-       timespan_vbox->pack_start (*timespan_label, false, false, 0);
-       timespan_vbox->pack_start (*timespan_align, true, true, 0);
-       advanced_paned->pack1(*timespan_vbox, true, false);
-
-       Gtk::VBox* channels_vbox = Gtk::manage (new Gtk::VBox());
-       channels_vbox->set_spacing (12);
-       channels_vbox->set_border_width (12);
-
-       Gtk::Alignment * channels_align = Gtk::manage (new Gtk::Alignment());
-       channels_label = Gtk::manage (new Gtk::Label (_("Channels"), Gtk::ALIGN_LEFT));
-       channels_align->add (*channel_selector);
-       channels_align->set_padding (0, 12, 18, 0);
-       channels_vbox->pack_start (*channels_label, false, false, 0);
-       channels_vbox->pack_start (*channels_align, true, true, 0);
-       advanced_paned->pack2(*channels_vbox, channel_selector_is_expandable(), false);
-
-       get_vbox()->pack_start (*file_notebook, false, false, 0);
-       get_vbox()->pack_start (warning_widget, false, false, 0);
-       get_vbox()->pack_start (progress_widget, false, false, 0);
-
-       advanced = Gtk::manage (new Gtk::Expander (_("Time span and channel options")));
-       advanced->property_expanded().signal_changed().connect(
-               sigc::mem_fun(*this, &ExportDialog::expanded_changed));
-       advanced->add (*advanced_paned);
-
-       if (channel_selector_is_expandable()) {
-               advanced_sizegroup = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_VERTICAL);
-               advanced_sizegroup->add_widget(*timespan_selector);
-               advanced_sizegroup->add_widget(*channel_selector);
-       }
 
-       get_vbox()->pack_start (*advanced, true, true);
+       Gtk::VBox * file_format_selector = Gtk::manage (new Gtk::VBox());
+       file_format_selector->set_homogeneous (false);
+       file_format_selector->pack_start (*preset_align, false, false, 0);
+       file_format_selector->pack_start (*file_notebook, false, false, 0);
+       file_format_selector->pack_start (*soundcloud_selector, false, false, 0);
 
-       Pango::AttrList bold;
-       Pango::Attribute b = Pango::Attribute::create_attr_weight (Pango::WEIGHT_BOLD);
-       bold.insert (b);
+       export_notebook.append_page (*file_format_selector, _("File format"));
+       export_notebook.append_page (*timespan_selector, _("Time Span"));
+       export_notebook.append_page (*channel_selector, _("Channels"));
+       
+       get_vbox()->pack_start (export_notebook, true, true, 0);
+       get_vbox()->pack_end   (warning_widget, false, false, 0);
+       get_vbox()->pack_end   (progress_widget, false, false, 0);
 
-       timespan_label->set_attributes (bold);
-       channels_label->set_attributes (bold);
 }
 
 void
@@ -211,6 +171,7 @@ ExportDialog::init_components ()
        preset_selector.reset (new ExportPresetSelector ());
        timespan_selector.reset (new ExportTimespanSelectorMultiple (_session, profile_manager));
        channel_selector.reset (new PortExportChannelSelector (_session, profile_manager));
+       soundcloud_selector.reset (new SoundcloudExportSelector ());
        file_notebook.reset (new ExportFileNotebook ());
 }
 
@@ -300,11 +261,35 @@ ExportDialog::show_conflicting_files ()
        dialog.run();
 }
 
+void
+ExportDialog::soundcloud_upload_progress(double total, double now, std::string title)
+{
+       soundcloud_selector->do_progress_callback(total, now, title);
+
+}
+
 void
 ExportDialog::do_export ()
 {
        try {
                profile_manager->prepare_for_export ();
+               handler->soundcloud_username     = soundcloud_selector->username ();
+               handler->soundcloud_password     = soundcloud_selector->password ();
+               handler->soundcloud_make_public  = soundcloud_selector->make_public ();
+               handler->soundcloud_open_page    = soundcloud_selector->open_page ();
+               handler->soundcloud_downloadable = soundcloud_selector->downloadable ();
+
+               handler->SoundcloudProgress.connect_same_thread(
+                               *this, 
+                               boost::bind(&ExportDialog::soundcloud_upload_progress, this, _1, _2, _3)
+                               );
+#if 0
+               handler->SoundcloudProgress.connect(
+                               *this, invalidator (*this),
+                               boost::bind(&ExportDialog::soundcloud_upload_progress, this, _1, _2, _3),
+                               gui_context()
+                               );
+#endif
                handler->do_export ();
                show_progress ();
        } catch(std::exception & e) {
@@ -418,6 +403,7 @@ ExportRangeDialog::init_components ()
        preset_selector.reset (new ExportPresetSelector ());
        timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, range_id));
        channel_selector.reset (new PortExportChannelSelector (_session, profile_manager));
+       soundcloud_selector.reset (new SoundcloudExportSelector ());
        file_notebook.reset (new ExportFileNotebook ());
 }
 
@@ -431,6 +417,7 @@ ExportSelectionDialog::init_components ()
        preset_selector.reset (new ExportPresetSelector ());
        timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, X_("selection")));
        channel_selector.reset (new PortExportChannelSelector (_session, profile_manager));
+       soundcloud_selector.reset (new SoundcloudExportSelector ());
        file_notebook.reset (new ExportFileNotebook ());
 }
 
@@ -444,8 +431,7 @@ void
 ExportRegionDialog::init_gui ()
 {
        ExportDialog::init_gui ();
-
-       channels_label->set_text (_("Source"));
+       export_notebook.set_tab_label_text(*export_notebook.get_nth_page(2), _("Source"));
 }
 
 void
@@ -456,6 +442,7 @@ ExportRegionDialog::init_components ()
        preset_selector.reset (new ExportPresetSelector ());
        timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, loc_id));
        channel_selector.reset (new RegionExportChannelSelector (_session, profile_manager, region, track));
+       soundcloud_selector.reset (new SoundcloudExportSelector ());
        file_notebook.reset (new ExportFileNotebook ());
 }
 
@@ -471,5 +458,6 @@ StemExportDialog::init_components ()
        preset_selector.reset (new ExportPresetSelector ());
        timespan_selector.reset (new ExportTimespanSelectorMultiple (_session, profile_manager));
        channel_selector.reset (new TrackExportChannelSelector (_session, profile_manager));
+       soundcloud_selector.reset (new SoundcloudExportSelector ());
        file_notebook.reset (new ExportFileNotebook ());
 }
index 756a3e7b53a4fda2f5b0288262157b7e894d445c..5e895e54c85a4e9b18320e1670e019db143ec05f 100644 (file)
@@ -32,6 +32,7 @@
 #include "export_file_notebook.h"
 #include "export_preset_selector.h"
 #include "ardour_dialog.h"
+#include "soundcloud_export_selector.h"
 
 #include <gtkmm.h>
 
@@ -43,7 +44,8 @@ namespace ARDOUR {
 class ExportTimespanSelector;
 class ExportChannelSelector;
 
-class ExportDialog : public ArdourDialog {
+class ExportDialog : public ArdourDialog, public PBD::ScopedConnectionList 
+{
 
   public:
 
@@ -75,26 +77,23 @@ class ExportDialog : public ArdourDialog {
        // Must initialize all the shared_ptrs below
        virtual void init_components ();
 
-       // Override if the channel selector should not be grown
-       virtual bool channel_selector_is_expandable() { return true; }
-
        boost::scoped_ptr<ExportPresetSelector>   preset_selector;
        boost::scoped_ptr<ExportTimespanSelector> timespan_selector;
        boost::scoped_ptr<ExportChannelSelector>  channel_selector;
        boost::scoped_ptr<ExportFileNotebook>     file_notebook;
 
+       boost::shared_ptr<SoundcloudExportSelector> soundcloud_selector;
+
        Gtk::VBox                                 warning_widget;
        Gtk::VBox                                 progress_widget;
 
-       Gtk::Label *                              timespan_label;
-       Gtk::Label *                              channels_label;
+       /*** GUI components ***/
+       Gtk::Notebook export_notebook;
 
   private:
 
        void init ();
 
-       void expanded_changed();
-
        void notify_errors (bool force = false);
        void close_dialog ();
 
@@ -112,10 +111,7 @@ class ExportDialog : public ArdourDialog {
        PublicEditor &  editor;
        StatusPtr       status;
 
-       /*** GUI components ***/
 
-       Glib::RefPtr<Gtk::SizeGroup> advanced_sizegroup;
-       Gtk::Expander * advanced;
 
        /* Warning area */
 
@@ -138,6 +134,8 @@ class ExportDialog : public ArdourDialog {
 
        float previous_progress; // Needed for gtk bug workaround
 
+       void soundcloud_upload_progress(double total, double now, std::string title);
+
        /* Buttons */
 
        Gtk::Button *           cancel_button;
@@ -170,9 +168,6 @@ class ExportRegionDialog : public ExportDialog
   public:
        ExportRegionDialog (PublicEditor & editor, ARDOUR::AudioRegion const & region, ARDOUR::AudioTrack & track);
 
-  protected:
-       virtual bool channel_selector_is_expandable() { return false; }
-
   private:
        void init_gui ();
        void init_components ();
index 54c0f628c7cf6fabad1d95bce3175aaa0310c088..ae924c10e3d9ae754f9afc25c7582c30d2fe6161 100644 (file)
@@ -27,6 +27,7 @@
 #include "i18n.h"
 
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 
 ExportFileNotebook::ExportFileNotebook () :
@@ -86,27 +87,41 @@ ExportFileNotebook::sync_with_manager ()
        }
 
        set_current_page (0);
+       update_soundcloud_upload ();
        CriticalSelectionChanged ();
 }
 
 void
-ExportFileNotebook::update_example_filenames()
+ExportFileNotebook::update_soundcloud_upload ()
 {
-       int i = 0;
-       FilePage * page;
-       while ((page = dynamic_cast<FilePage *> (get_nth_page (i++)))) {
-               page->update_example_filename();
+       int i;
+       bool show_credentials_entry = false;
+       ExportProfileManager::FormatStateList const & formats = profile_manager->get_formats ();
+       ExportProfileManager::FormatStateList::const_iterator format_it;
+
+       for (i = 0, format_it = formats.begin(); format_it != formats.end(); ++i, ++format_it) {
+               FilePage * page;
+               if ((page = dynamic_cast<FilePage *> (get_nth_page (i)))) {
+                       bool this_soundcloud_upload = page->get_soundcloud_upload ();
+                       (*format_it)->format->set_soundcloud_upload (this_soundcloud_upload);
+                       if (this_soundcloud_upload) {
+                               show_credentials_entry  = true;
+                       }
+               }
        }
+
+       soundcloud_export_selector->set_visible (show_credentials_entry);
+
 }
 
-std::string
-ExportFileNotebook::get_nth_format_name (uint32_t n)
+void
+ExportFileNotebook::update_example_filenames ()
 {
+       int i = 0;
        FilePage * page;
-       if ((page = dynamic_cast<FilePage *> (get_nth_page (n - 1)))) {
-               return page->get_format_name();
+       while ((page = dynamic_cast<FilePage *> (get_nth_page (i++)))) {
+               page->update_example_filename();
        }
-       return "";
 }
 
 void
@@ -177,6 +192,7 @@ ExportFileNotebook::FilePage::FilePage (Session * s, ManagerPtr profile_manager,
 
   format_label (_("Format"), Gtk::ALIGN_LEFT),
   filename_label (_("Location"), Gtk::ALIGN_LEFT),
+  soundcloud_upload_button (_("Upload to Soundcloud")),
   tab_number (number)
 {
        set_border_width (12);
@@ -185,6 +201,7 @@ ExportFileNotebook::FilePage::FilePage (Session * s, ManagerPtr profile_manager,
        pack_start (format_align, false, false, 0);
        pack_start (filename_label, false, false, 0);
        pack_start (filename_align, false, false, 0);
+       pack_start (soundcloud_upload_button, false, false, 0);
 
        format_align.add (format_selector);
        format_align.set_padding (6, 12, 18, 0);
@@ -219,6 +236,7 @@ ExportFileNotebook::FilePage::FilePage (Session * s, ManagerPtr profile_manager,
        filename_selector.CriticalSelectionChanged.connect (
                sigc::mem_fun (*this, &ExportFileNotebook::FilePage::critical_selection_changed));
 
+       soundcloud_upload_button.signal_toggled().connect (sigc::mem_fun (*parent, &ExportFileNotebook::update_soundcloud_upload));
        /* Tab widget */
 
        tab_close_button.add (*Gtk::manage (new Gtk::Image (::get_icon("close"))));
@@ -255,6 +273,12 @@ ExportFileNotebook::FilePage::get_format_name () const
        return _("No format!");
 }
 
+bool
+ExportFileNotebook::FilePage::get_soundcloud_upload () const
+{
+       return soundcloud_upload_button.get_active ();
+}
+
 void
 ExportFileNotebook::FilePage::save_format_to_manager (FormatPtr format)
 {
index 5555828e98e5a6dc1e35759fa39038dd8050efb4..42a05e7611314307a07025c04e811a44774ccf07 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "export_format_selector.h"
 #include "export_filename_selector.h"
+#include "soundcloud_export_selector.h"
 
 class ExportFileNotebook : public Gtk::Notebook, public ARDOUR::SessionHandlePtr
 {
@@ -38,10 +39,9 @@ class ExportFileNotebook : public Gtk::Notebook, public ARDOUR::SessionHandlePtr
 
        void set_session_and_manager (ARDOUR::Session * s, boost::shared_ptr<ARDOUR::ExportProfileManager> manager);
        void sync_with_manager ();
-
        void update_example_filenames();
 
-       std::string get_nth_format_name (uint32_t n);
+       boost::shared_ptr<SoundcloudExportSelector> soundcloud_export_selector;
 
        sigc::signal<void> CriticalSelectionChanged;
 
@@ -58,6 +58,7 @@ class ExportFileNotebook : public Gtk::Notebook, public ARDOUR::SessionHandlePtr
        void add_file_page (ARDOUR::ExportProfileManager::FormatStatePtr format_state, ARDOUR::ExportProfileManager::FilenameStatePtr filename_state);
        void remove_file_page (FilePage * page);
        void update_remove_file_page_sensitivity ();
+       void update_soundcloud_upload ();
 
        sigc::connection page_change_connection;
        void handle_page_change (GtkNotebookPage*, uint32_t page);
@@ -80,6 +81,7 @@ class ExportFileNotebook : public Gtk::Notebook, public ARDOUR::SessionHandlePtr
                Gtk::Widget & get_tab_widget () { return tab_widget; }
                void set_remove_sensitive (bool value);
                std::string get_format_name () const;
+               bool get_soundcloud_upload () const;
 
                void update_example_filename();
 
@@ -108,6 +110,7 @@ class ExportFileNotebook : public Gtk::Notebook, public ARDOUR::SessionHandlePtr
                Gtk::Alignment          filename_align;
                ExportFilenameSelector  filename_selector;
 
+               Gtk::CheckButton        soundcloud_upload_button;
                Gtk::HBox               tab_widget;
                Gtk::Label              tab_label;
                Gtk::Alignment          tab_close_alignment;
index c5d1573d54bff71de2a897a866fa62eff3e74fa7..a6d3282ce8e891e304503071b70ed19e992ad223 100644 (file)
@@ -51,6 +51,8 @@ ExportFormatDialog::ExportFormatDialog (FormatPtr format, bool new_dialog) :
   silence_end_checkbox (_("Add silence at end:")),
   silence_end_clock ("silence_end", true, "", true, false, true),
 
+  command_label(_("Command to run post-export\n(%f=full path & filename, %d=directory, %b=basename):")),
+
   format_table (3, 4),
   compatibility_label (_("Compatibility"), Gtk::ALIGN_LEFT),
   quality_label (_("Quality"), Gtk::ALIGN_LEFT),
@@ -113,6 +115,9 @@ ExportFormatDialog::ExportFormatDialog (FormatPtr format, bool new_dialog) :
        silence_table.attach (silence_end_checkbox, 1, 2, 2, 3);
        silence_table.attach (silence_end_clock, 2, 3, 2, 3);
 
+       get_vbox()->pack_start (command_label, false, false);
+       get_vbox()->pack_start (command_entry, false, false);
+
        /* Format table */
 
        init_format_table();
@@ -142,6 +147,7 @@ ExportFormatDialog::ExportFormatDialog (FormatPtr format, bool new_dialog) :
 
        with_cue.signal_toggled().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_with_cue));
        with_toc.signal_toggled().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_with_toc));
+       command_entry.signal_changed().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_command));
 
        cue_toc_vbox.pack_start (with_cue, false, false);
        cue_toc_vbox.pack_start (with_toc, false, false);
@@ -296,6 +302,7 @@ ExportFormatDialog::load_state (FormatPtr spec)
        }
 
        tag_checkbox.set_active (spec->tag());
+       command_entry.set_text (spec->command());
 }
 
 void
@@ -717,6 +724,13 @@ ExportFormatDialog::update_with_toc ()
        manager.select_with_toc (with_toc.get_active());
 }
 
+
+void
+ExportFormatDialog::update_command ()
+{
+       manager.set_command (command_entry.get_text());
+}
+
 void
 ExportFormatDialog::update_description()
 {
index 3e38cf09d662a55b3e67c8dbfb9706f7d4e3d9df..8a3211db233debd8b25d53da7c4fa2671e3f28fd 100644 (file)
@@ -179,6 +179,11 @@ class ExportFormatDialog : public ArdourDialog, public PBD::ScopedConnectionList
        Gtk::CheckButton silence_end_checkbox;
        AudioClock       silence_end_clock;
 
+       /* Post-export hook */
+       
+       Gtk::Label       command_label;
+       Gtk::Entry       command_entry;
+
        /* Format table */
 
        struct CompatibilityCols : public Gtk::TreeModelColumnRecord
@@ -311,6 +316,7 @@ class ExportFormatDialog : public ArdourDialog, public PBD::ScopedConnectionList
 
        void update_with_toc ();
        void update_with_cue ();
+       void update_command ();
 
        Gtk::TreeView sample_format_view;
        Gtk::TreeView dither_type_view;
index 97a8dba25fa7de301c4eca26d70e8d2689476989..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,209 +0,0 @@
-/*
-    Copyright (C) 2006 Paul Davis
-    Author: Andre Raue
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <sys/stat.h>
-
-#include <sstream>
-
-#include "ardour/audioengine.h"
-#include "ardour/sndfile_helpers.h"
-
-#include "ardour_ui.h"
-#include "export_range_markers_dialog.h"
-
-#include "i18n.h"
-
-using namespace Gtk;
-using namespace ARDOUR;
-using namespace PBD;
-using namespace std;
-
-ExportRangeMarkersDialog::ExportRangeMarkersDialog (PublicEditor& editor)
-       : ExportDialog(editor)
-{
-       set_title (_("Export Ranges"));
-       file_frame.set_label (_("Export to Directory"));
-
-       do_not_allow_export_cd_markers();
-
-       total_duration = 0;
-       current_range_marker_index = 0;
-}
-
-Gtk::FileChooserAction
-ExportRangeMarkersDialog::browse_action () const
-{
-       return Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER;
-}
-
-void
-ExportRangeMarkersDialog::export_data ()
-{
-       getSession().locations()->apply(*this, &ExportRangeMarkersDialog::process_range_markers_export);
-}
-
-void
-ExportRangeMarkersDialog::process_range_markers_export(Locations::LocationList& locations)
-{
-       Locations::LocationList::iterator locationIter;
-       current_range_marker_index = 0;
-       init_progress_computing(locations);
-
-       for (locationIter = locations.begin(); locationIter != locations.end(); ++locationIter) {
-               Location *currentLocation = (*locationIter);
-
-               if(currentLocation->is_range_marker()){
-                       // init filename
-                       string filepath = get_target_filepath(
-                               get_selected_file_name(),
-                               currentLocation->name(),
-                               get_selected_header_format());
-
-                       initSpec(filepath);
-
-                       spec.start_frame = currentLocation->start();
-                       spec.end_frame = currentLocation->end();
-
-                       if (getSession().start_export(spec)){
-                               // if export fails
-                               return;
-                       }
-
-                       // wait until export of this range finished
-                       gtk_main_iteration();
-
-                       while (spec.running){
-                               if(gtk_events_pending()){
-                                       gtk_main_iteration();
-                               }else {
-                                       Glib::usleep(10000);
-                               }
-                       }
-
-                       current_range_marker_index++;
-
-                       getSession().stop_export (spec);
-               }
-       }
-
-       spec.running = false;
-}
-
-
-string
-ExportRangeMarkersDialog::get_target_filepath(string path, string filename, string postfix)
-{
-       string target_path = path;
-       if ((target_path.find_last_of ('/')) != string::npos) {
-               target_path += '/';
-       }
-
-       string target_filepath = target_path + filename + postfix;
-       struct stat statbuf;
-
-       for(int counter=1; (stat (target_filepath.c_str(), &statbuf) == 0); counter++){
-               // while file exists
-               ostringstream scounter;
-               scounter.flush();
-               scounter << counter;
-
-               target_filepath =
-                       target_path + filename + "_" + scounter.str() + postfix;
-       }
-
-       return target_filepath;
-}
-
-bool
-ExportRangeMarkersDialog::is_filepath_valid(string &filepath)
-{
-       // sanity check file name first
-       struct stat statbuf;
-
-       if (filepath.empty()) {
-               // warning dialog
-               string txt = _("Please enter a valid target directory.");
-               MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
-               msg.run();
-               return false;
-       }
-
-       if ( (stat (filepath.c_str(), &statbuf) != 0) ||
-               (!S_ISDIR (statbuf.st_mode)) ) {
-               string txt = _("Please select an existing target directory. Files are not allowed!");
-               MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
-               msg.run();
-               return false;
-       }
-
-       // directory needs to exist and be writable
-       string dirpath = Glib::path_get_dirname (filepath);
-       if (!exists_and_writable (dirpath)) {
-               string txt = _("Cannot write file in: ") + dirpath;
-               MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
-               msg.run();
-               return false;
-       }
-
-       return true;
-}
-
-void
-ExportRangeMarkersDialog::init_progress_computing(Locations::LocationList& locations)
-{
-       // flush vector
-       range_markers_durations_aggregated.resize(0);
-
-       framecnt_t duration_before_current_location = 0;
-       Locations::LocationList::iterator locationIter;
-
-       for (locationIter = locations.begin(); locationIter != locations.end(); ++locationIter) {
-               Location *currentLocation = (*locationIter);
-
-               if(currentLocation->is_range_marker()){
-                       range_markers_durations_aggregated.push_back (duration_before_current_location);
-
-                       framecnt_t duration = currentLocation->end() - currentLocation->start();
-
-                       range_markers_durations.push_back (duration);
-                       duration_before_current_location += duration;
-               }
-       }
-
-       total_duration = duration_before_current_location;
-}
-
-
-gint
-ExportRangeMarkersDialog::progress_timeout ()
-{
-       double progress = 0.0;
-
-       if (current_range_marker_index >= range_markers_durations.size()){
-               progress = 1.0;
-       } else{
-               progress = ((double) range_markers_durations_aggregated[current_range_marker_index] +
-                           (spec.progress * (double) range_markers_durations[current_range_marker_index])) /
-                       (double) total_duration;
-       }
-
-       set_progress_fraction( progress );
-       return TRUE;
-}
index b0a29b5dc291e737acde9a06fd1756e756ed7ec5..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,66 +0,0 @@
-/*
-    Copyright (C) 2006 Andre Raue
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef __export_range_markers_dialog_h__
-#define __export_range_markers_dialog_h__
-
-#include "ardour/location.h"
-
-#include "export_dialog.h"
-
-
-class ExportRangeMarkersDialog : public ExportDialog
-{
-  public:
-       ExportRangeMarkersDialog (PublicEditor&);
-
-       Gtk::FileChooserAction browse_action() const;
-
-  protected:
-       virtual bool is_filepath_valid(string &filepath);
-
-       void export_data();
-
-       bool wants_dir() { return true; }
-
-  private:
-       // keeps the duration of all range_markers before the current
-       vector<nframes_t>       range_markers_durations_aggregated;
-       vector<nframes_t>       range_markers_durations;
-       // duration of all range markers
-       nframes_t       total_duration;
-       // index of range marker, that get's exported right now
-       unsigned int    current_range_marker_index;
-
-       // sets value of progress bar
-       virtual gint progress_timeout ();
-
-       // initializes range_markers_durations_aggregated, range_markers_durations
-       // and total_duration
-       void init_progress_computing(ARDOUR::Locations::LocationList& locations);
-
-       // searches for a filename like "<filename><nr>.<postfix>" in path, that
-       // does not exist
-    string get_target_filepath(string path, string filename, string postfix);
-
-       void process_range_markers_export(ARDOUR::Locations::LocationList&);
-};
-
-
-#endif // __export_range_markers_dialog_h__
index d6ca02fe0387ab91e5d4395ad2a415608eff5d1a..61d813d2229cf4b751b651517657777d8904eb57 100644 (file)
@@ -105,6 +105,9 @@ ExportTimespanSelector::ExportTimespanSelector (ARDOUR::Session * session, Profi
        /* Range view */
 
        range_list = Gtk::ListStore::create (range_cols);
+       // order by location start times
+       range_list->set_sort_column(range_cols.location, Gtk::SORT_ASCENDING);
+       range_list->set_sort_func(range_cols.location, sigc::mem_fun(*this, &ExportTimespanSelector::location_sorter));
        range_view.set_model (range_list);
        range_view.set_headers_visible (true);
 }
@@ -114,6 +117,22 @@ ExportTimespanSelector::~ExportTimespanSelector ()
 
 }
 
+int
+ExportTimespanSelector::location_sorter(Gtk::TreeModel::iterator a, Gtk::TreeModel::iterator b)
+{
+       Location *l1 = (*a)[range_cols.location];
+       Location *l2 = (*b)[range_cols.location];
+       const Location *ls = _session->locations()->session_range_location();
+
+       // always sort session range first
+       if (l1 == ls)
+               return -1;
+       if (l2 == ls)
+               return +1;
+
+       return l1->start() - l2->start();
+}
+
 void
 ExportTimespanSelector::add_range_to_selection (ARDOUR::Location const * loc)
 {
index 5556f5f676eb4dae77abfa03a16b0e628b592d69..12166709911688721999564836dc900eb2aea847 100644 (file)
@@ -89,6 +89,7 @@ class ExportTimespanSelector : public Gtk::VBox, public ARDOUR::SessionHandlePtr
        void update_range_name (std::string const & path, std::string const & new_text);
 
        void set_selection_state_of_all_timespans (bool);
+       int location_sorter(Gtk::TreeModel::iterator a, Gtk::TreeModel::iterator b);
 
        /*** GUI components ***/
 
@@ -132,7 +133,7 @@ class ExportTimespanSelector : public Gtk::VBox, public ARDOUR::SessionHandlePtr
        Gtk::ScrolledWindow          range_scroller;
 };
 
-/// Allows seleting multiple timespans
+/// Allows selecting multiple timespans
 class ExportTimespanSelectorMultiple : public ExportTimespanSelector
 {
   public:
index a9d0f99ad1246623199dd31d4e4d8cfc888dae11..e5f189073724075d3044b5d46fcfe784692f2886 100644 (file)
@@ -51,7 +51,6 @@
 #include "ardour/session_metadata.h"
 #include "ardour/broadcast_info.h"
 
-#include "utils.h"
 #include "opts.h"
 #include "export_video_dialog.h"
 #include "utils_videotl.h"
index 7e781cafd93cd1d6724f33f68dfd89264a229c2b..76a7c3540e051520b22e762cde145640997ccb0c 100644 (file)
@@ -54,6 +54,7 @@
 #include "i18n.h"
 
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtkmm2ext;
 using namespace Gtk;
index faec8fb4ae109f56d864844073e58b4a425d5a03..2ecfcca44f99f01cab457ad7a26880f38b7125d7 100644 (file)
@@ -45,7 +45,6 @@
 #include "ardour_ui.h"
 #include "prompter.h"
 #include "plugin_ui.h"
-#include "utils.h"
 #include "gui_thread.h"
 #include "automation_controller.h"
 
@@ -636,7 +635,6 @@ GenericPluginUI::build_control_ui (guint32 port_index, boost::shared_ptr<Automat
                        control_ui->clickbox = new ClickBox (adj, "PluginUIClickBox");
                        Gtkmm2ext::set_size_request_to_display_given_text (*control_ui->clickbox, "g9999999", 2, 2);
                        if (desc.midinote) {
-                               printf("MIDI NOTE\n");
                                control_ui->clickbox->set_printer (sigc::bind (sigc::mem_fun (*this, &GenericPluginUI::midinote_printer), control_ui));
                        } else {
                                control_ui->clickbox->set_printer (sigc::bind (sigc::mem_fun (*this, &GenericPluginUI::integer_printer), control_ui));
index da2beeeca7069f378e4c519afb732d1c84fafaa6..759ffb94d23a824823f5f8c682c5deda89559093 100644 (file)
@@ -18,7 +18,7 @@
 */
 
 #include "evoral/Note.hpp"
-#include "canvas/group.h"
+#include "canvas/container.h"
 #include "canvas/rectangle.h"
 #include "canvas/wave_view.h"
 #include "canvas/debug.h"
@@ -38,11 +38,11 @@ using namespace ARDOUR;
 
 PBD::Signal1<void,GhostRegion*> GhostRegion::CatchDeletion;
 
-GhostRegion::GhostRegion (ArdourCanvas::Group* parent, TimeAxisView& tv, TimeAxisView& source_tv, double initial_pos)
+GhostRegion::GhostRegion (ArdourCanvas::Container* parent, TimeAxisView& tv, TimeAxisView& source_tv, double initial_pos)
        : trackview (tv)
        , source_trackview (source_tv)
 {
-       group = new ArdourCanvas::Group (parent);
+       group = new ArdourCanvas::Container (parent);
        CANVAS_DEBUG_NAME (group, "ghost region");
        group->set_position (ArdourCanvas::Duple (initial_pos, 0));
 
@@ -191,7 +191,7 @@ MidiGhostRegion::~MidiGhostRegion()
        clear_events ();
 }
 
-MidiGhostRegion::GhostEvent::GhostEvent (NoteBase* e, ArdourCanvas::Group* g)
+MidiGhostRegion::GhostEvent::GhostEvent (NoteBase* e, ArdourCanvas::Container* g)
        : event (e)
 {
        rect = new ArdourCanvas::Rectangle (g, ArdourCanvas::Rect (e->x0(), e->y0(), e->x1(), e->y1()));
index e8271a8ad8aa6e947b69c16518198a89a51caf70..85b6d96ed143ba2b9260c691fb2103f1f5c04cdb 100644 (file)
@@ -36,7 +36,7 @@ class TimeAxisView;
 class GhostRegion : public sigc::trackable
 {
 public:
-       GhostRegion(ArdourCanvas::Group* parent, TimeAxisView& tv, TimeAxisView& source_tv, double initial_unit_pos);
+       GhostRegion(ArdourCanvas::Container* parent, TimeAxisView& tv, TimeAxisView& source_tv, double initial_unit_pos);
        virtual ~GhostRegion();
 
        virtual void set_samples_per_pixel (double) = 0;
@@ -52,7 +52,7 @@ public:
        TimeAxisView& trackview;
        /** TimeAxisView that we are a ghost for */
        TimeAxisView& source_trackview;
-       ArdourCanvas::Group* group;
+       ArdourCanvas::Container* group;
        ArdourCanvas::Rectangle* base_rect;
 
        static PBD::Signal1<void,GhostRegion*> CatchDeletion;
@@ -73,7 +73,7 @@ class MidiGhostRegion : public GhostRegion {
 public:
        class GhostEvent : public sigc::trackable {
          public:
-           GhostEvent(::NoteBase *, ArdourCanvas::Group *);
+           GhostEvent(::NoteBase *, ArdourCanvas::Container *);
            virtual ~GhostEvent ();
            
            NoteBase* event;
index 218cfe7a1cadb2d0b112a013ceabcf843cc9d95c..706b4b672752038e893703b3cd71d5968cbff890 100644 (file)
@@ -32,6 +32,7 @@
 
 using namespace std;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 
 GlobalPortMatrix::GlobalPortMatrix (Gtk::Window* p, Session* s, DataType t)
        : PortMatrix (p, s, t)
index 3bd6f100a96cdc6df015fc7c88f9bdcdc04225ec..f1abb8dbb8795852b4b2cbfc9092a480c4b88f89 100644 (file)
@@ -22,7 +22,9 @@
 
 #include <sigc++/signal.h>
 
+namespace ARDOUR_UI_UTILS {
 extern sigc::signal<void>  ColorsChanged;
 extern sigc::signal<void>  DPIReset;
+} // namespace
 
 #endif /* __gtk_ardour_global_signals_h__ */
index 05e843870934ec735698e7d386bac971e767db7c..b64713362af8a5cfb0c25d24302e5b5b725fcbfc 100644 (file)
 
 #include "gui_thread.h"
 #include "route_group_dialog.h"
+#include "global_signals.h"
 #include "group_tabs.h"
 #include "keyboard.h"
 #include "i18n.h"
 #include "ardour_ui.h"
+#include "rgb_macros.h"
 #include "utils.h"
 
 using namespace std;
 using namespace Gtk;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using Gtkmm2ext::Keyboard;
 
 list<Gdk::Color> GroupTabs::_used_colors;
@@ -43,6 +46,7 @@ GroupTabs::GroupTabs ()
        , _dragging_new_tab (0)
 {
        add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
+       ColorsChanged.connect (sigc::mem_fun (*this, &GroupTabs::queue_draw));  
 }
 
 GroupTabs::~GroupTabs ()
@@ -535,19 +539,30 @@ GroupTabs::remove_group (RouteGroup* g)
 
 /** Set the color of the tab of a route group */
 void
-GroupTabs::set_group_color (RouteGroup* group, Gdk::Color color)
+GroupTabs::set_group_color (RouteGroup* group, uint32_t color)
 {
        assert (group);
+       uint32_t r, g, b, a;
+
+       UINT_TO_RGBA (color, &r, &g, &b, &a);
 
        /* Hack to disallow black route groups; force a dark grey instead */
-       if (color.get_red() == 0 && color.get_green() == 0 && color.get_blue() == 0) {
-               color.set_grey_p (0.1);
+
+       if (r == 0 && g == 0 && b == 0) {
+               r = 25;
+               g = 25;
+               b = 25;
        }
        
        GUIObjectState& gui_state = *ARDOUR_UI::instance()->gui_object_state;
 
        char buf[64];
-       snprintf (buf, sizeof (buf), "%d:%d:%d", color.get_red(), color.get_green(), color.get_blue());
+       
+       /* for historical reasons the colors must be stored as 16 bit color
+        * values. Ugh.
+        */
+
+       snprintf (buf, sizeof (buf), "%d:%d:%d", (r<<8), (g<<8), (b<<8));
        gui_state.set_property (group_gui_id (group), "color", buf);
        
        /* the group color change notification */
@@ -577,35 +592,35 @@ GroupTabs::group_gui_id (RouteGroup* group)
 }
 
 /** @return the color to use for a route group tab */
-Gdk::Color
+uint32_t
 GroupTabs::group_color (RouteGroup* group)
 {
        assert (group);
        
        GUIObjectState& gui_state = *ARDOUR_UI::instance()->gui_object_state;
-
        string const gui_id = group_gui_id (group);
-
        bool empty;
        string const color = gui_state.get_string (gui_id, "color", &empty);
+
        if (empty) {
                /* no color has yet been set, so use a random one */
-               Gdk::Color const color = unique_random_color (_used_colors);
-               set_group_color (group, color);
-               return color;
+               uint32_t c = gdk_color_to_rgba (unique_random_color (_used_colors));
+               set_group_color (group, c);
+               return c;
        }
 
-       Gdk::Color c;
-
        int r, g, b;
 
+       /* for historical reasons, colors are stored as 16 bit values.
+        */
+
        sscanf (color.c_str(), "%d:%d:%d", &r, &g, &b);
 
-       c.set_red (r);
-       c.set_green (g);
-       c.set_blue (b);
-       
-       return c;
+       r /= 256;
+       g /= 256;
+       b /= 256;
+
+       return RGBA_TO_UINT (r, g, b, 255);
 }
 
 void
index 0d83a553c177ae2a29337d62c2756034e43bd765..ff231e8a681c89a8105f0bb1eef91ab4be5fd150 100644 (file)
@@ -50,9 +50,9 @@ public:
 
        void run_new_group_dialog (ARDOUR::RouteList const &);
 
-       static void set_group_color (ARDOUR::RouteGroup *, Gdk::Color);
+       static void set_group_color (ARDOUR::RouteGroup *, uint32_t);
        static std::string group_gui_id (ARDOUR::RouteGroup *);
-       static Gdk::Color group_color (ARDOUR::RouteGroup *);
+       static uint32_t group_color (ARDOUR::RouteGroup *);
 
 protected:
 
@@ -61,7 +61,7 @@ protected:
 
                double from;
                double to;
-               Gdk::Color color; ///< color
+               uint32_t color; ///< color
                ARDOUR::RouteGroup* group; ///< route group
        };
 
diff --git a/gtk2_ardour/gtk-custom-hruler.c b/gtk2_ardour/gtk-custom-hruler.c
deleted file mode 100644 (file)
index 5f17ffe..0000000
+++ /dev/null
@@ -1,267 +0,0 @@
-/* GTK - The GIMP Toolkit
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/*
- * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
- * file for a list of people on the GTK+ Team.  See the ChangeLog
- * files for a list of changes.  These files are distributed with
- * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/* modified by andreas meyer <hexx3000@gmx.de> */
-/* subsequently specialized for audio time displays by paul davis <paul@linuxaudiosystems.com> */
-
-#include <math.h>
-#include <stdio.h>
-#include <string.h>
-#include "gtk-custom-hruler.h"
-
-#define RULER_HEIGHT          14
-#define MINIMUM_INCR          5
-#define MAXIMUM_SUBDIVIDE     5
-
-#define ROUND(x) ((int) ((x) + 0.5))
-
-static void gtk_custom_hruler_class_init (GtkCustomHRulerClass * klass);
-static void gtk_custom_hruler_init (GtkCustomHRuler * custom_hruler);
-static gint gtk_custom_hruler_motion_notify (GtkWidget * widget, GdkEventMotion * event);
-static void gtk_custom_hruler_draw_ticks (GtkCustomRuler * ruler);
-static void gtk_custom_hruler_draw_pos (GtkCustomRuler * ruler);
-
-GType gtk_custom_hruler_get_type (void)
-{
-       static GType hruler_type = 0;
-
-       if (!hruler_type) {
-               static const GTypeInfo hruler_info =
-                       {
-                               sizeof (GtkCustomHRulerClass),
-                               NULL,           /* base_init */
-                               NULL,           /* base_finalize */
-                               (GClassInitFunc) gtk_custom_hruler_class_init,
-                               NULL,           /* class_finalize */
-                               NULL,           /* class_data */
-                               sizeof (GtkCustomHRuler),
-                               0,              /* n_preallocs */
-                               (GInstanceInitFunc) gtk_custom_hruler_init,
-                               NULL /* value_table */
-                       };
-
-               hruler_type = g_type_register_static (gtk_custom_ruler_get_type(), "GtkCustomHRuler",
-                                                     &hruler_info, (GTypeFlags)0);
-       }
-
-       return hruler_type;
-}
-
-static void
-gtk_custom_hruler_class_init (GtkCustomHRulerClass * klass)
-{
-       GtkWidgetClass *widget_class;
-       GtkCustomRulerClass *ruler_class;
-
-       widget_class = (GtkWidgetClass *) klass;
-       ruler_class = (GtkCustomRulerClass *) klass;
-
-       widget_class->motion_notify_event = gtk_custom_hruler_motion_notify;
-
-       ruler_class->draw_ticks = gtk_custom_hruler_draw_ticks;
-       ruler_class->draw_pos = gtk_custom_hruler_draw_pos;
-}
-
-static void
-gtk_custom_hruler_init (GtkCustomHRuler * custom_hruler)
-{
-       GtkWidget *widget;
-
-       widget = GTK_WIDGET (custom_hruler);
-       widget->requisition.width = widget->style->xthickness * 2 + 1;
-       widget->requisition.height = widget->style->ythickness * 2 + RULER_HEIGHT;
-}
-
-
-GtkWidget *
-gtk_custom_hruler_new (void)
-{
-       return GTK_WIDGET (gtk_type_new (gtk_custom_hruler_get_type ()));
-}
-
-static gint
-gtk_custom_hruler_motion_notify (GtkWidget * widget, GdkEventMotion * event)
-{
-       GtkCustomRuler *ruler;
-       gint x;
-
-       g_return_val_if_fail (widget != NULL, FALSE);
-       g_return_val_if_fail (GTK_IS_CUSTOM_HRULER (widget), FALSE);
-       g_return_val_if_fail (event != NULL, FALSE);
-
-       ruler = GTK_CUSTOM_RULER (widget);
-
-       if (event->is_hint)
-               gdk_window_get_pointer (widget->window, &x, NULL, NULL);
-       else
-               x = event->x;
-
-       ruler->position = ruler->lower + ((ruler->upper - ruler->lower) * x) / widget->allocation.width;
-
-       /*  Make sure the ruler has been allocated already  */
-       if (ruler->backing_store != NULL)
-               gtk_custom_ruler_draw_pos (ruler);
-
-       return FALSE;
-}
-
-static void
-gtk_custom_hruler_draw_ticks (GtkCustomRuler * ruler)
-{
-       GtkWidget *widget;
-       GdkGC *gc;
-       gint i;
-       GtkCustomRulerMark *marks;
-       gint ythickness;
-       gint nmarks;
-       gint max_chars;
-       gint digit_offset;
-       PangoLayout *layout;
-       PangoRectangle logical_rect, ink_rect;
-
-       g_return_if_fail (ruler != NULL);
-       g_return_if_fail (GTK_IS_CUSTOM_HRULER (ruler));
-
-       if (!GTK_WIDGET_DRAWABLE (ruler))
-               return;
-
-       widget = GTK_WIDGET (ruler);
-
-       gc = widget->style->fg_gc[GTK_STATE_NORMAL];
-
-       layout = gtk_widget_create_pango_layout (widget, "012456789");
-       pango_layout_get_extents (layout, &ink_rect, &logical_rect);
-
-       digit_offset = ink_rect.y;
-
-       ythickness = widget->style->ythickness;
-
-       gtk_paint_box (widget->style, ruler->backing_store,
-                      GTK_STATE_NORMAL, GTK_SHADOW_NONE,
-                      NULL, widget, "custom_hruler", 0, 0, widget->allocation.width, widget->allocation.height);
-
-       gdk_draw_line (ruler->backing_store, gc, 0, widget->allocation.height - 1,
-                      widget->allocation.width, widget->allocation.height - 1);
-
-       if ((ruler->upper - ruler->lower) == 0) {
-               return;
-       }
-
-       /* we have to assume a fixed width font here */
-
-       max_chars = widget->allocation.width / 12; // XXX FIX ME: pixel with of the char `8'
-
-       nmarks = ruler->metric->get_marks (&marks, ruler->lower, ruler->upper, max_chars);
-
-       for (i = 0; i < nmarks; i++) {
-               gint pos;
-               gint height;
-
-               pos = ROUND ((marks[i].position - ruler->lower) / ruler->metric->units_per_pixel);
-               height = widget->allocation.height;
-
-               switch (marks[i].style) {
-               case GtkCustomRulerMarkMajor:
-                       gdk_draw_line (ruler->backing_store, gc, pos, height, pos, 0);
-                       break;
-               case GtkCustomRulerMarkMinor:
-                       gdk_draw_line (ruler->backing_store, gc, pos, height, pos, height - (height/2));
-                       break;
-               case GtkCustomRulerMarkMicro:
-                       gdk_draw_line (ruler->backing_store, gc, pos, height, pos, height - 3);
-                       break;
-               }
-
-               pango_layout_set_text (layout, marks[i].label, -1);
-               pango_layout_get_extents (layout, &logical_rect, NULL);
-
-               gtk_paint_layout (widget->style,
-                                 ruler->backing_store,
-                                 GTK_WIDGET_STATE (widget),
-                                 FALSE,
-                                 NULL,
-                                 widget,
-                                 "hruler",
-                                 pos + 2, ythickness + PANGO_PIXELS (logical_rect.y - digit_offset),
-                                 layout);
-
-               g_free (marks[i].label);
-       }
-
-       if (nmarks) {
-               g_free (marks);
-       }
-
-       g_object_unref (layout);
-}
-
-static void
-gtk_custom_hruler_draw_pos (GtkCustomRuler * ruler)
-{
-       GtkWidget *widget;
-       GdkGC *gc;
-       int i;
-       gint x, y;
-       gint width, height;
-       gint bs_width, bs_height;
-       gint xthickness;
-       gint ythickness;
-       gfloat increment;
-
-       g_return_if_fail (ruler != NULL);
-       g_return_if_fail (GTK_IS_CUSTOM_HRULER (ruler));
-       if (GTK_WIDGET_DRAWABLE (ruler) && (ruler->upper - ruler->lower) > 0) {
-               widget = GTK_WIDGET (ruler);
-               gc = widget->style->fg_gc[GTK_STATE_NORMAL];
-               xthickness = widget->style->xthickness;
-               ythickness = widget->style->ythickness;
-               width = widget->allocation.width;
-               height = widget->allocation.height - ythickness * 2;
-
-               bs_width = height / 2;
-               bs_width |= 1;                  /* make sure it's odd */
-               bs_height = bs_width / 2 + 1;
-
-               if ((bs_width > 0) && (bs_height > 0)) {
-                       /*  If a backing store exists, restore the ruler  */
-                       if (ruler->backing_store && ruler->non_gr_exp_gc)
-                               gdk_draw_drawable (ruler->widget.window,
-                                                   ruler->non_gr_exp_gc,
-                                                   GDK_DRAWABLE(ruler->backing_store), ruler->xsrc, ruler->ysrc, ruler->xsrc, ruler->ysrc, bs_width, bs_height);
-
-                       increment = (gfloat) width / (ruler->upper - ruler->lower);
-                       x = ROUND ((ruler->position - ruler->lower) * increment) + (xthickness - bs_width) / 2 - 1;
-                       y = (height + bs_height) / 2 + ythickness;
-
-                       for (i = 0; i < bs_height; i++)
-                               gdk_draw_line (widget->window, gc, x + i, y + i, x + bs_width - 1 - i, y + i);
-
-
-                       ruler->xsrc = x;
-                       ruler->ysrc = y;
-               }
-       }
-}
diff --git a/gtk2_ardour/gtk-custom-hruler.h b/gtk2_ardour/gtk-custom-hruler.h
deleted file mode 100644 (file)
index 5213e30..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/* GTK - The GIMP Toolkit
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/*
- * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
- * file for a list of people on the GTK+ Team.  See the ChangeLog
- * files for a list of changes.  These files are distributed with
- * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/* modified by andreas meyer <hexx3000@gmx.de> */
-
-#ifndef __GTK_CUSTOM_HRULER_H__
-#define __GTK_CUSTOM_HRULER_H__
-
-
-#include <gdk/gdk.h>
-#include "gtk-custom-ruler.h"
-
-
-G_BEGIN_DECLS
-
-
-#define GTK_CUSTOM_HRULER(obj)          G_TYPE_CHECK_INSTANCE_CAST (obj, gtk_custom_hruler_get_type (), GtkCustomHRuler)
-#define GTK_CUSTOM_HRULER_CLASS(klass)  G_TYPE_CHECK_CLASS_CAST (klass, gtk_custom_hruler_get_type (), GtkCustomHRulerClass)
-#define GTK_IS_CUSTOM_HRULER(obj)       G_TYPE_CHECK_INSTANCE_TYPE (obj, gtk_custom_hruler_get_type ())
-
-
-typedef struct _GtkCustomHRuler       GtkCustomHRuler;
-typedef struct _GtkCustomHRulerClass  GtkCustomHRulerClass;
-
-struct _GtkCustomHRuler
-{
-  GtkCustomRuler ruler;
-};
-
-struct _GtkCustomHRulerClass
-{
-  GtkCustomRulerClass parent_class;
-};
-
-
-GType      gtk_custom_hruler_get_type (void);
-GtkWidget* gtk_custom_hruler_new      (void);
-
-
-G_END_DECLS
-
-
-#endif /* __GTK_CUSTOM_HRULER_H__ */
diff --git a/gtk2_ardour/gtk-custom-ruler.c b/gtk2_ardour/gtk-custom-ruler.c
deleted file mode 100644 (file)
index 16097fa..0000000
+++ /dev/null
@@ -1,493 +0,0 @@
-/* GTK - The GIMP Toolkit
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/*
- * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
- * file for a list of people on the GTK+ Team.  See the ChangeLog
- * files for a list of changes.  These files are distributed with
- * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/* modified by andreas meyer <hexx3000@gmx.de> */
-
-#include <stdio.h>
-#include "gettext.h"
-#define _(Text)  dgettext (PACKAGE,Text)
-
-#include "gtk-custom-ruler.h"
-
-enum
-{
-       PROP_0,
-       PROP_LOWER,
-       PROP_UPPER,
-       PROP_POSITION,
-       PROP_MAX_SIZE,
-       PROP_SHOW_POSITION
-};
-
-static void gtk_custom_ruler_class_init (GtkCustomRulerClass * klass);
-static void gtk_custom_ruler_init (GtkCustomRuler * ruler);
-static void gtk_custom_ruler_realize (GtkWidget * widget);
-static void gtk_custom_ruler_unrealize (GtkWidget * widget);
-static void gtk_custom_ruler_size_allocate (GtkWidget * widget, GtkAllocation * allocation);
-static gint gtk_custom_ruler_expose (GtkWidget * widget, GdkEventExpose * event);
-static void gtk_custom_ruler_make_pixmap (GtkCustomRuler * ruler);
-static void gtk_custom_ruler_set_property  (GObject        *object,
-                                           guint            prop_id,
-                                           const GValue   *value,
-                                           GParamSpec     *pspec);
-static void gtk_custom_ruler_get_property  (GObject        *object,
-                                           guint           prop_id,
-                                           GValue         *value,
-                                           GParamSpec     *pspec);
-
-
-static gint
-default_metric_get_marks (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
-{
-       (void) marks;
-       (void) lower;
-       (void) upper;
-       (void) maxchars;
-
-       return 0;
-}
-
-static const GtkCustomMetric default_metric = {
-       1.0,
-       default_metric_get_marks
-};
-
-static GtkWidgetClass *parent_class;
-
-GType gtk_custom_ruler_get_type (void)
-{
-       static GType ruler_type = 0;
-
-       if (!ruler_type)
-       {
-               static const GTypeInfo ruler_info =
-                       {
-                               sizeof (GtkCustomRulerClass),
-                               (GBaseInitFunc) NULL,           /* base_init */
-                               (GBaseFinalizeFunc) NULL,               /* base_finalize */
-                               (GClassInitFunc) gtk_custom_ruler_class_init,
-                               (GClassFinalizeFunc) NULL,              /* class_finalize */
-                               NULL,           /* class_data */
-                               sizeof (GtkCustomRuler),
-                               0,              /* n_preallocs */
-                               (GInstanceInitFunc) gtk_custom_ruler_init,
-                               NULL                    /* value_table */
-                       };
-
-               ruler_type = g_type_register_static (GTK_TYPE_WIDGET, "GtkCustomRuler",
-                                          &ruler_info, (GTypeFlags)0);
-       }
-
-       return ruler_type;
-}
-
-static void
-gtk_custom_ruler_class_init (GtkCustomRulerClass * class)
-{
-       GObjectClass   *gobject_class;
-       GtkWidgetClass *widget_class;
-
-       gobject_class = (GObjectClass *) class;
-       widget_class = (GtkWidgetClass*) class;
-
-       parent_class = g_type_class_peek_parent (class);
-
-       gobject_class->set_property = gtk_custom_ruler_set_property;
-       gobject_class->get_property = gtk_custom_ruler_get_property;
-
-       widget_class->realize = gtk_custom_ruler_realize;
-       widget_class->unrealize = gtk_custom_ruler_unrealize;
-       widget_class->size_allocate = gtk_custom_ruler_size_allocate;
-       widget_class->expose_event = gtk_custom_ruler_expose;
-
-       class->draw_ticks = NULL;
-       class->draw_pos = NULL;
-
-       g_object_class_install_property (gobject_class,
-                                        PROP_LOWER,
-                                        g_param_spec_double ("lower",
-                                                             _("Lower"),
-                                                             _("Lower limit of ruler"),
-                                                             -G_MAXDOUBLE,
-                                                             G_MAXDOUBLE,
-                                                             0.0,
-                                                             G_PARAM_READWRITE));
-
-       g_object_class_install_property (gobject_class,
-                                        PROP_UPPER,
-                                        g_param_spec_double ("upper",
-                                                             _("Upper"),
-                                                             _("Upper limit of ruler"),
-                                                             -G_MAXDOUBLE,
-                                                             G_MAXDOUBLE,
-                                                             0.0,
-                                                             G_PARAM_READWRITE));
-
-       g_object_class_install_property (gobject_class,
-                                        PROP_POSITION,
-                                        g_param_spec_double ("position",
-                                                             _("Position"),
-                                                             _("Position of mark on the ruler"),
-                                                             -G_MAXDOUBLE,
-                                                             G_MAXDOUBLE,
-                                                             0.0,
-                                                             G_PARAM_READWRITE));
-
-       g_object_class_install_property (gobject_class,
-                                        PROP_MAX_SIZE,
-                                        g_param_spec_double ("max_size",
-                                                             _("Max Size"),
-                                                             _("Maximum size of the ruler"),
-                                                             -G_MAXDOUBLE,
-                                                             G_MAXDOUBLE,
-                                                             0.0,
-                                                             G_PARAM_READWRITE));
-
-       g_object_class_install_property (gobject_class,
-                                        PROP_SHOW_POSITION,
-                                        g_param_spec_boolean ("show_position",
-                                                              _("Show Position"),
-                                                              _("Draw current ruler position"),
-                                                              TRUE,
-                                                              G_PARAM_READWRITE));
-}
-
-static void
-gtk_custom_ruler_init (GtkCustomRuler * ruler)
-{
-       ruler->backing_store = NULL;
-       ruler->non_gr_exp_gc = NULL;
-       ruler->xsrc = 0;
-       ruler->ysrc = 0;
-       ruler->slider_size = 0;
-       ruler->lower = 0;
-       ruler->upper = 0;
-       ruler->position = 0;
-       ruler->max_size = 0;
-       ruler->show_position = FALSE;
-
-       gtk_custom_ruler_set_metric (ruler, NULL);
-}
-
-static void
-gtk_custom_ruler_set_property (GObject      *object,
-                              guint         prop_id,
-                              const GValue *value,
-                              GParamSpec   *pspec)
-{
-  GtkCustomRuler *ruler = GTK_CUSTOM_RULER (object);
-  (void) pspec;
-
-  switch (prop_id)
-    {
-    case PROP_LOWER:
-      gtk_custom_ruler_set_range (ruler, g_value_get_double (value), ruler->upper,
-                          ruler->position, ruler->max_size);
-      break;
-    case PROP_UPPER:
-      gtk_custom_ruler_set_range (ruler, ruler->lower, g_value_get_double (value),
-                          ruler->position, ruler->max_size);
-      break;
-    case PROP_POSITION:
-      gtk_custom_ruler_set_range (ruler, ruler->lower, ruler->upper,
-                          g_value_get_double (value), ruler->max_size);
-      break;
-    case PROP_MAX_SIZE:
-      gtk_custom_ruler_set_range (ruler, ruler->lower, ruler->upper,
-                          ruler->position,  g_value_get_double (value));
-      break;
-    case PROP_SHOW_POSITION:
-      gtk_custom_ruler_set_show_position (ruler, g_value_get_boolean (value));
-      break;
-    }
-}
-
-static void
-gtk_custom_ruler_get_property (GObject      *object,
-                              guint         prop_id,
-                              GValue       *value,
-                              GParamSpec   *pspec)
-{
-  GtkCustomRuler *ruler = GTK_CUSTOM_RULER (object);
-
-  switch (prop_id)
-    {
-    case PROP_LOWER:
-      g_value_set_double (value, ruler->lower);
-      break;
-    case PROP_UPPER:
-      g_value_set_double (value, ruler->upper);
-      break;
-    case PROP_POSITION:
-      g_value_set_double (value, ruler->position);
-      break;
-    case PROP_MAX_SIZE:
-      g_value_set_double (value, ruler->max_size);
-      break;
-    case PROP_SHOW_POSITION:
-      g_value_set_boolean (value, ruler->show_position);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
-}
-
-void
-gtk_custom_ruler_set_metric (GtkCustomRuler * ruler, GtkCustomMetric * metric)
-{
-       g_return_if_fail (ruler != NULL);
-       g_return_if_fail (GTK_IS_CUSTOM_RULER (ruler));
-
-       if (metric == NULL)
-               ruler->metric = (GtkCustomMetric *) & default_metric;
-       else
-               ruler->metric = metric;
-
-       if (GTK_WIDGET_DRAWABLE (ruler))
-               gtk_widget_queue_draw (GTK_WIDGET (ruler));
-}
-
-void
-gtk_custom_ruler_set_range (GtkCustomRuler *ruler,
-                           gdouble   lower,
-                           gdouble   upper,
-                           gdouble   position,
-                           gdouble   max_size)
-{
-  g_return_if_fail (GTK_IS_CUSTOM_RULER (ruler));
-
-  g_object_freeze_notify (G_OBJECT (ruler));
-  if (ruler->lower != lower)
-    {
-      ruler->lower = lower;
-      g_object_notify (G_OBJECT (ruler), "lower");
-    }
-  if (ruler->upper != upper)
-    {
-      ruler->upper = upper;
-      g_object_notify (G_OBJECT (ruler), "upper");
-    }
-  if (ruler->position != position)
-    {
-      ruler->position = position;
-      g_object_notify (G_OBJECT (ruler), "position");
-    }
-  if (ruler->max_size != max_size)
-    {
-      ruler->max_size = max_size;
-      g_object_notify (G_OBJECT (ruler), "max-size");
-    }
-  g_object_thaw_notify (G_OBJECT (ruler));
-
-  if (GTK_WIDGET_DRAWABLE (ruler))
-    gtk_widget_queue_draw (GTK_WIDGET (ruler));
-}
-
-/**
- * gtk_custom_ruler_get_range:
- * @ruler: a #GtkCustomRuler
- * @lower: location to store lower limit of the ruler, or %NULL
- * @upper: location to store upper limit of the ruler, or %NULL
- * @position: location to store the current position of the mark on the ruler, or %NULL
- * @max_size: location to store the maximum size of the ruler used when calculating
- *            the space to leave for the text, or %NULL.
- *
- * Retrieves values indicating the range and current position of a #GtkCustomRuler.
- * See gtk_custom_ruler_set_range().
- **/
-void
-gtk_custom_ruler_get_range (GtkCustomRuler *ruler,
-                    gdouble  *lower,
-                    gdouble  *upper,
-                    gdouble  *position,
-                    gdouble  *max_size)
-{
-  g_return_if_fail (GTK_IS_CUSTOM_RULER (ruler));
-
-  if (lower)
-    *lower = ruler->lower;
-  if (upper)
-    *upper = ruler->upper;
-  if (position)
-    *position = ruler->position;
-  if (max_size)
-    *max_size = ruler->max_size;
-}
-
-void
-gtk_custom_ruler_draw_ticks (GtkCustomRuler * ruler)
-{
-        g_return_if_fail (GTK_IS_CUSTOM_RULER (ruler));
-
-        if (GTK_CUSTOM_RULER_GET_CLASS (ruler)->draw_ticks)
-                GTK_CUSTOM_RULER_GET_CLASS (ruler)->draw_ticks (ruler);
-
-}
-
-void
-gtk_custom_ruler_draw_pos (GtkCustomRuler * ruler)
-{
-        g_return_if_fail (GTK_IS_CUSTOM_RULER (ruler));
-
-        if (GTK_CUSTOM_RULER_GET_CLASS (ruler)->draw_pos && ruler->show_position)
-                GTK_CUSTOM_RULER_GET_CLASS (ruler)->draw_pos (ruler);
-}
-
-static void
-gtk_custom_ruler_realize (GtkWidget * widget)
-{
-       GtkCustomRuler *ruler;
-       GdkWindowAttr attributes;
-       gint attributes_mask;
-
-       g_return_if_fail (widget != NULL);
-       g_return_if_fail (GTK_IS_CUSTOM_RULER (widget));
-
-       ruler = GTK_CUSTOM_RULER (widget);
-       GTK_WIDGET_SET_FLAGS (ruler, GTK_REALIZED);
-
-       attributes.window_type = GDK_WINDOW_CHILD;
-       attributes.x = widget->allocation.x;
-       attributes.y = widget->allocation.y;
-       attributes.width = widget->allocation.width;
-       attributes.height = widget->allocation.height;
-       attributes.wclass = GDK_INPUT_OUTPUT;
-       attributes.visual = gtk_widget_get_visual (widget);
-       attributes.colormap = gtk_widget_get_colormap (widget);
-       attributes.event_mask = gtk_widget_get_events (widget);
-       attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
-
-       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
-
-       widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
-       gdk_window_set_user_data (widget->window, ruler);
-
-       widget->style = gtk_style_attach (widget->style, widget->window);
-       gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
-
-       gtk_custom_ruler_make_pixmap (ruler);
-}
-
-static void
-gtk_custom_ruler_unrealize (GtkWidget *widget)
-{
-  GtkCustomRuler *ruler = GTK_CUSTOM_RULER (widget);
-
-  if (ruler->backing_store)
-    g_object_unref (ruler->backing_store);
-  if (ruler->non_gr_exp_gc)
-    g_object_unref (ruler->non_gr_exp_gc);
-
-  ruler->backing_store = NULL;
-  ruler->non_gr_exp_gc = NULL;
-
-  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
-    (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
-}
-
-static void
-gtk_custom_ruler_size_allocate (GtkWidget     *widget,
-                        GtkAllocation *allocation)
-{
-  GtkCustomRuler *ruler = GTK_CUSTOM_RULER (widget);
-
-  widget->allocation = *allocation;
-
-  if (GTK_WIDGET_REALIZED (widget))
-    {
-      gdk_window_move_resize (widget->window,
-                             allocation->x, allocation->y,
-                             allocation->width, allocation->height);
-
-      gtk_custom_ruler_make_pixmap (ruler);
-    }
-}
-
-static gint
-gtk_custom_ruler_expose (GtkWidget * widget, GdkEventExpose * event)
-{
-       GtkCustomRuler *ruler;
-
-       g_return_val_if_fail (widget != NULL, FALSE);
-       g_return_val_if_fail (GTK_IS_CUSTOM_RULER (widget), FALSE);
-       g_return_val_if_fail (event != NULL, FALSE);
-
-       if (GTK_WIDGET_DRAWABLE (widget)) {
-               ruler = GTK_CUSTOM_RULER (widget);
-
-               gtk_custom_ruler_draw_ticks (ruler);
-
-               gdk_draw_drawable (widget->window,
-                                   ruler->non_gr_exp_gc,
-                                   GDK_DRAWABLE(ruler->backing_store), 0, 0, 0, 0, widget->allocation.width, widget->allocation.height);
-
-               gtk_custom_ruler_draw_pos (ruler);
-       }
-
-       return FALSE;
-}
-
-
-static void
-gtk_custom_ruler_make_pixmap (GtkCustomRuler *ruler)
-{
-  GtkWidget *widget;
-  gint width;
-  gint height;
-
-  widget = GTK_WIDGET (ruler);
-
-  if (ruler->backing_store)
-    {
-      gdk_drawable_get_size (ruler->backing_store, &width, &height);
-      if ((width == widget->allocation.width) &&
-         (height == widget->allocation.height))
-       return;
-
-      g_object_unref (ruler->backing_store);
-    }
-
-  ruler->backing_store = gdk_pixmap_new (widget->window,
-                                        widget->allocation.width,
-                                        widget->allocation.height,
-                                        -1);
-
-  ruler->xsrc = 0;
-  ruler->ysrc = 0;
-
-  if (!ruler->non_gr_exp_gc)
-    {
-      ruler->non_gr_exp_gc = gdk_gc_new (widget->window);
-      gdk_gc_set_exposures (ruler->non_gr_exp_gc, FALSE);
-    }
-}
-
-void
-gtk_custom_ruler_set_show_position (GtkCustomRuler * ruler, gboolean yn)
-{
-       ruler->show_position = yn;
-}
diff --git a/gtk2_ardour/gtk-custom-ruler.h b/gtk2_ardour/gtk-custom-ruler.h
deleted file mode 100644 (file)
index bcb3423..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-/* GTK - The GIMP Toolkit
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/*
- * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
- * file for a list of people on the GTK+ Team.  See the ChangeLog
- * files for a list of changes.  These files are distributed with
- * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/* modified by andreas meyer <hexx3000@gmx.de> */
-
-#ifndef __GTK_CUSTOM_RULER_H__
-#define __GTK_CUSTOM_RULER_H__
-
-#include <gdk/gdk.h>
-#include <gtk/gtkwidget.h>
-
-
-G_BEGIN_DECLS
-
-#define GTK_TYPE_CUSTOM_RULER            (gtk_custom_ruler_get_type ())
-#define GTK_CUSTOM_RULER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CUSTOM_RULER, GtkCustomRuler))
-#define GTK_CUSTOM_RULER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CUSTOM_RULER, GtkCustomRulerClass))
-#define GTK_IS_CUSTOM_RULER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CUSTOM_RULER))
-#define GTK_IS_CUSTOM_RULER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CUSTOM_RULER))
-#define GTK_CUSTOM_RULER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CUSTOM_RULER, GtkCustomRulerClass))
-
-typedef struct _GtkCustomRuler          GtkCustomRuler;
-typedef struct _GtkCustomRulerClass     GtkCustomRulerClass;
-typedef struct _GtkCustomMetric         GtkCustomMetric;
-typedef struct _GtkCustomRulerMark      GtkCustomRulerMark;
-
-struct _GtkCustomRuler {
-  GtkWidget widget;
-
-  GdkPixmap *backing_store;
-  GdkGC *non_gr_exp_gc;
-  GtkCustomMetric *metric;
-  gint xsrc, ysrc;
-  gint slider_size;
-  gboolean show_position;
-
-  /* The upper limit of the ruler (in points) */
-  gdouble lower;
-  /* The lower limit of the ruler */
-  gdouble upper;
-  /* The position of the mark on the ruler */
-  gdouble position;
-  /* The maximum size of the ruler */
-  gdouble max_size;
-};
-
-struct _GtkCustomRulerClass {
-  GtkWidgetClass parent_class;
-
-  void (* draw_ticks) (GtkCustomRuler *ruler);
-  void (* draw_pos)   (GtkCustomRuler *ruler);
-};
-
-typedef enum {
-   GtkCustomRulerMarkMajor,
-   GtkCustomRulerMarkMinor,
-   GtkCustomRulerMarkMicro
-} GtkCustomRulerMarkStyle;
-
-struct _GtkCustomRulerMark {
-  gchar                  *label;
-  gdouble                 position;
-  GtkCustomRulerMarkStyle style;
-};
-
-struct _GtkCustomMetric {
-  gfloat units_per_pixel;
-  gint (* get_marks) (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars);
-};
-
-GType   gtk_custom_ruler_get_type            (void);
-void    gtk_custom_ruler_set_metric          (GtkCustomRuler *ruler, GtkCustomMetric *metric);
-void    gtk_custom_ruler_set_range           (GtkCustomRuler *ruler,
-                                             gdouble lower,
-                                             gdouble upper,
-                                             gdouble position,
-                                             gdouble  max_size);
-void    gtk_custom_ruler_draw_ticks          (GtkCustomRuler *ruler);
-void    gtk_custom_ruler_draw_pos            (GtkCustomRuler *ruler);
-void    gtk_custom_ruler_set_show_position   (GtkCustomRuler *rule, gboolean yn);
-
-G_END_DECLS
-
-#endif /* __GTK_CUSTOM_RULER_H__ */
index 69dd8d5bc8be0397cf87b4e1abbc12bcd0ad0381..43dc8ce3474866a7b1b7a4ed6f6340bed65c2be9 100644 (file)
 
 #include "midi_region_view.h"
 #include "public_editor.h"
-#include "utils.h"
 #include "hit.h"
 
 using namespace ARDOUR;
 using namespace ArdourCanvas;
 
-Hit::Hit (MidiRegionView& region, Group* group, double size, const boost::shared_ptr<NoteType> note, bool with_events) 
+Hit::Hit (MidiRegionView& region, Item* parent, double size, const boost::shared_ptr<NoteType> note, bool with_events) 
        : NoteBase (region, with_events, note)
 {
-       _polygon = new ArdourCanvas::Polygon (group);
+       _polygon = new ArdourCanvas::Polygon (parent);
        CANVAS_DEBUG_NAME (_polygon, "note");
        set_item (_polygon);
        set_height (size);
index 0a02501606b0830f92749e9c1d790aea0cea14ab..5a6cef9943b686020b5b0edcc241dc04bf9ec81b 100644 (file)
@@ -33,7 +33,7 @@ public:
        typedef Evoral::Note<double> NoteType;
 
        Hit (MidiRegionView&                   region,
-            ArdourCanvas::Group*              group,
+            ArdourCanvas::Item*               parent,
             double                            size,
             const boost::shared_ptr<NoteType> note = boost::shared_ptr<NoteType>(),
             bool with_events = true);
diff --git a/gtk2_ardour/icons/anchored_trim_left_cursor.png b/gtk2_ardour/icons/anchored_trim_left_cursor.png
new file mode 100644 (file)
index 0000000..7f95351
Binary files /dev/null and b/gtk2_ardour/icons/anchored_trim_left_cursor.png differ
diff --git a/gtk2_ardour/icons/anchored_trim_right_cursor.png b/gtk2_ardour/icons/anchored_trim_right_cursor.png
new file mode 100644 (file)
index 0000000..c06ca71
Binary files /dev/null and b/gtk2_ardour/icons/anchored_trim_right_cursor.png differ
diff --git a/gtk2_ardour/icons/cursor_z/expand_left_right_cursor.png b/gtk2_ardour/icons/cursor_z/expand_left_right_cursor.png
new file mode 100755 (executable)
index 0000000..fb49ed5
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/expand_left_right_cursor.png differ
diff --git a/gtk2_ardour/icons/cursor_z/expand_up_down_cursor.png b/gtk2_ardour/icons/cursor_z/expand_up_down_cursor.png
new file mode 100755 (executable)
index 0000000..cb48028
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/expand_up_down_cursor.png differ
diff --git a/gtk2_ardour/icons/cursor_z/fade_in_cursor.png b/gtk2_ardour/icons/cursor_z/fade_in_cursor.png
new file mode 100755 (executable)
index 0000000..259e2d6
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/fade_in_cursor.png differ
diff --git a/gtk2_ardour/icons/cursor_z/fade_out_cursor.png b/gtk2_ardour/icons/cursor_z/fade_out_cursor.png
new file mode 100755 (executable)
index 0000000..24bef6b
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/fade_out_cursor.png differ
diff --git a/gtk2_ardour/icons/cursor_z/grabber.png b/gtk2_ardour/icons/cursor_z/grabber.png
new file mode 100755 (executable)
index 0000000..8f4795b
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/grabber.png differ
diff --git a/gtk2_ardour/icons/cursor_z/grabber_edit_point.png b/gtk2_ardour/icons/cursor_z/grabber_edit_point.png
new file mode 100755 (executable)
index 0000000..8a3959a
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/grabber_edit_point.png differ
diff --git a/gtk2_ardour/icons/cursor_z/grabber_note.png b/gtk2_ardour/icons/cursor_z/grabber_note.png
new file mode 100755 (executable)
index 0000000..29a730a
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/grabber_note.png differ
diff --git a/gtk2_ardour/icons/cursor_z/hide.png b/gtk2_ardour/icons/cursor_z/hide.png
new file mode 100755 (executable)
index 0000000..635f32f
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/hide.png differ
diff --git a/gtk2_ardour/icons/cursor_z/i_beam_cursor.png b/gtk2_ardour/icons/cursor_z/i_beam_cursor.png
new file mode 100755 (executable)
index 0000000..e7616dc
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/i_beam_cursor.png differ
diff --git a/gtk2_ardour/icons/cursor_z/move_cursor.png b/gtk2_ardour/icons/cursor_z/move_cursor.png
new file mode 100755 (executable)
index 0000000..5fec60a
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/move_cursor.png differ
diff --git a/gtk2_ardour/icons/cursor_z/resize_bottom_cursor.png b/gtk2_ardour/icons/cursor_z/resize_bottom_cursor.png
new file mode 100755 (executable)
index 0000000..f521550
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/resize_bottom_cursor.png differ
diff --git a/gtk2_ardour/icons/cursor_z/resize_bottom_left_cursor.png b/gtk2_ardour/icons/cursor_z/resize_bottom_left_cursor.png
new file mode 100755 (executable)
index 0000000..86c6c10
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/resize_bottom_left_cursor.png differ
diff --git a/gtk2_ardour/icons/cursor_z/resize_bottom_right_cursor.png b/gtk2_ardour/icons/cursor_z/resize_bottom_right_cursor.png
new file mode 100755 (executable)
index 0000000..1a08b59
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/resize_bottom_right_cursor.png differ
diff --git a/gtk2_ardour/icons/cursor_z/resize_left_cursor.png b/gtk2_ardour/icons/cursor_z/resize_left_cursor.png
new file mode 100755 (executable)
index 0000000..f183e5d
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/resize_left_cursor.png differ
diff --git a/gtk2_ardour/icons/cursor_z/resize_right_cursor.png b/gtk2_ardour/icons/cursor_z/resize_right_cursor.png
new file mode 100755 (executable)
index 0000000..9521fa5
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/resize_right_cursor.png differ
diff --git a/gtk2_ardour/icons/cursor_z/resize_top_cursor.png b/gtk2_ardour/icons/cursor_z/resize_top_cursor.png
new file mode 100755 (executable)
index 0000000..6111800
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/resize_top_cursor.png differ
diff --git a/gtk2_ardour/icons/cursor_z/resize_top_left_cursor.png b/gtk2_ardour/icons/cursor_z/resize_top_left_cursor.png
new file mode 100755 (executable)
index 0000000..6c99c4e
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/resize_top_left_cursor.png differ
diff --git a/gtk2_ardour/icons/cursor_z/resize_top_right_cursor.png b/gtk2_ardour/icons/cursor_z/resize_top_right_cursor.png
new file mode 100755 (executable)
index 0000000..b19928d
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/resize_top_right_cursor.png differ
diff --git a/gtk2_ardour/icons/cursor_z/trim_bottom_cursor.png b/gtk2_ardour/icons/cursor_z/trim_bottom_cursor.png
new file mode 100755 (executable)
index 0000000..53b2671
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/trim_bottom_cursor.png differ
diff --git a/gtk2_ardour/icons/cursor_z/trim_left_cursor.png b/gtk2_ardour/icons/cursor_z/trim_left_cursor.png
new file mode 100755 (executable)
index 0000000..432ad90
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/trim_left_cursor.png differ
diff --git a/gtk2_ardour/icons/cursor_z/trim_left_cursor_5.png b/gtk2_ardour/icons/cursor_z/trim_left_cursor_5.png
new file mode 100755 (executable)
index 0000000..d0d3d24
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/trim_left_cursor_5.png differ
diff --git a/gtk2_ardour/icons/cursor_z/trim_left_cursor_right_only.png b/gtk2_ardour/icons/cursor_z/trim_left_cursor_right_only.png
new file mode 100755 (executable)
index 0000000..b0d4b82
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/trim_left_cursor_right_only.png differ
diff --git a/gtk2_ardour/icons/cursor_z/trim_right_cursor.png b/gtk2_ardour/icons/cursor_z/trim_right_cursor.png
new file mode 100755 (executable)
index 0000000..23fd432
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/trim_right_cursor.png differ
diff --git a/gtk2_ardour/icons/cursor_z/trim_right_cursor_5.png b/gtk2_ardour/icons/cursor_z/trim_right_cursor_5.png
new file mode 100755 (executable)
index 0000000..e178374
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/trim_right_cursor_5.png differ
diff --git a/gtk2_ardour/icons/cursor_z/trim_right_cursor_left_only.png b/gtk2_ardour/icons/cursor_z/trim_right_cursor_left_only.png
new file mode 100755 (executable)
index 0000000..6e1300a
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/trim_right_cursor_left_only.png differ
diff --git a/gtk2_ardour/icons/cursor_z/trim_top_cursor.png b/gtk2_ardour/icons/cursor_z/trim_top_cursor.png
new file mode 100755 (executable)
index 0000000..db4a036
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/trim_top_cursor.png differ
diff --git a/gtk2_ardour/icons/cursor_z/zoom_in_cursor.png b/gtk2_ardour/icons/cursor_z/zoom_in_cursor.png
new file mode 100755 (executable)
index 0000000..e0097c0
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/zoom_in_cursor.png differ
diff --git a/gtk2_ardour/icons/cursor_z/zoom_out_cursor.png b/gtk2_ardour/icons/cursor_z/zoom_out_cursor.png
new file mode 100755 (executable)
index 0000000..95e5e4b
Binary files /dev/null and b/gtk2_ardour/icons/cursor_z/zoom_out_cursor.png differ
index 13a0a05b7b6a2ca9df34447e5d2748d24cc22f17..3278c434a2668d7cfe12ef717c2b26410d24d22b 100644 (file)
Binary files a/gtk2_ardour/icons/fadein-constant-power.png and b/gtk2_ardour/icons/fadein-constant-power.png differ
index 3565ee26b3cc99a20a5e5238c7a178b6f4270a16..0ddf5883b090c1541e6e25bcb0cc4d807038a8e2 100644 (file)
Binary files a/gtk2_ardour/icons/fadein-fast-cut.png and b/gtk2_ardour/icons/fadein-fast-cut.png differ
index 7b8980aca721d555dfd8cb9c6ad853ccec857e41..87dbf6cc56e0127b78c98446f6ab02a015ed76b3 100644 (file)
Binary files a/gtk2_ardour/icons/fadein-linear.png and b/gtk2_ardour/icons/fadein-linear.png differ
index 8080e3a9bbf68fb6ce5a44e2d7e00737fb0b1c80..76c27008919cae75e85f8ec28e7c6ee6a00c9cc8 100644 (file)
Binary files a/gtk2_ardour/icons/fadein-slow-cut.png and b/gtk2_ardour/icons/fadein-slow-cut.png differ
diff --git a/gtk2_ardour/icons/fadein-symmetric.png b/gtk2_ardour/icons/fadein-symmetric.png
new file mode 100644 (file)
index 0000000..eff3b69
Binary files /dev/null and b/gtk2_ardour/icons/fadein-symmetric.png differ
index 359f37096c2c8915f622f8701d5054a276912919..786ec853914a33181831b4b84deeb8a1b2cb94fe 100644 (file)
Binary files a/gtk2_ardour/icons/fadeout-constant-power.png and b/gtk2_ardour/icons/fadeout-constant-power.png differ
index 39d00c0916809a4bd9a522bb0beb3f0703942837..f0d2b693eca86aa716141f6391e198991b390873 100644 (file)
Binary files a/gtk2_ardour/icons/fadeout-fast-cut.png and b/gtk2_ardour/icons/fadeout-fast-cut.png differ
index a9fb6a345f1140b59b98b1256792993f64a797d4..71fea91a557a8b78831d05d635fb59c8bd4971a0 100644 (file)
Binary files a/gtk2_ardour/icons/fadeout-linear.png and b/gtk2_ardour/icons/fadeout-linear.png differ
index b740ad2c2215d3c5a6a0ae9f6df9aba2d4c186dc..8e30356ad3ea86cbb377178ac7401fbae74bdd07 100644 (file)
Binary files a/gtk2_ardour/icons/fadeout-slow-cut.png and b/gtk2_ardour/icons/fadeout-slow-cut.png differ
diff --git a/gtk2_ardour/icons/fadeout-symmetric.png b/gtk2_ardour/icons/fadeout-symmetric.png
new file mode 100644 (file)
index 0000000..c3ccc14
Binary files /dev/null and b/gtk2_ardour/icons/fadeout-symmetric.png differ
diff --git a/gtk2_ardour/icons/padlock_closed.png b/gtk2_ardour/icons/padlock_closed.png
new file mode 100644 (file)
index 0000000..903f231
Binary files /dev/null and b/gtk2_ardour/icons/padlock_closed.png differ
diff --git a/gtk2_ardour/icons/padlock_open.png b/gtk2_ardour/icons/padlock_open.png
new file mode 100644 (file)
index 0000000..9076aab
Binary files /dev/null and b/gtk2_ardour/icons/padlock_open.png differ
diff --git a/gtk2_ardour/icons/soundcloud.png b/gtk2_ardour/icons/soundcloud.png
new file mode 100644 (file)
index 0000000..39c50fe
Binary files /dev/null and b/gtk2_ardour/icons/soundcloud.png differ
index 31f8cfa1cb70d251bbfffda7685addc04c734f0d..270ed8ce32bfd75368c60b2250ee3ee92d85c62b 100644 (file)
@@ -36,6 +36,7 @@
 #include "i18n.h"
 
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace Gtk;
 
 IOSelector::IOSelector (Gtk::Window* p, ARDOUR::Session* session, boost::shared_ptr<ARDOUR::IO> io)
index 98ffab1160bb563fa2a2f5abf868062f6d10e281..3b5b207ce2b8923add6fd83bf9a096352704c46c 100644 (file)
@@ -123,7 +123,7 @@ ArdourKeyboard::setup_keybindings ()
                        /* not absolute - look in the usual places */
                        std::string keybindings_file;
 
-                       if ( ! find_file_in_search_path (ardour_config_search_path(), keybindings_path, keybindings_file)) {
+                       if ( ! find_file (ardour_config_search_path(), keybindings_path, keybindings_file)) {
 
                                if (keybindings_path == default_bindings) {
                                        error << string_compose (_("Default keybindings not found - %1 will be hard to use!"), PROGRAM_NAME) << endmsg;
index f23e24695888cb937f51fa5c663747c2b382478d..409f89dfaf57411a267f995443015a78b7e29b83 100644 (file)
@@ -38,7 +38,6 @@
 #include "actions.h"
 #include "keyboard.h"
 #include "keyeditor.h"
-#include "utils.h"
 
 #include "i18n.h"
 
index 94b8d63d7f3290f7cf6c622815d684ac614209c0..1a6f59f1b04f9347dc0362e591a414f4ef8c75c6 100644 (file)
@@ -37,6 +37,7 @@
 #include "i18n.h"
 
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtkmm2ext;
 using namespace Gtk;
index 469246f5170f38724c6181e1e1d8237451c6e873..5eaf485c7569587ff45bce90c1d9d70828e33282 100644 (file)
@@ -336,9 +336,9 @@ void* gui_event_loop (void* ptr)
        VSTState* vstfx;
        int LXVST_sched_timer_interval = 40; //ms, 25fps
        XEvent event;
-       struct timeval clock1, clock2;
+       uint64_t clock1, clock2;
        
-       gettimeofday(&clock1, NULL);
+       clock1 = g_get_monotonic_time();
        /*The 'Forever' loop - runs the plugin UIs etc - based on the FST gui event loop*/
        
        while (!gui_quit)
@@ -382,12 +382,12 @@ void* gui_event_loop (void* ptr)
                
                /*See if its time for us to do a scheduled event pass on all the plugins*/
 
-               gettimeofday(&clock2, NULL);
-               const int elapsed_time = (clock2.tv_sec - clock1.tv_sec) * 1000 + (clock2.tv_usec - clock1.tv_usec) / 1000;
+               clock2 = g_get_monotonic_time();
+               const int64_t elapsed_time_ms = (clock2 - clock1) / 1000;
 
-               if((LXVST_sched_timer_interval != 0) && elapsed_time >= LXVST_sched_timer_interval)
+               if((LXVST_sched_timer_interval != 0) && elapsed_time_ms >= LXVST_sched_timer_interval)
                {
-                       //printf("elapsed %d ms ^= %.2f Hz\n", elapsed_time, 1000.0/(double)elapsed_time); // DEBUG
+                       //printf("elapsed %d ms ^= %.2f Hz\n", elapsed_time_ms, 1000.0/(double)elapsed_time_ms); // DEBUG
                        pthread_mutex_lock (&plugin_mutex);
                    
 again:
@@ -463,7 +463,7 @@ again:
                        }
                        pthread_mutex_unlock (&plugin_mutex);
 
-                       gettimeofday(&clock1, NULL);
+                       clock1 = g_get_monotonic_time();
                }
        }
 
index 92f11da019cda195170a4cbbd19775b73a1230b5..f4a9b7ea07f980d6b341cebf468779126b699b37 100644 (file)
@@ -38,6 +38,7 @@
 
 using namespace std;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtk;
 using namespace Gtkmm2ext;
@@ -163,6 +164,7 @@ LocationEditRow::LocationEditRow(Session * sess, Location * loc, int32_t num)
 
          set_location (loc);
          set_number (num);
+         cd_toggled(); // show/hide cd-track details
  }
 
  LocationEditRow::~LocationEditRow()
@@ -499,7 +501,7 @@ LocationEditRow::cd_toggled ()
 
        location->set_cd (cd_check_button.get_active(), this);
 
-       if (location->is_cd_marker() && !(location->is_mark())) {
+       if (location->is_cd_marker()) {
 
                show_cd_track_details ();
 
index 5dd3695dc9036b9fdb0209c49b873b61b60e3cc2..d924d9b30a6f55243a2e885b47f0830cb7879956 100644 (file)
@@ -49,7 +49,6 @@
 #include <gtkmm2ext/utils.h>
 
 #include "version.h"
-#include "utils.h"
 #include "ardour_ui.h"
 #include "opts.h"
 #include "enums.h"
index 0242ab98b71689c9a21557a90a071f48d0f72f36..af201286a9bd1f53997478ca92982a94ac33a781 100644 (file)
 #include "ardour/tempo.h"
 
 #include "canvas/rectangle.h"
-#include "canvas/group.h"
+#include "canvas/container.h"
 #include "canvas/line.h"
 #include "canvas/polygon.h"
 #include "canvas/text.h"
 #include "canvas/canvas.h"
+#include "canvas/scroll_group.h"
 #include "canvas/debug.h"
 
 #include "ardour_ui.h"
 
 using namespace std;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace Gtkmm2ext;
 
 PBD::Signal1<void,Marker*> Marker::CatchDeletion;
 
 static const double marker_height = 13.0;
 
-Marker::Marker (PublicEditor& ed, ArdourCanvas::Group& parent, guint32 rgba, const string& annotation,
+Marker::Marker (PublicEditor& ed, ArdourCanvas::Container& parent, guint32 rgba, const string& annotation,
                Type type, framepos_t frame, bool handle_events)
 
        : editor (ed)
        , _parent (&parent)
-       , _time_bars_line (0)
        , _track_canvas_line (0)
        , _type (type)
        , _selected (false)
@@ -242,7 +243,7 @@ Marker::Marker (PublicEditor& ed, ArdourCanvas::Group& parent, guint32 rgba, con
        unit_position = editor.sample_to_pixel (frame);
        unit_position -= _shift;
 
-       group = new ArdourCanvas::Group (&parent, ArdourCanvas::Duple (unit_position, 0));
+       group = new ArdourCanvas::Container (&parent, ArdourCanvas::Duple (unit_position, 0));
 #ifdef CANVAS_DEBUG
        group->name = string_compose ("Marker::group for %1", annotation);
 #endif 
@@ -299,11 +300,10 @@ Marker::~Marker ()
 
        /* destroying the parent group destroys its contents, namely any polygons etc. that we added */
        delete group;
-       delete _time_bars_line;
        delete _track_canvas_line;
 }
 
-void Marker::reparent(ArdourCanvas::Group & parent)
+void Marker::reparent(ArdourCanvas::Container & parent)
 {
        group->reparent (&parent);
        _parent = &parent;
@@ -328,29 +328,16 @@ Marker::setup_line ()
 {
        if (_shown && (_selected || _line_shown)) {
 
-               if (_time_bars_line == 0) {
+               if (_track_canvas_line == 0) {
 
-                       _time_bars_line = new ArdourCanvas::Line (editor.get_time_bars_group());
-                       _time_bars_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EditPoint());
-                       _time_bars_line->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_marker_event), group, this));
-                       
-                       _track_canvas_line = new ArdourCanvas::Line (editor.get_track_canvas_group());
+                       _track_canvas_line = new ArdourCanvas::Line (editor.get_hscroll_group());
                        _track_canvas_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EditPoint());
                        _track_canvas_line->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_marker_event), group, this));
                }
 
-               ArdourCanvas::Duple g = group->item_to_canvas (ArdourCanvas::Duple (0, 0));
-               ArdourCanvas::Duple d = _time_bars_line->canvas_to_item (ArdourCanvas::Duple (g.x + _shift, 0));
-
-                _time_bars_line->set_x0 (d.x);
-                _time_bars_line->set_x1 (d.x);
-               _time_bars_line->set_y0 (d.y);
-               _time_bars_line->set_y1 (ArdourCanvas::COORD_MAX);
-               _time_bars_line->set_outline_color (_selected ? ARDOUR_UI::config()->get_canvasvar_EditPoint() : _color);
-               _time_bars_line->raise_to_top ();
-               _time_bars_line->show ();
+               ArdourCanvas::Duple g = group->canvas_origin();
+               ArdourCanvas::Duple d = _track_canvas_line->canvas_to_item (ArdourCanvas::Duple (g.x + _shift, 0));
 
-                d = _track_canvas_line->canvas_to_item (ArdourCanvas::Duple (g.x + _shift, 0));
                _track_canvas_line->set_x0 (d.x);
                _track_canvas_line->set_x1 (d.x);
                _track_canvas_line->set_y0 (d.y);
@@ -360,8 +347,7 @@ Marker::setup_line ()
                _track_canvas_line->show ();
 
        } else {
-               if (_time_bars_line) {
-                       _time_bars_line->hide ();
+               if (_track_canvas_line) {
                        _track_canvas_line->hide ();
                }
        }
@@ -475,8 +461,7 @@ Marker::set_color_rgba (uint32_t c)
        mark->set_fill_color (_color);
        mark->set_outline_color (_color);
 
-       if (_time_bars_line && !_selected) {
-               _time_bars_line->set_outline_color (_color);
+       if (_track_canvas_line && !_selected) {
                _track_canvas_line->set_outline_color (_color);
        }
 
@@ -517,7 +502,7 @@ Marker::set_right_label_limit (double p)
 
 /***********************************************************************/
 
-TempoMarker::TempoMarker (PublicEditor& editor, ArdourCanvas::Group& parent, guint32 rgba, const string& text,
+TempoMarker::TempoMarker (PublicEditor& editor, ArdourCanvas::Container& parent, guint32 rgba, const string& text,
                          ARDOUR::TempoSection& temp)
        : Marker (editor, parent, rgba, text, Tempo, 0, false),
          _tempo (temp)
@@ -532,7 +517,7 @@ TempoMarker::~TempoMarker ()
 
 /***********************************************************************/
 
-MeterMarker::MeterMarker (PublicEditor& editor, ArdourCanvas::Group& parent, guint32 rgba, const string& text,
+MeterMarker::MeterMarker (PublicEditor& editor, ArdourCanvas::Container& parent, guint32 rgba, const string& text,
                          ARDOUR::MeterSection& m)
        : Marker (editor, parent, rgba, text, Meter, 0, false),
          _meter (m)
index b3a85294ea20e4cf56a4e5223ca1d9d36561d2c8..3a3339d31881370d33a90495a2f590a37b6e5a22 100644 (file)
@@ -56,7 +56,7 @@ class Marker : public sigc::trackable
        };
 
 
-       Marker (PublicEditor& editor, ArdourCanvas::Group &, guint32 rgba, const std::string& text, Type,
+       Marker (PublicEditor& editor, ArdourCanvas::Container &, guint32 rgba, const std::string& text, Type,
                framepos_t frame = 0, bool handle_events = true);
 
        virtual ~Marker ();
@@ -76,8 +76,8 @@ class Marker : public sigc::trackable
 
        framepos_t position() const { return frame_position; }
 
-       ArdourCanvas::Group * get_parent() { return _parent; }
-       void reparent (ArdourCanvas::Group & parent);
+       ArdourCanvas::Container * get_parent() { return _parent; }
+       void reparent (ArdourCanvas::Container & parent);
 
        void hide ();
        void show ();
@@ -98,12 +98,11 @@ class Marker : public sigc::trackable
 
        Pango::FontDescription name_font;
 
-       ArdourCanvas::Group* _parent;
-       ArdourCanvas::Group *group;
+       ArdourCanvas::Container* _parent;
+       ArdourCanvas::Container *group;
        ArdourCanvas::Polygon *mark;
         ArdourCanvas::Text *_name_item;
        ArdourCanvas::Points *points;
-       ArdourCanvas::Line* _time_bars_line;
        ArdourCanvas::Line* _track_canvas_line;
        ArdourCanvas::Rectangle* _name_background;
 
@@ -135,7 +134,7 @@ private:
 class TempoMarker : public Marker
 {
   public:
-        TempoMarker (PublicEditor& editor, ArdourCanvas::Group &, guint32 rgba, const std::string& text, ARDOUR::TempoSection&);
+        TempoMarker (PublicEditor& editor, ArdourCanvas::Container &, guint32 rgba, const std::string& text, ARDOUR::TempoSection&);
        ~TempoMarker ();
 
        ARDOUR::TempoSection& tempo() const { return _tempo; }
@@ -147,7 +146,7 @@ class TempoMarker : public Marker
 class MeterMarker : public Marker
 {
   public:
-        MeterMarker (PublicEditor& editor, ArdourCanvas::Group &, guint32 rgba, const std::string& text, ARDOUR::MeterSection&);
+        MeterMarker (PublicEditor& editor, ArdourCanvas::Container &, guint32 rgba, const std::string& text, ARDOUR::MeterSection&);
        ~MeterMarker ();
 
        ARDOUR::MeterSection& meter() const { return _meter; }
index 2fa1d7356577fa6d174cc7f4c5ba6fe64557e1d7..55eb16246a532e5fcc2e4d66de8614827adceee6 100644 (file)
@@ -31,6 +31,7 @@
 #include "i18n.h"
 
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtk;
 using namespace Gtkmm2ext;
@@ -613,7 +614,7 @@ meter_render_metrics (Gtk::Widget& w, MeterType type, vector<DataType> types)
        Pango::AttrFontDesc* font_attr;
        Pango::FontDescription font;
 
-       font = Pango::FontDescription ("ArdourMono");
+       font = Pango::FontDescription (ARDOUR_UI::config()->get_canvasvar_SmallMonospaceFont());
        double fixfontsize = 81920.0 / (double) ARDOUR::Config->get_font_scale();
 
        font.set_weight (Pango::WEIGHT_NORMAL);
index 632c6ef82c808fde5a1aa9af2c3e64f56d5dd545..fd38da65e6b606a32d769658b5eb9903f7f662ee 100644 (file)
@@ -48,6 +48,7 @@
 #include "i18n.h"
 
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtk;
 using namespace Gtkmm2ext;
@@ -114,14 +115,15 @@ MeterStrip::MeterStrip (int metricmode, MeterType mt)
 
 MeterStrip::MeterStrip (Session* sess, boost::shared_ptr<ARDOUR::Route> rt)
        : AxisView(sess)
-       , RouteUI(sess)
+       , RouteUI(0)
        , _route(rt)
        , peak_display()
 {
        mtr_vbox.set_spacing(2);
        nfo_vbox.set_spacing(2);
-       RouteUI::set_route (rt);
        SessionHandlePtr::set_session (sess);
+       RouteUI::init ();
+       RouteUI::set_route (rt);
 
        _has_midi = false;
        _tick_bar = 0;
@@ -162,8 +164,12 @@ MeterStrip::MeterStrip (Session* sess, boost::shared_ptr<ARDOUR::Route> rt)
        peakbx.pack_start(peak_align, true, true, 3);
        peakbx.set_size_request(-1, 14);
 
-       // add track-name label
-       name_label.set_text(_route->name());
+       // add track-name & -number label
+       number_label.set_text("-");
+       number_label.set_size_request(18, 18);
+
+       name_changed();
+
        name_label.set_corner_radius(2);
        name_label.set_name("meterbridge label");
        name_label.set_angle(-90.0);
@@ -174,8 +180,16 @@ MeterStrip::MeterStrip (Session* sess, boost::shared_ptr<ARDOUR::Route> rt)
        ARDOUR_UI::instance()->set_tip (name_label, _route->name());
        ARDOUR_UI::instance()->set_tip (*level_meter, _route->name());
 
+       number_label.set_corner_radius(2);
+       number_label.set_name("tracknumber label");
+       number_label.set_angle(-90.0);
+       number_label.layout()->set_width(18 * PANGO_SCALE);
+       number_label.set_alignment(.5, .5);
+
        namebx.set_size_request(18, 52);
-       namebx.pack_start(name_label, true, false, 3);
+       namebx.pack_start(namenumberbx, true, false, 0);
+       namenumberbx.pack_start(name_label, true, false, 0);
+       namenumberbx.pack_start(number_label, false, false, 0);
 
        mon_in_box.pack_start(*monitor_input_button, true, false);
        btnbox.pack_start(mon_in_box, false, false, 1);
@@ -191,6 +205,7 @@ MeterStrip::MeterStrip (Session* sess, boost::shared_ptr<ARDOUR::Route> rt)
 
        rec_enable_button->set_corner_radius(2);
        rec_enable_button->set_size_request(16, 16);
+       rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
 
        mute_button->set_corner_radius(2);
        mute_button->set_size_request(16, 16);
@@ -245,11 +260,18 @@ MeterStrip::MeterStrip (Session* sess, boost::shared_ptr<ARDOUR::Route> rt)
        mtr_container.show();
        mtr_hsep.show();
        nfo_vbox.show();
-       monitor_input_button->show();
-       monitor_disk_button->show();
+       namenumberbx.show();
+
+       if (boost::dynamic_pointer_cast<Track>(_route)) {
+               monitor_input_button->show();
+               monitor_disk_button->show();
+       } else {
+               monitor_input_button->hide();
+               monitor_disk_button->hide();
+       }
 
        _route->shared_peak_meter()->ConfigurationChanged.connect (
-                       route_connections, invalidator (*this), boost::bind (&MeterStrip::meter_configuration_changed, this, _1), gui_context()
+                       meter_route_connections, invalidator (*this), boost::bind (&MeterStrip::meter_configuration_changed, this, _1), gui_context()
                        );
 
        ResetAllPeakDisplays.connect (sigc::mem_fun(*this, &MeterStrip::reset_peak_display));
@@ -265,8 +287,8 @@ MeterStrip::MeterStrip (Session* sess, boost::shared_ptr<ARDOUR::Route> rt)
        meter_ticks1_area.signal_expose_event().connect (sigc::mem_fun(*this, &MeterStrip::meter_ticks1_expose));
        meter_ticks2_area.signal_expose_event().connect (sigc::mem_fun(*this, &MeterStrip::meter_ticks2_expose));
 
-       _route->DropReferences.connect (route_connections, invalidator (*this), boost::bind (&MeterStrip::self_delete, this), gui_context());
-       _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MeterStrip::strip_property_changed, this, _1), gui_context());
+       _route->DropReferences.connect (meter_route_connections, invalidator (*this), boost::bind (&MeterStrip::self_delete, this), gui_context());
+       _route->PropertyChanged.connect (meter_route_connections, invalidator (*this), boost::bind (&MeterStrip::strip_property_changed, this, _1), gui_context());
 
        peak_display.signal_button_release_event().connect (sigc::mem_fun(*this, &MeterStrip::peak_button_release), false);
        name_label.signal_button_release_event().connect (sigc::mem_fun(*this, &MeterStrip::name_label_button_release), false);
@@ -334,7 +356,6 @@ MeterStrip::set_button_names()
 {
        mute_button->set_text (_("M"));
        rec_enable_button->set_text ("");
-       rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
 
        if (_route && _route->solo_safe()) {
                solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
@@ -364,8 +385,8 @@ MeterStrip::strip_property_changed (const PropertyChange& what_changed)
        if (!what_changed.contains (ARDOUR::Properties::name)) {
                return;
        }
-       ENSURE_GUI_THREAD (*this, &MeterStrip::strip_name_changed, what_changed)
-       name_label.set_text(_route->name());
+       ENSURE_GUI_THREAD (*this, &MeterStrip::strip_name_changed, what_changed);
+       name_changed();
        ARDOUR_UI::instance()->set_tip (name_label, _route->name());
        if (level_meter) {
                ARDOUR_UI::instance()->set_tip (*level_meter, _route->name());
@@ -503,10 +524,15 @@ MeterStrip::on_size_allocate (Gtk::Allocation& a)
                        nh = 148;
                        break;
        }
-       namebx.set_size_request(18, nh);
+       int tnh = 0;
+       if (_session && _session->config.get_track_name_number()) {
+               tnh = 4 + _session->track_number_decimals() * 8;
+       }
+       namebx.set_size_request(18, nh + tnh);
+       namenumberbx.set_size_request(18, nh + tnh);
        if (_route) {
-               name_label.set_size_request(18, nh-2);
-               name_label.layout()->set_width((nh-4) * PANGO_SCALE);
+               name_label.set_size_request(18, nh + (_route->is_master() ? tnh : -1));
+               name_label.layout()->set_width((nh - 4 + (_route->is_master() ? tnh : 0)) * PANGO_SCALE);
        }
        VBox::on_size_allocate(a);
 }
@@ -716,6 +742,31 @@ MeterStrip::parameter_changed (std::string const & p)
        else if (p == "meterbridge-label-height") {
                queue_resize();
        }
+       else if (p == "track-name-number") {
+               name_changed();
+               queue_resize();
+       }
+}
+
+void
+MeterStrip::name_changed () {
+       if (!_route) {
+               return;
+       }
+       name_label.set_text(_route->name ());
+       if (_session && _session->config.get_track_name_number()) {
+               const int64_t track_number = _route->track_number ();
+               if (track_number == 0) {
+                       number_label.set_text("-");
+                       number_label.hide();
+               } else {
+                       number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
+                       number_label.show();
+               }
+               number_label.set_size_request(18, 4 + _session->track_number_decimals() * 8);
+       } else {
+               number_label.hide();
+       }
 }
 
 bool
index e1bae713265059c78bdb16df7a75c9dab76b52e7..0ccf21d60cf9f9c14f660865fea13abfa0364453 100644 (file)
@@ -74,7 +74,7 @@ class MeterStrip : public Gtk::VBox, public RouteUI
 
   protected:
        boost::shared_ptr<ARDOUR::Route> _route;
-       PBD::ScopedConnectionList route_connections;
+       PBD::ScopedConnectionList meter_route_connections;
        PBD::ScopedConnectionList level_meter_connection;
        void self_delete ();
 
@@ -100,7 +100,9 @@ class MeterStrip : public Gtk::VBox, public RouteUI
        Gtk::HBox meterbox;
        Gtk::HBox spacer;
        Gtk::HBox namebx;
+       Gtk::VBox namenumberbx;
        ArdourButton name_label;
+       ArdourButton number_label;
        Gtk::DrawingArea meter_metric_area;
        Gtk::DrawingArea meter_ticks1_area;
        Gtk::DrawingArea meter_ticks2_area;
@@ -128,7 +130,6 @@ class MeterStrip : public Gtk::VBox, public RouteUI
 
        LevelMeterHBox *level_meter;
 
-       PBD::ScopedConnection _config_connection;
        void strip_property_changed (const PBD::PropertyChange&);
        void meter_configuration_changed (ARDOUR::ChanCount);
        void meter_type_changed (ARDOUR::MeterType);
@@ -140,6 +141,7 @@ class MeterStrip : public Gtk::VBox, public RouteUI
        void redraw_metrics ();
        void update_button_box ();
        void update_name_box ();
+       void name_changed ();
 
        bool _suspend_menu_callbacks;
        bool level_meter_button_release (GdkEventButton* ev);
index 4d2a0c7b3b0e8c4861455944e3ae6ea80443ac49..f89d420b613f52c13b8be20f573cc773612c3629 100644 (file)
@@ -40,6 +40,7 @@
 
 #include "ardour/audio_track.h"
 #include "ardour/midi_track.h"
+#include "ardour/route_sorters.h"
 
 #include "meterbridge.h"
 
@@ -57,6 +58,7 @@
 #include "i18n.h"
 
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtk;
 using namespace Glib;
@@ -78,24 +80,6 @@ Meterbridge::instance ()
        return _instance;
 }
 
-/* copy from gtk2_ardour/mixer_ui.cc -- TODO consolidate
- * used by Meterbridge::set_session() below
- */
-struct SignalOrderRouteSorter {
-       bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
-               if (a->is_master() || a->is_monitor()) {
-                       /* "a" is a special route (master, monitor, etc), and comes
-                        * last in the mixer ordering
-                        */
-                       return false;
-               } else if (b->is_master() || b->is_monitor()) {
-                       /* everything comes before b */
-                       return true;
-               }
-               return a->order_key () < b->order_key ();
-       }
-};
-
 Meterbridge::Meterbridge ()
        : Window (Gtk::WINDOW_TOPLEVEL)
        , VisibilityTracker (*((Gtk::Window*) this))
@@ -450,7 +434,7 @@ Meterbridge::set_session (Session* s)
        _show_master = _session->config.get_show_master_on_meterbridge();
        _show_midi = _session->config.get_show_midi_on_meterbridge();
 
-       SignalOrderRouteSorter sorter;
+       ARDOUR::SignalOrderRouteSorter sorter;
        boost::shared_ptr<RouteList> routes = _session->get_routes();
 
        RouteList copy(*routes);
@@ -812,6 +796,12 @@ Meterbridge::parameter_changed (std::string const & p)
        else if (p == "meterbridge-label-height") {
                scroller.queue_resize();
        }
+       else if (p == "show-monitor-on-meterbridge") {
+               scroller.queue_resize();
+       }
+       else if (p == "track-name-number") {
+               scroller.queue_resize();
+       }
 }
 
 void
index 971944266fad8df105812dd59d9eedfb006b8bc7..e5f30493d7e7860528efcbef44228d0cc6ea2ceb 100644 (file)
@@ -29,12 +29,12 @@ using namespace std;
 MidiAutomationLine::MidiAutomationLine (
        const std::string&                                      name,
        TimeAxisView&                                           tav,
-       ArdourCanvas::Group&                                    group,
+       ArdourCanvas::Item&                                     parent,
        boost::shared_ptr<ARDOUR::AutomationList>               list,
        boost::shared_ptr<ARDOUR::MidiRegion>                   region,
        Evoral::Parameter                                       parameter,
        Evoral::TimeConverter<double, ARDOUR::framepos_t>*      converter)
-       : AutomationLine (name, tav, group, list, converter)
+       : AutomationLine (name, tav, parent, list, converter)
        , _region (region)
        , _parameter (parameter)
 {
index df4db06c2c6fc5022c0888c1b56befb448a9db11..3748c35bd465a3f4496e06021230b44cf36e3e00 100644 (file)
@@ -26,7 +26,7 @@
 class MidiAutomationLine : public AutomationLine
 {
 public:
-       MidiAutomationLine (const std::string&, TimeAxisView&, ArdourCanvas::Group&,
+       MidiAutomationLine (const std::string&, TimeAxisView&, ArdourCanvas::Item&,
                            boost::shared_ptr<ARDOUR::AutomationList>,
                            boost::shared_ptr<ARDOUR::MidiRegion>,
                            Evoral::Parameter,
index 0d17bdbf6a33b071d12140c89cb2c12e3a7033f7..a738b3c0c47236c2b862cd464abdabab5087a4d1 100644 (file)
@@ -44,6 +44,7 @@
 #include "evoral/midi_util.h"
 
 #include "canvas/debug.h"
+#include "canvas/text.h"
 
 #include "automation_region_view.h"
 #include "automation_time_axis.h"
@@ -68,7 +69,6 @@
 #include "rgb_macros.h"
 #include "selection.h"
 #include "streamview.h"
-#include "utils.h"
 #include "patch_change_dialog.h"
 #include "verbose_cursor.h"
 #include "ardour_ui.h"
@@ -88,13 +88,13 @@ PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
 
 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
 
-MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
-                                boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
+MidiRegionView::MidiRegionView (ArdourCanvas::Container *parent, RouteTimeAxisView &tv,
+                                boost::shared_ptr<MidiRegion> r, double spu, uint32_t basic_color)
        : RegionView (parent, tv, r, spu, basic_color)
        , _current_range_min(0)
        , _current_range_max(0)
        , _active_notes(0)
-       , _note_group (new ArdourCanvas::Group (group))
+       , _note_group (new ArdourCanvas::Container (group))
        , _note_diff_command (0)
        , _ghost_note(0)
        , _step_edit_cursor (0)
@@ -124,14 +124,14 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &
        SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
 }
 
-MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
-                                boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
+MidiRegionView::MidiRegionView (ArdourCanvas::Container *parent, RouteTimeAxisView &tv,
+                                boost::shared_ptr<MidiRegion> r, double spu, uint32_t basic_color,
                                 TimeAxisViewItem::Visibility visibility)
        : RegionView (parent, tv, r, spu, basic_color, false, visibility)
        , _current_range_min(0)
        , _current_range_max(0)
        , _active_notes(0)
-       , _note_group (new ArdourCanvas::Group (parent))
+       , _note_group (new ArdourCanvas::Container (parent))
        , _note_diff_command (0)
        , _ghost_note(0)
        , _step_edit_cursor (0)
@@ -177,7 +177,7 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other)
        , _current_range_min(0)
        , _current_range_max(0)
        , _active_notes(0)
-       , _note_group (new ArdourCanvas::Group (get_canvas_group()))
+       , _note_group (new ArdourCanvas::Container (get_canvas_group()))
        , _note_diff_command (0)
        , _ghost_note(0)
        , _step_edit_cursor (0)
@@ -197,13 +197,7 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other)
        , pre_press_cursor (0)
        , _note_player (0)
 {
-       Gdk::Color c;
-       int r,g,b,a;
-
-       UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
-       c.set_rgb_p (r/255.0, g/255.0, b/255.0);
-
-       init (c, false);
+       init (false);
 }
 
 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
@@ -211,7 +205,7 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<M
        , _current_range_min(0)
        , _current_range_max(0)
        , _active_notes(0)
-       , _note_group (new ArdourCanvas::Group (get_canvas_group()))
+       , _note_group (new ArdourCanvas::Container (get_canvas_group()))
        , _note_diff_command (0)
        , _ghost_note(0)
        , _step_edit_cursor (0)
@@ -231,17 +225,11 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<M
        , pre_press_cursor (0)
        , _note_player (0)
 {
-       Gdk::Color c;
-       int r,g,b,a;
-
-       UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
-       c.set_rgb_p (r/255.0, g/255.0, b/255.0);
-
-       init (c, true);
+       init (true);
 }
 
 void
-MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
+MidiRegionView::init (bool wfd)
 {
        PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
 
@@ -256,9 +244,7 @@ MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
        _model = midi_region()->midi_source(0)->model();
        _enable_display = false;
 
-       RegionView::init (basic_color, false);
-
-       compute_colors (basic_color);
+       RegionView::init (false);
 
        set_height (trackview.current_height());
 
@@ -3624,7 +3610,7 @@ void
 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
 {
        if (_step_edit_cursor == 0) {
-               ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
+               ArdourCanvas::Item* const group = get_canvas_group();
 
                _step_edit_cursor = new ArdourCanvas::Rectangle (group);
                _step_edit_cursor->set_y0 (0);
@@ -3739,7 +3725,7 @@ MidiRegionView::trim_front_starting ()
        /* Reparent the note group to the region view's parent, so that it doesn't change
           when the region view is trimmed.
        */
-       _temporary_note_group = new ArdourCanvas::Group (group->parent ());
+       _temporary_note_group = new ArdourCanvas::Container (group->parent ());
        _temporary_note_group->move (group->position ());
        _note_group->reparent (_temporary_note_group);
 }
@@ -3824,28 +3810,9 @@ MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
 void
 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
 {
-       double wx, wy;
-
-       trackview.editor().verbose_cursor()->set_text (text);
-       trackview.editor().get_pointer_position (wx, wy);
-
-       wx += xoffset;
-       wy += yoffset;
-
-       /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
-
-       boost::optional<ArdourCanvas::Rect> bbo = trackview.editor().verbose_cursor()->item().bounding_box();
-
-       assert (bbo);
-       
-       ArdourCanvas::Rect bb = bbo.get();
-
-       if ((wy + bb.y1 - bb.y0) > trackview.editor().visible_canvas_height()) {
-               wy -= (bb.y1 - bb.y0) + 2 * yoffset;
-       }
-
-       trackview.editor().verbose_cursor()->set_position (wx, wy);
+       trackview.editor().verbose_cursor()->set (text);
        trackview.editor().verbose_cursor()->show ();
+       trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
 }
 
 /** @param p A session framepos.
index 49759da531a0cd4f9a9b9010d69a7c9b430a4a99..3fd6625ce90c18c4103788ac5b7fb2e946657324 100644 (file)
@@ -21,7 +21,7 @@
 
 #include <string>
 #include <vector>
-
+#include <stdint.h>
 
 #include "pbd/signals.h"
 
@@ -67,18 +67,18 @@ public:
        typedef Evoral::Note<Evoral::MusicalTime> NoteType;
        typedef Evoral::Sequence<Evoral::MusicalTime>::Notes Notes;
 
-       MidiRegionView (ArdourCanvas::Group *,
+       MidiRegionView (ArdourCanvas::Container *,
                        RouteTimeAxisView&,
                        boost::shared_ptr<ARDOUR::MidiRegion>,
                        double initial_samples_per_pixel,
-                       Gdk::Color const & basic_color);
+                       uint32_t base_color);
 
        MidiRegionView (const MidiRegionView& other);
        MidiRegionView (const MidiRegionView& other, boost::shared_ptr<ARDOUR::MidiRegion>);
 
        ~MidiRegionView ();
 
-       virtual void init (Gdk::Color const & basic_color, bool wfd);
+       void init (bool wfd);
 
        const boost::shared_ptr<ARDOUR::MidiRegion> midi_region() const;
 
@@ -309,11 +309,11 @@ protected:
        /** Allows derived types to specify their visibility requirements
         * to the TimeAxisViewItem parent class.
         */
-       MidiRegionView (ArdourCanvas::Group *,
+       MidiRegionView (ArdourCanvas::Container *,
                        RouteTimeAxisView&,
                        boost::shared_ptr<ARDOUR::MidiRegion>,
                        double samples_per_pixel,
-                       Gdk::Color& basic_color,
+                       uint32_t basic_color,
                        TimeAxisViewItem::Visibility);
 
        void region_resized (const PBD::PropertyChange&);
@@ -387,7 +387,7 @@ private:
        PatchChanges                         _patch_changes;
        SysExes                              _sys_exes;
        Note**                               _active_notes;
-       ArdourCanvas::Group*                 _note_group;
+       ArdourCanvas::Container*                 _note_group;
        ARDOUR::MidiModel::NoteDiffCommand*  _note_diff_command;
        Note*                                _ghost_note;
        double                               _last_ghost_x;
@@ -401,7 +401,7 @@ private:
        /** A group used to temporarily reparent _note_group to during start trims, so
         *  that the notes don't move with the parent region view.
         */
-       ArdourCanvas::Group*                 _temporary_note_group;
+       ArdourCanvas::Container*                 _temporary_note_group;
 
        MouseState _mouse_state;
        int _pressed_button;
index a1e1269468ada724f5c7b2829b42bcc66293ac67..97ce7ed9399c98223814b8224b9ef9230219caf0 100644 (file)
@@ -52,6 +52,7 @@
 
 using namespace std;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Editing;
 
@@ -68,14 +69,14 @@ MidiStreamView::MidiStreamView (MidiTimeAxisView& tv)
        , _updates_suspended (false)
 {
        /* use a group dedicated to MIDI underlays. Audio underlays are not in this group. */
-       midi_underlay_group = new ArdourCanvas::Group (_canvas_group);
+       midi_underlay_group = new ArdourCanvas::Container (_canvas_group);
        midi_underlay_group->lower_to_bottom();
 
        /* put the note lines in the timeaxisview's group, so it
           can be put below ghost regions from MIDI underlays
        */
-       _note_lines = new ArdourCanvas::LineSet (_canvas_group);
-
+       _note_lines = new ArdourCanvas::LineSet (_canvas_group, ArdourCanvas::LineSet::Horizontal);
+       
        _note_lines->Event.connect(
                sigc::bind(sigc::mem_fun(_trackview.editor(),
                                         &PublicEditor::canvas_stream_view_event),
@@ -110,7 +111,7 @@ MidiStreamView::create_region_view (boost::shared_ptr<Region> r, bool /*wfd*/, b
        RegionView* region_view = new MidiRegionView (_canvas_group, _trackview, region,
                                                      _samples_per_pixel, region_color);
 
-       region_view->init (region_color, false);
+       region_view->init (false);
 
        return region_view;
 }
@@ -273,7 +274,8 @@ void
 MidiStreamView::update_contents_height ()
 {
        StreamView::update_contents_height();
-       _note_lines->set_height (child_height ());
+
+       _note_lines->set_extent (ArdourCanvas::COORD_MAX);
 
        apply_note_range (lowest_note(), highest_note(), true);
 }
index b3506d42248d2c34cea8b16911b1c56dcab6c48d..9dbfbae5ead073f08b1dec59bbaf4adad4e9bc86 100644 (file)
@@ -68,7 +68,7 @@ class MidiStreamView : public StreamView
        };
 
        Gtk::Adjustment note_range_adjustment;
-       ArdourCanvas::Group* midi_underlay_group;
+       ArdourCanvas::Container* midi_underlay_group;
 
        void set_note_range(VisibleNoteRange r);
 
index 8ced280661ec9f6806acfe53294d38b5abc784db..c37916dbf0c95b7056b1c8f7668e8a7ec57570d3 100644 (file)
@@ -91,6 +91,7 @@
 #include "i18n.h"
 
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtk;
 using namespace Gtkmm2ext;
index d7a1149a4b29d6a5174522f97f3fee3119c8d344..090293612b440f612b18a2c850db679da6d20938 100644 (file)
@@ -85,7 +85,7 @@ MixerActor::load_bindings ()
 
        std::string binding_file;
 
-       if (find_file_in_search_path (ardour_config_search_path(), "mixer.bindings", binding_file)) {
+       if (find_file (ardour_config_search_path(), "mixer.bindings", binding_file)) {
                 bindings.load (binding_file);
                info << string_compose (_("Loaded mixer bindings from %1"), binding_file) << endmsg;
         } else {
index 5878c3d3acac0fed345e653d1b62dbc572047751..03e9409df22b4dfc07b83e17e7f1192b88a92e85 100644 (file)
 #include "gtkmm2ext/utils.h"
 
 #include "ardour/route_group.h"
+
+#include "canvas/utils.h"
+
+#include "ardour_ui.h"
 #include "mixer_group_tabs.h"
 #include "mixer_strip.h"
 #include "mixer_ui.h"
+#include "rgb_macros.h"
+#include "route_group_dialog.h"
 #include "utils.h"
+
 #include "i18n.h"
-#include "route_group_dialog.h"
 
 using namespace std;
 using namespace Gtk;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 
 MixerGroupTabs::MixerGroupTabs (Mixer_UI* m)
@@ -91,13 +98,17 @@ void
 MixerGroupTabs::draw_tab (cairo_t* cr, Tab const & tab) const
 {
        double const arc_radius = get_height();
-
+       double r, g, b, a;
+       
        if (tab.group && tab.group->is_active()) {
-               cairo_set_source_rgba (cr, tab.color.get_red_p (), tab.color.get_green_p (), tab.color.get_blue_p (), 1);
+               ArdourCanvas::color_to_rgba (tab.color, r, g, b, a);
        } else {
-               cairo_set_source_rgba (cr, 1, 1, 1, 0.2);
+               ArdourCanvas::color_to_rgba (ARDOUR_UI::config()->get_canvasvar_InactiveGroupTab(), r, g, b, a);
        }
+       
+       a = 1.0;
 
+       cairo_set_source_rgba (cr, r, g, b, a);
        cairo_arc (cr, tab.from + arc_radius, get_height(), arc_radius, M_PI, 3 * M_PI / 2);
        cairo_line_to (cr, tab.to - arc_radius, 0);
        cairo_arc (cr, tab.to - arc_radius, get_height(), arc_radius, 3 * M_PI / 2, 2 * M_PI);
@@ -109,8 +120,11 @@ MixerGroupTabs::draw_tab (cairo_t* cr, Tab const & tab) const
 
                cairo_text_extents_t ext;
                cairo_text_extents (cr, tab.group->name().c_str(), &ext);
+               
+               ArdourCanvas::Color c = ArdourCanvas::contrasting_text_color (ArdourCanvas::rgba_to_color (r, g, b, a));
+               ArdourCanvas::color_to_rgba (c, r, g, b, a);
 
-               cairo_set_source_rgb (cr, 1, 1, 1);
+               cairo_set_source_rgb (cr, r, g, b);
                cairo_move_to (cr, tab.from + (tab.to - tab.from - f.second) / 2, get_height() - ext.height / 2);
                cairo_save (cr);
                cairo_show_text (cr, f.first.c_str());
index 8b25e6f41ee330b2668c2dff4fee7828f037f5b0..005993e23d31cd787c4cdbfbe31c5e3b4dc24ca1 100644 (file)
@@ -69,6 +69,7 @@
 #include "i18n.h"
 
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtk;
 using namespace Gtkmm2ext;
@@ -379,6 +380,7 @@ MixerStrip::init ()
        parameter_changed (X_("mixer-strip-visibility"));
 
        Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
+       _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
 
        gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
 }
@@ -1526,6 +1528,7 @@ MixerStrip::build_route_ops_menu ()
        items.push_back (CheckMenuElem (_("Active")));
        Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
        i->set_active (_route->active());
+       i->set_sensitive(! _session->transport_rolling());
        i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
 
        items.push_back (SeparatorElem());
@@ -1600,10 +1603,19 @@ MixerStrip::name_changed ()
 {
        switch (_width) {
        case Wide:
-               name_button.set_text (_route->name());
+               if (_session->config.get_track_name_number()) {
+                       name_button.set_markup(track_number_to_string (_route->track_number (), " ", _route->name ()));
+               } else {
+                       name_button.set_text (_route->name());
+               }
                break;
        case Narrow:
-               name_button.set_text (PBD::short_version (_route->name(), 5));
+               if (_session->config.get_track_name_number()) {
+                       name_button.set_markup(track_number_to_string (_route->track_number (), " ",
+                                               PBD::short_version (_route->name (), 5)));
+               } else {
+                       name_button.set_text (PBD::short_version (_route->name(), 5));
+               }
                break;
        }
 
@@ -1900,6 +1912,7 @@ MixerStrip::show_send (boost::shared_ptr<Send> send)
        panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
        panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
        panner_ui().setup_pan ();
+       panner_ui().set_send_drawing_mode (true);
        panner_ui().show_all ();
 
        input_button.set_sensitive (false);
@@ -1935,6 +1948,7 @@ MixerStrip::revert_to_default_display ()
        panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
        update_panner_choices();
        panner_ui().setup_pan ();
+       panner_ui().set_send_drawing_mode (false);
 
        if (has_audio_outputs ()) {
                panners.show_all ();
@@ -2075,6 +2089,9 @@ MixerStrip::parameter_changed (string p)
                */
                _visibility.set_state (Config->get_mixer_strip_visibility ());
        }
+       else if (p == "track-name-number") {
+               name_changed ();
+       }
 }
 
 /** Called to decide whether the solo isolate / solo lock button visibility should
index 883bfffdba14a131fa8347373c20e74e7165f689..afb165f36a620d05f0a777d3b3af1bc63994f6cc 100644 (file)
@@ -305,7 +305,7 @@ class MixerStrip : public RouteUI, public Gtk::EventBox
        VisibilityGroup _visibility;
        boost::optional<bool> override_solo_visibility () const;
 
-       PBD::ScopedConnection _config_connection;
+       PBD::ScopedConnectionList _config_connection;
 
        void add_input_port (ARDOUR::DataType);
        void add_output_port (ARDOUR::DataType);
index 874f8feddf7ac119cb6bb3c85701cda842b24790..e9c36690a64164f7aa074110c597cf4257a99a3c 100644 (file)
@@ -41,6 +41,7 @@
 #include "ardour/midi_track.h"
 #include "ardour/plugin_manager.h"
 #include "ardour/route_group.h"
+#include "ardour/route_sorters.h"
 #include "ardour/session.h"
 
 #include "keyboard.h"
@@ -60,6 +61,7 @@
 #include "i18n.h"
 
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtk;
 using namespace Glib;
@@ -91,7 +93,10 @@ Mixer_UI::Mixer_UI ()
        , _monitor_section (0)
        , _strip_width (Config->get_default_narrow_ms() ? Narrow : Wide)
        , ignore_reorder (false)
+        , _in_group_rebuild_or_clear (false)
+        , _route_deletion_in_progress (false)
        , _following_editor_selection (false)
+       , _maximised (false)
 {
        /* allow this window to become the key focus window */
        set_flags (CAN_FOCUS);
@@ -239,9 +244,6 @@ Mixer_UI::Mixer_UI ()
        list_hpane.show();
        group_display.show();
 
-       _in_group_rebuild_or_clear = false;
-       _maximised = false;
-
        MixerStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_strip, this, _1), gui_context());
 
         MonitorSection::setup_knob_images ();
@@ -420,6 +422,7 @@ Mixer_UI::remove_strip (MixerStrip* strip)
        
        for (ri = rows.begin(); ri != rows.end(); ++ri) {
                if ((*ri)[track_columns.strip] == strip) {
+                        PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
                        track_model->erase (ri);
                        break;
                }
@@ -997,9 +1000,17 @@ Mixer_UI::track_list_delete (const Gtk::TreeModel::Path&)
 {
        /* this happens as the second step of a DnD within the treeview as well
           as when a row/route is actually deleted.
+           
+           if it was a deletion then we have to force a redisplay because
+           order keys may not have changed.
        */
+
        DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview row deleted\n");
        sync_order_keys_from_treeview ();
+
+        if (_route_deletion_in_progress) {
+                redisplay_track_list ();
+        }
 }
 
 void
@@ -1090,28 +1101,12 @@ Mixer_UI::strip_width_changed ()
 
 }
 
-struct SignalOrderRouteSorter {
-    bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
-           if (a->is_master() || a->is_monitor()) {
-                   /* "a" is a special route (master, monitor, etc), and comes
-                    * last in the mixer ordering
-                    */
-                   return false;
-           } else if (b->is_master() || b->is_monitor()) {
-                   /* everything comes before b */
-                   return true;
-           }
-           return a->order_key () < b->order_key ();
-
-    }
-};
-
 void
 Mixer_UI::initial_track_display ()
 {
        boost::shared_ptr<RouteList> routes = _session->get_routes();
        RouteList copy (*routes);
-       SignalOrderRouteSorter sorter;
+       ARDOUR::SignalOrderRouteSorter sorter;
 
        copy.sort (sorter);
 
index 693fd9dfa557b7848e0bc95899bf288d5723fbbf..e5d78f22182a79067aab8cffd736f11044732fad 100644 (file)
@@ -272,6 +272,7 @@ class Mixer_UI : public Gtk::Window, public PBD::ScopedConnectionList, public AR
            it during a session teardown.
        */
        bool _in_group_rebuild_or_clear;
+        bool _route_deletion_in_progress;
 
        void update_title ();
        MixerStrip* strip_by_x (int x);
index d0bc802b0e7ea44ee4a06fc87c5f92041e8ad880..9b4479a4f7e8317e6ea672593d6ae6d2679bcbd9 100644 (file)
@@ -339,7 +339,7 @@ This mode provides many different operations on both regions and control points,
 @trans|Transport/ToggleAutoPlay|5|toggle auto play
 @trans|Transport/ToggleAutoReturn|6|toggle auto return
 @trans|Transport/ToggleClick|7|toggle click (metronome)
-@ranges|Editor/set-tempo-from-region|9|set tempo (1 bar) from region(s)
+@ranges|Region/set-tempo-from-region|9|set tempo (1 bar) from region(s)
 @ranges|Editor/set-tempo-from-edit-range|0|set tempo (1 bar) from edit range
 
 ; mouse stuff
index 46d500dac6e6021056c08c2173bd81596c7a14e7..a816dd397dce3daaa6044a400e16ed3141cd6d3e 100644 (file)
@@ -40,6 +40,7 @@
 #include "i18n.h"
 
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace Gtk;
 using namespace Gtkmm2ext;
 using namespace PBD;
index e136c05153bd7eef3d14585537ad6b16abbcee2a..fad2c09b644cdd3a6bcb693777c40630f8425b5d 100644 (file)
@@ -50,6 +50,7 @@
 using namespace std;
 using namespace Gtk;
 using namespace Gtkmm2ext;
+using namespace ARDOUR_UI_UTILS;
 
 static const int pos_box_size = 9;
 static const int lr_box_size = 15;
@@ -80,9 +81,7 @@ MonoPanner::MonoPanner (boost::shared_ptr<ARDOUR::PannerShell> p)
        if (!have_font) {
                Pango::FontDescription font;
                Pango::AttrFontDesc* font_attr;
-               font = Pango::FontDescription ("ArdourMono");
-               font.set_weight (Pango::WEIGHT_BOLD);
-               font.set_size(9 * PANGO_SCALE);
+               font = Pango::FontDescription (ARDOUR_UI::config()->get_canvasvar_SmallBoldMonospaceFont());
                font_attr = new Pango::AttrFontDesc (Pango::Attribute::create_attr_font_desc (font));
                panner_font_attributes.change(*font_attr);
                delete font_attr;
@@ -158,6 +157,10 @@ MonoPanner::on_expose_event (GdkEventExpose*)
                t  = 0x606060ff;
        }
 
+       if (_send_mode) {
+               b = rgba_from_style("SendStripBase",
+                               UINT_RGBA_R(b), UINT_RGBA_G(b), UINT_RGBA_B(b), 255, "fg");
+       }
        /* background */
        context->set_source_rgba (UINT_RGBA_R_FLT(b), UINT_RGBA_G_FLT(b), UINT_RGBA_B_FLT(b), UINT_RGBA_A_FLT(b));
        context->rectangle (0, 0, width, height);
index 98e33d77d696240ca12dfa8151e56bd8459fd5b7..4ba5c66688c86ee92e172dad1f821ff56adaba67 100644 (file)
 #include "mouse_cursors.h"
 #include "editor_xpms"
 
+using namespace ARDOUR_UI_UTILS;
+
 MouseCursors::MouseCursors ()
+       : cross_hair (0)
+       , trimmer (0)
+       , right_side_trim (0)
+       , anchored_right_side_trim (0)
+       , left_side_trim (0)
+       , anchored_left_side_trim (0)
+       , right_side_trim_left_only (0)
+       , left_side_trim_right_only (0)
+       , fade_in (0)
+       , fade_out (0)
+       , selector (0)
+       , grabber (0)
+       , grabber_note (0)
+       , grabber_edit_point (0)
+       , zoom_in (0)
+       , zoom_out (0)
+       , time_fx (0)
+       , fader (0)
+       , speaker (0)
+       , midi_pencil (0)
+       , midi_select (0)
+       , midi_resize (0)
+       , midi_erase (0)
+       , up_down (0)
+       , wait (0)
+       , timebar (0)
+       , transparent (0)
+       , resize_left (0)
+       , resize_top_left (0)
+       , resize_top (0)
+       , resize_top_right (0)
+       , resize_right (0)
+       , resize_bottom_right (0)
+       , resize_bottom (0)
+       , resize_bottom_left (0)
+       , move (0)
+       , expand_left_right (0)
+       , expand_up_down (0)
+{
+}
+
+void
+MouseCursors::drop_all ()
+{
+       delete cross_hair; cross_hair = 0;
+       delete trimmer; trimmer = 0;
+       delete right_side_trim; right_side_trim = 0;
+       delete anchored_right_side_trim; anchored_right_side_trim = 0;
+       delete left_side_trim; left_side_trim = 0;
+       delete anchored_left_side_trim; anchored_left_side_trim = 0;
+       delete right_side_trim_left_only; right_side_trim_left_only = 0;
+       delete left_side_trim_right_only; left_side_trim_right_only = 0;
+       delete fade_in; fade_in = 0;
+       delete fade_out; fade_out = 0;
+       delete selector; selector = 0;
+       delete grabber; grabber = 0;
+       delete grabber_note; grabber_note = 0;
+       delete grabber_edit_point; grabber_edit_point = 0;
+       delete zoom_in; zoom_in = 0;
+       delete zoom_out; zoom_out = 0;
+       delete time_fx; time_fx = 0;
+       delete fader; fader = 0;
+       delete speaker; speaker = 0;
+       delete midi_pencil; midi_pencil = 0;
+       delete midi_select; midi_select = 0;
+       delete midi_resize; midi_resize = 0;
+       delete midi_erase; midi_erase = 0;
+       delete up_down; up_down = 0;
+       delete wait; wait = 0;
+       delete timebar; timebar = 0;
+       delete transparent; transparent = 0;
+       delete resize_left; resize_left = 0;
+       delete resize_top_left; resize_top_left = 0;
+       delete resize_top; resize_top = 0;
+       delete resize_top_right; resize_top_right = 0;
+       delete resize_right; resize_right = 0;
+       delete resize_bottom_right; resize_bottom_right = 0;
+       delete resize_bottom; resize_bottom = 0;
+       delete resize_bottom_left; resize_bottom_left = 0;
+       delete move; move = 0;
+       delete expand_left_right; expand_left_right = 0;
+       delete expand_up_down; expand_up_down = 0;
+}
+
+void
+MouseCursors::set_cursor_set (const std::string& name)
 {
        using namespace Glib;
        using namespace Gdk;
+       
+       drop_all ();
+       _cursor_set = name;
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("zoom_in_cursor"));
+               RefPtr<Pixbuf> p (::get_icon ("zoom_in_cursor", _cursor_set));
                zoom_in = new Cursor (Display::get_default(), p, 10, 5);
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("zoom_out_cursor"));
+               RefPtr<Pixbuf> p (::get_icon ("zoom_out_cursor", _cursor_set));
                zoom_out = new Cursor (Display::get_default(), p, 5, 5);
        }
 
@@ -67,17 +158,17 @@ MouseCursors::MouseCursors ()
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("grabber"));
+               RefPtr<Pixbuf> p (::get_icon ("grabber", _cursor_set));
                grabber = new Cursor (Display::get_default(), p, 5, 0);
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("grabber_note"));
+               RefPtr<Pixbuf> p (::get_icon ("grabber_note", _cursor_set));
                grabber_note = new Cursor (Display::get_default(), p, 5, 10);
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("grabber_edit_point"));
+               RefPtr<Pixbuf> p (::get_icon ("grabber_edit_point", _cursor_set));
                grabber_edit_point = new Cursor (Display::get_default(), p, 5, 17);
        }
 
@@ -85,92 +176,102 @@ MouseCursors::MouseCursors ()
        trimmer =  new Cursor (SB_H_DOUBLE_ARROW);
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("trim_left_cursor"));
+               RefPtr<Pixbuf> p (::get_icon ("trim_left_cursor", _cursor_set));
                left_side_trim = new Cursor (Display::get_default(), p, 5, 11);
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("trim_right_cursor"));
+               RefPtr<Pixbuf> p (::get_icon ("anchored_trim_left_cursor", _cursor_set));
+               anchored_left_side_trim = new Cursor (Display::get_default(), p, 5, 11);
+       }
+
+       {
+               RefPtr<Pixbuf> p (::get_icon ("trim_right_cursor", _cursor_set));
                right_side_trim = new Cursor (Display::get_default(), p, 23, 11);
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("trim_left_cursor_right_only"));
+               RefPtr<Pixbuf> p (::get_icon ("anchored_trim_right_cursor", _cursor_set));
+               anchored_right_side_trim = new Cursor (Display::get_default(), p, 23, 11);
+       }
+
+       {
+               RefPtr<Pixbuf> p (::get_icon ("trim_left_cursor_right_only", _cursor_set));
                left_side_trim_right_only = new Cursor (Display::get_default(), p, 5, 11);
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("trim_right_cursor_left_only"));
+               RefPtr<Pixbuf> p (::get_icon ("trim_right_cursor_left_only", _cursor_set));
                right_side_trim_left_only = new Cursor (Display::get_default(), p, 23, 11);
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("fade_in_cursor"));
+               RefPtr<Pixbuf> p (::get_icon ("fade_in_cursor", _cursor_set));
                fade_in = new Cursor (Display::get_default(), p, 0, 0);
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("fade_out_cursor"));
+               RefPtr<Pixbuf> p (::get_icon ("fade_out_cursor", _cursor_set));
                fade_out = new Cursor (Display::get_default(), p, 29, 0);
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("resize_left_cursor"));
+               RefPtr<Pixbuf> p (::get_icon ("resize_left_cursor", _cursor_set));
                resize_left = new Cursor (Display::get_default(), p, 3, 10);
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("resize_top_left_cursor"));
+               RefPtr<Pixbuf> p (::get_icon ("resize_top_left_cursor", _cursor_set));
                resize_top_left = new Cursor (Display::get_default(), p, 3, 3);
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("resize_top_cursor"));
+               RefPtr<Pixbuf> p (::get_icon ("resize_top_cursor", _cursor_set));
                resize_top = new Cursor (Display::get_default(), p, 10, 3);
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("resize_top_right_cursor"));
+               RefPtr<Pixbuf> p (::get_icon ("resize_top_right_cursor", _cursor_set));
                resize_top_right = new Cursor (Display::get_default(), p, 18, 3);
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("resize_right_cursor"));
+               RefPtr<Pixbuf> p (::get_icon ("resize_right_cursor", _cursor_set));
                resize_right = new Cursor (Display::get_default(), p, 24, 10);
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("resize_bottom_right_cursor"));
+               RefPtr<Pixbuf> p (::get_icon ("resize_bottom_right_cursor", _cursor_set));
                resize_bottom_right = new Cursor (Display::get_default(), p, 18, 18);
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("resize_bottom_cursor"));
+               RefPtr<Pixbuf> p (::get_icon ("resize_bottom_cursor", _cursor_set));
                resize_bottom = new Cursor (Display::get_default(), p, 10, 24);
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("resize_bottom_left_cursor"));
+               RefPtr<Pixbuf> p (::get_icon ("resize_bottom_left_cursor", _cursor_set));
                resize_bottom_left = new Cursor (Display::get_default(), p, 3, 18);
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("move_cursor"));
+               RefPtr<Pixbuf> p (::get_icon ("move_cursor", _cursor_set));
                move = new Cursor (Display::get_default(), p, 11, 11);
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("expand_left_right_cursor"));
+               RefPtr<Pixbuf> p (::get_icon ("expand_left_right_cursor", _cursor_set));
                expand_left_right = new Cursor (Display::get_default(), p, 11, 4);
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("expand_up_down_cursor"));
+               RefPtr<Pixbuf> p (::get_icon ("expand_up_down_cursor", _cursor_set));
                expand_up_down = new Cursor (Display::get_default(), p, 4, 11);
        }
 
        {
-               RefPtr<Pixbuf> p (::get_icon ("i_beam_cursor"));
+               RefPtr<Pixbuf> p (::get_icon ("i_beam_cursor", _cursor_set));
                selector = new Cursor (Display::get_default(), p, 4, 11);
        }
 
index bf93e1ad78b396e75ca571dff788f2404ed383fb..30eca6acb87ada57e3fc5a571cd235d2139f1e7a 100644 (file)
@@ -30,10 +30,15 @@ class MouseCursors
 public:
        MouseCursors ();
 
+       void set_cursor_set (const std::string& name);
+       std::string cursor_set() const { return _cursor_set; }
+
        Gdk::Cursor* cross_hair;
        Gdk::Cursor* trimmer;
        Gdk::Cursor* right_side_trim;
+       Gdk::Cursor* anchored_right_side_trim;
        Gdk::Cursor* left_side_trim;
+       Gdk::Cursor* anchored_left_side_trim;
        Gdk::Cursor* right_side_trim_left_only;
        Gdk::Cursor* left_side_trim_right_only;
        Gdk::Cursor* fade_in;
@@ -66,6 +71,11 @@ public:
        Gdk::Cursor* move;
        Gdk::Cursor* expand_left_right;
        Gdk::Cursor* expand_up_down;
+
+    private:
+       std::string _cursor_set;
+       void drop_all ();
+
 };
 
 #endif /* __gtk2_ardour_mouse_cursors__ */
index 3adcd751f8f8d8c2f2cb163684c1159bf2f5b986..dc8decf83776d6a76e1d5abe6705d549f59c377f 100644 (file)
@@ -31,9 +31,9 @@ using namespace ARDOUR;
 using namespace ArdourCanvas;
 
 Note::Note (
-       MidiRegionView& region, Group* group, const boost::shared_ptr<NoteType> note, bool with_events)
+       MidiRegionView& region, Item* parent, const boost::shared_ptr<NoteType> note, bool with_events)
        : NoteBase (region, with_events, note)
-       , _rectangle (new ArdourCanvas::Rectangle (group))
+       , _rectangle (new ArdourCanvas::Rectangle (parent))
 {
        CANVAS_DEBUG_NAME (_rectangle, "note");
        set_item (_rectangle);
index c6bcd957e3a9e6a2717d8dcd520811f0b95eb921..71faeb53fb6b43bec41d8ccb8f35e41df999fbc6 100644 (file)
@@ -26,7 +26,7 @@
 #include "midi_util.h"
 
 namespace ArdourCanvas {
-       class Group;
+       class Container;
 }
 
 class Note : public NoteBase
@@ -35,7 +35,7 @@ public:
        typedef Evoral::Note<Evoral::MusicalTime> NoteType;
 
        Note (MidiRegionView&                   region,
-             ArdourCanvas::Group*              group,
+             ArdourCanvas::Item*               parent,
              const boost::shared_ptr<NoteType> note = boost::shared_ptr<NoteType>(),
              bool with_events = true);
 
index 3bdb73489e078e53eaf7f7d3c3abe821092e5967..1a02b6e02e1d802e65f4c8980b1f7d0bd587590f 100644 (file)
@@ -170,18 +170,14 @@ NoteBase::base_color()
        switch (mode) {
        case TrackColor:
        {
-               Gdk::Color color = _region.midi_stream_view()->get_region_color();
-               return UINT_INTERPOLATE (RGBA_TO_UINT(
-                                                SCALE_USHORT_TO_UINT8_T(color.get_red()),
-                                                SCALE_USHORT_TO_UINT8_T(color.get_green()),
-                                                SCALE_USHORT_TO_UINT8_T(color.get_blue()),
-                                                opacity), 
-                                        ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected(), 0.5);
+               uint32_t color = _region.midi_stream_view()->get_region_color();
+               return UINT_INTERPOLATE (UINT_RGBA_CHANGE_A (color, opacity), 
+                                        ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected(), 
+                                        0.5);
        }
 
        case ChannelColors:
-               return UINT_INTERPOLATE (UINT_RGBA_CHANGE_A (NoteBase::midi_channel_colors[_note->channel()],
-                                                            opacity), 
+               return UINT_INTERPOLATE (UINT_RGBA_CHANGE_A (NoteBase::midi_channel_colors[_note->channel()], opacity), 
                                         ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected(), 0.5);
 
        default:
index 33926d6523afffb37b5c767cc18fa949f6c47d06..36ff825c1b90c7c52b6f57391bd2fdaa0c8ec196 100644 (file)
@@ -16,6 +16,7 @@
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
 */
+#include <algorithm>
 
 #include <gtkmm/box.h>
 #include <gtkmm/alignment.h>
@@ -29,7 +30,6 @@
 
 #include "option_editor.h"
 #include "gui_thread.h"
-#include "utils.h"
 #include "i18n.h"
 
 using namespace std;
@@ -147,6 +147,8 @@ EntryOption::EntryOption (string const & i, string const & n, sigc::slot<string>
        _label = manage (left_aligned_label (n + ":"));
        _entry = manage (new Entry);
        _entry->signal_activate().connect (sigc::mem_fun (*this, &EntryOption::activated));
+       _entry->signal_focus_out_event().connect (sigc::mem_fun (*this, &EntryOption::focus_out));
+       _entry->signal_insert_text().connect (sigc::mem_fun (*this, &EntryOption::filter_text));
 }
 
 void
@@ -161,12 +163,37 @@ EntryOption::set_state_from_config ()
        _entry->set_text (_get ());
 }
 
+void
+EntryOption::set_sensitive (bool s)
+{
+       _entry->set_sensitive (s);
+}
+
+void
+EntryOption::filter_text (const Glib::ustring&, int*)
+{
+       std::string text = _entry->get_text ();
+       for (size_t i = 0; i < _invalid.length(); ++i) {
+               text.erase (std::remove(text.begin(), text.end(), _invalid.at(i)), text.end());
+       }
+       if (text != _entry->get_text ()) {
+               _entry->set_text (text);
+       }
+}
+
 void
 EntryOption::activated ()
 {
        _set (_entry->get_text ());
 }
 
+bool
+EntryOption::focus_out (GdkEventFocus*)
+{
+       _set (_entry->get_text ());
+       return true;
+}
+
 /** Construct a BoolComboOption.
  *  @param i id
  *  @param n User-visible name.
@@ -230,6 +257,7 @@ FaderOption::FaderOption (string const & i, string const & n, sigc::slot<gain_t>
        _db_slider = manage (new HSliderController (&_db_adjustment, 115, 18, false));
 
        _label.set_text (n + ":");
+       _label.set_alignment (0, 0.5);
        _label.set_name (X_("OptionsLabel"));
 
        _fader_centering_box.pack_start (*_db_slider, true, false);
@@ -439,7 +467,10 @@ DirectoryOption::set_state_from_config ()
 void
 DirectoryOption::add_to_page (OptionEditorPage* p)
 {
-       add_widgets_to_page (p, manage (new Label (_name)), &_file_chooser);
+       Gtk::Label *label = manage (new Label (_name));
+       label->set_alignment (0, 0.5);
+       label->set_name (X_("OptionsLabel"));
+       add_widgets_to_page (p, label, &_file_chooser);
 }
 
 void
index 47afa77868f6186be211db07894235c8992619e0..f42d1da9b738aa96a2084c98382f617b7560d66c 100644 (file)
@@ -181,6 +181,23 @@ private:
        Gtk::Label*            _label; ///< label for button, so we can use markup
 };
 
+/** Component which allows to add any GTK Widget - intended for single buttons and custom stateless objects */
+class FooOption : public OptionEditorComponent
+{
+public:
+       FooOption (Gtk::Widget *w) : _w (w) {}
+
+       void add_to_page (OptionEditorPage* p) {
+               add_widget_to_page (p, _w);
+       }
+
+       Gtk::Widget& tip_widget() { return *_w; }
+       void set_state_from_config () {}
+       void parameter_changed (std::string const &) {}
+private:
+       Gtk::Widget *_w;
+};
+
 /** Component which provides the UI to handle a string option using a GTK Entry */
 class EntryOption : public Option
 {
@@ -189,17 +206,22 @@ public:
        EntryOption (std::string const &, std::string const &, sigc::slot<std::string>, sigc::slot<bool, std::string>);
        void set_state_from_config ();
        void add_to_page (OptionEditorPage*);
+       void set_sensitive (bool);
+       void set_invalid_chars (std::string i) { _invalid = i; }
 
-        Gtk::Widget& tip_widget() { return *_entry; }
+       Gtk::Widget& tip_widget() { return *_entry; }
 
 private:
 
        void activated ();
+       bool focus_out (GdkEventFocus*);
+       void filter_text (const Glib::ustring&, int*);
 
        sigc::slot<std::string> _get; ///< slot to get the configuration variable's value
        sigc::slot<bool, std::string> _set;  ///< slot to set the configuration variable's value
        Gtk::Label* _label; ///< UI label
        Gtk::Entry* _entry; ///< UI entry
+       std::string _invalid;
 };
 
 
@@ -347,6 +369,7 @@ public:
        }
 
        Gtk::Widget& tip_widget() { return *_hscale; }
+       Gtk::HScale& scale() { return *_hscale; }
 
 private:
        sigc::slot<float> _get;
index 90d753af4c604ed06caa750e9cb3a934fbd6372c..4596b5c62cb3fccc7cf10331e943b3e3c1483987 100644 (file)
@@ -102,7 +102,7 @@ ARDOUR_COMMAND_LINE::parse_opts (int argc, char *argv[])
                { "help", 0, 0, 'h' },
                { "no-announcements", 0, 0, 'a' },
                { "bindings", 0, 0, 'b' },
-               { "disable-plugins", 1, 0, 'd' },
+               { "disable-plugins", 0, 0, 'd' },
                { "debug", 1, 0, 'D' },
                { "no-splash", 0, 0, 'n' },
                { "menus", 1, 0, 'm' },
index a3befff5cf8a78c07d069115f40521d621aa9dda..abb47a98730a7f599b52e7726f10026d1aa862e0 100644 (file)
@@ -44,6 +44,7 @@
 using namespace std;
 using namespace Gtk;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using Gtkmm2ext::Keyboard;
 
@@ -75,6 +76,7 @@ Panner2d::Panner2d (boost::shared_ptr<PannerShell> p, int32_t h)
        , height (h)
        , last_width (0)
        , have_elevation (false)
+       , _send_mode (false)
 {
        panner_shell->Changed.connect (panshell_connections, invalidator (*this), boost::bind (&Panner2d::handle_state_change, this), gui_context());
 
@@ -378,6 +380,15 @@ Panner2d::find_closest_object (gdouble x, gdouble y, bool& is_signal)
        return closest;
 }
 
+void
+Panner2d::set_send_drawing_mode (bool onoff)
+{
+       if (_send_mode != onoff) {
+               _send_mode = onoff;
+               queue_draw ();
+       }
+}
+
 bool
 Panner2d::on_motion_notify_event (GdkEventMotion *ev)
 {
@@ -412,10 +423,16 @@ Panner2d::on_expose_event (GdkEventExpose *event)
        /* background */
 
        cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
+
+       float r, g, b;
+       r = g = b = 0.1;
+       if (_send_mode) {
+               rgba_p_from_style("SendStripBase", &r, &g, &b, "fg");
+       }
        if (!panner_shell->bypassed()) {
-               cairo_set_source_rgba (cr, 0.1, 0.1, 0.1, 1.0);
+               cairo_set_source_rgba (cr, r, g, b, 1.0);
        } else {
-               cairo_set_source_rgba (cr, 0.1, 0.1, 0.1, 0.2);
+               cairo_set_source_rgba (cr, r, g, b , 0.2);
        }
        cairo_fill_preserve (cr);
        cairo_clip (cr);
index fdd4748f8f753d19a50052321673c4b665736eee..99377bdd509bf8817a855dcd75276e7f6e05bbcd 100644 (file)
@@ -46,7 +46,7 @@ namespace Gtk {
 }
 
 namespace Pango {
-       class Layout;
+       class Container;
 }
 
 class Panner2dWindow;
@@ -63,6 +63,7 @@ class Panner2d : public Gtk::DrawingArea
        int  add_signal (const char* text, const PBD::AngularVector&);
        void move_signal (int which, const PBD::AngularVector&);
        void reset (uint32_t n_inputs);
+       void set_send_drawing_mode (bool);
 
        boost::shared_ptr<ARDOUR::PannerShell> get_panner_shell() const { return panner_shell; }
 
@@ -117,6 +118,7 @@ class Panner2d : public Gtk::DrawingArea
        double  last_width;
        bool    did_move;
        bool    have_elevation;
+       bool    _send_mode;
 
        Target *find_closest_object (gdouble x, gdouble y, bool& is_signal);
 
index 09cf29dfd62757718e1c54cb3dbc580ed0008a08..0fcba4cd1c6ef5c4c2d1ce4fa691bdc83c5fdfdc 100644 (file)
@@ -34,6 +34,7 @@ using namespace Gtkmm2ext;
 PannerInterface::PannerInterface (boost::shared_ptr<Panner> p)
        : _panner (p)
        , _tooltip (this)
+       , _send_mode (false)
        , _editor (0)
 {
         set_flags (Gtk::CAN_FOCUS);
@@ -109,6 +110,14 @@ PannerInterface::edit ()
        _editor->show ();
 }
 
+void
+PannerInterface::set_send_drawing_mode(bool onoff) {
+       if (_send_mode != onoff) {
+               _send_mode = onoff;
+               queue_draw ();
+       }
+}
+
 PannerPersistentTooltip::PannerPersistentTooltip (Gtk::Widget* w)
        : PersistentTooltip (w)
        , _dragging (false)
index 02f4e210bde88714ad775b1a284b2c16cf913365..a18c08352873b1474cd13ff82fe68cea4b1fe4d1 100644 (file)
@@ -59,6 +59,7 @@ public:
        }
 
        void edit ();
+       void set_send_drawing_mode (bool);
 
 protected:
        virtual void set_tooltip () = 0;
@@ -74,6 +75,8 @@ protected:
        boost::shared_ptr<ARDOUR::Panner> _panner;
        PannerPersistentTooltip _tooltip;
 
+       bool _send_mode;
+
 private:
        virtual PannerEditor* editor () = 0;
        PannerEditor* _editor;
index d20d719b14cb4e3d4e338378bce5843c5a966d69..ec25fad32ca1b5b0ac08f17062d87132785b3c4d 100644 (file)
@@ -31,7 +31,6 @@
 #include "ardour_ui.h"
 #include "panner_ui.h"
 #include "panner2d.h"
-#include "utils.h"
 #include "gui_thread.h"
 #include "stereo_panner.h"
 #include "mono_panner.h"
@@ -50,6 +49,7 @@ PannerUI::PannerUI (Session* s)
        : _current_nouts (-1)
        , _current_nins (-1)
        , _current_uri ("")
+       , _send_mode (false)
        , pan_automation_style_button ("")
        , pan_automation_state_button ("")
        , _panner_list()
@@ -251,6 +251,7 @@ PannerUI::setup_pan ()
 
                _stereo_panner = new StereoPanner (_panshell);
                _stereo_panner->set_size_request (-1, pan_bar_height);
+               _stereo_panner->set_send_drawing_mode (_send_mode);
                pan_vbox.pack_start (*_stereo_panner, false, false);
 
                boost::shared_ptr<AutomationControl> ac;
@@ -286,6 +287,7 @@ PannerUI::setup_pan ()
                _mono_panner->signal_button_release_event().connect (sigc::mem_fun(*this, &PannerUI::pan_button_event));
 
                _mono_panner->set_size_request (-1, pan_bar_height);
+               _mono_panner->set_send_drawing_mode (_send_mode);
 
                update_pan_sensitive ();
                pan_vbox.pack_start (*_mono_panner, false, false);
@@ -305,6 +307,7 @@ PannerUI::setup_pan ()
                        big_window->reset (nins);
                }
                twod_panner->set_size_request (-1, 61);
+               twod_panner->set_send_drawing_mode (_send_mode);
 
                /* and finally, add it to the panner frame */
 
@@ -323,6 +326,19 @@ PannerUI::setup_pan ()
        pan_vbox.show_all ();
 }
 
+void
+PannerUI::set_send_drawing_mode (bool onoff)
+{
+       if (_stereo_panner) {
+               _stereo_panner->set_send_drawing_mode (onoff);
+       } else if (_mono_panner) {
+               _mono_panner->set_send_drawing_mode (onoff);
+       } else if (twod_panner) {
+               twod_panner->set_send_drawing_mode (onoff);
+       }
+       _send_mode = onoff;
+}
+
 void
 PannerUI::start_touch (boost::weak_ptr<AutomationControl> wac)
 {
index 8bf448c7ea509c47815041718f6706cf142dff70..600a9d145c86d3af0a523718c1e47ed8e3269375 100644 (file)
@@ -74,6 +74,7 @@ class PannerUI : public Gtk::HBox, public ARDOUR::SessionHandlePtr
        void set_width (Width);
        void setup_pan ();
        void set_available_panners(std::map<std::string,std::string>);
+       void set_send_drawing_mode (bool);
 
        void effective_pan_display ();
 
@@ -97,6 +98,7 @@ class PannerUI : public Gtk::HBox, public ARDOUR::SessionHandlePtr
        int _current_nouts;
        int _current_nins;
        std::string _current_uri;
+       bool _send_mode;
 
        static const int pan_bar_height;
 
index 6be40811bfa99ea2f1526bf2c726ace70575dfd9..b2882f27afbede59231f2f3a2be01654cd203a98 100644 (file)
@@ -43,7 +43,7 @@ using namespace std;
  */
 PatchChange::PatchChange(
                MidiRegionView& region,
-               ArdourCanvas::Group* parent,
+               ArdourCanvas::Container* parent,
                const string&   text,
                double          height,
                double          x,
index 652f9d66c176adb79e0d75037a44a9d0ae6c89b3..44f10074d59c9f9999ef4e9ace3cc1c5fac3b56b 100644 (file)
@@ -35,7 +35,7 @@ class PatchChange
 public:
        PatchChange(
                MidiRegionView& region,
-               ArdourCanvas::Group* parent,
+               ArdourCanvas::Container* parent,
                const string&   text,
                double          height,
                double          x,
index 252accd5238aa3905ac4c162ceb925dc9f6fcfbf..fcf056307e7be2deaf81460a609df947fbdc46b0 100644 (file)
@@ -316,7 +316,12 @@ PluginSelector::refiller (const PluginInfoList& plugs, const::std::string& filte
 #else
                        while (pos < creator.length() && (isalnum (creator[pos]) || isspace (creator[pos]))) ++pos;
 #endif
-                       creator = creator.substr (0, pos);
+                       // If there were too few characters to create a
+                       // meaningful name, mark this creator as 'Unknown'
+                       if (creator.length()<2 || pos<3)
+                               creator = "Unknown";
+                       else
+                               creator = creator.substr (0, pos);
 
                        newrow[plugin_columns.creator] = creator;
 
@@ -685,11 +690,20 @@ PluginSelector::create_by_creator_menu (ARDOUR::PluginInfoList& all_plugs)
                /* stupid LADSPA creator strings */
                string::size_type pos = 0;
 #ifdef PLATFORM_WINDOWS
-               while (pos < creator.length() && creator[pos] > -2 && creator[pos] < 256 && (isalnum (creator[pos]) || isspace (creator[pos]))) ++pos;
+               while (pos < creator.length() && creator[pos]>(-2) && creator[pos]<256 && (isprint (creator[pos]))) ++pos;
 #else
                while (pos < creator.length() && (isalnum (creator[pos]) || isspace (creator[pos]))) ++pos;
 #endif
-               creator = creator.substr (0, pos);
+
+               // Check to see if we found any invalid characters.
+               if (creator.length() != pos) {
+                       // If there were too few characters to create a
+                       // meaningful name, mark this creator as 'Unknown'
+                       if (pos<3)
+                               creator = "Unknown?";
+                       else
+                               creator = creator.substr (0, pos);
+               }
 
                SubmenuMap::iterator x;
                Gtk::Menu* submenu;
index 190fd2ad969bddb5f6b1a0c253fc86aa8ac0fbe0..440dc805316c713cb15c8f1340d81bed6dc27a49 100644 (file)
@@ -73,6 +73,7 @@
 
 using namespace std;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtkmm2ext;
 using namespace Gtk;
index 2a982eabc1504e2fd414146114b0a5582addca91..a742740e5a607148ef3f84b53a466b516d2082b6 100644 (file)
@@ -28,7 +28,6 @@
 #include "ardour/session.h"
 
 #include "port_insert_ui.h"
-#include "utils.h"
 #include "gui_thread.h"
 #include "i18n.h"
 
index 6bd9d962629d499c007760ac22fc584c6232062c..aa1c853ed8c3dd5a7ebe9f8e28ac7d4c766fd78d 100644 (file)
@@ -43,6 +43,7 @@
 using namespace std;
 using namespace Gtk;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 
 /** PortMatrix constructor.
  *  @param session Our session.
index 1f5a21feec22daa38a9cef20161ce1340ca1522d..8e18dbea7a173032874e2aeffe6d696279609904 100644 (file)
@@ -23,7 +23,6 @@
 #include "port_matrix_column_labels.h"
 #include "port_matrix.h"
 #include "port_matrix_body.h"
-#include "utils.h"
 
 #include "i18n.h"
 
index de921a6cb71336c3cbc165ebd1cc18c5ceab7a9a..998a234bf353044703e328cb3fa0ad8c201774bc 100644 (file)
@@ -26,7 +26,6 @@
 #include "port_matrix.h"
 #include "port_matrix_body.h"
 #include "i18n.h"
-#include "utils.h"
 
 using namespace std;
 
index aa8f285a43efc41d519b3dd4cdd0fee7807395c8..4122d9687d83e3b8924150a58e5fd2c63d09cd61 100644 (file)
@@ -76,7 +76,6 @@
 #include "return_ui.h"
 #include "route_processor_selection.h"
 #include "send_ui.h"
-#include "utils.h"
 
 #include "i18n.h"
 
@@ -2111,8 +2110,8 @@ ProcessorBox::paste_processor_state (const XMLNodeList& nlist, boost::shared_ptr
 
                                boost::shared_ptr<Pannable> sendpan(new Pannable (*_session));
                                XMLNode n (**niter);
-                                InternalSend* s = new InternalSend (*_session, sendpan, _route->mute_master(),
-                                                                   boost::shared_ptr<Route>(), Delivery::Aux); 
+                               InternalSend* s = new InternalSend (*_session, sendpan, _route->mute_master(),
+                                               _route, boost::shared_ptr<Route>(), Delivery::Aux);
 
                                IOProcessor::prepare_for_reset (n, s->name());
 
index 6c5d528e1e9aad86d84496496955def2b5bc5c7f..e88f273c8795e29944c2c90f0c448a9eec292de8 100644 (file)
@@ -31,6 +31,7 @@ sigc::signal<void> PublicEditor::DropDownKeys;
 PublicEditor::PublicEditor ()
        : Window (Gtk::WINDOW_TOPLEVEL)
        , VisibilityTracker (*((Gtk::Window*)this))
+       , _suspend_route_redisplay_counter (0)
 {
 }
 
index f3b76e93100ab4a2b790dd9c43e9c6583cce049e..9eac1be2f71919ede945a1295773d193a4f1a137 100644 (file)
@@ -84,6 +84,13 @@ class VerboseCursor;
 class XMLNode;
 struct SelectionRect;
 
+class DisplaySuspender;
+
+namespace ARDOUR_UI_UTILS {
+bool relay_key_press (GdkEventKey* ev, Gtk::Window* win);
+bool forward_key_press (GdkEventKey* ev);
+}
+
 using ARDOUR::framepos_t;
 using ARDOUR::framecnt_t;
 
@@ -266,8 +273,10 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulDestructible, publi
        virtual framecnt_t current_page_samples() const = 0;
        virtual double visible_canvas_height () const = 0;
        virtual void temporal_zoom_step (bool coarser) = 0;
-        virtual void ensure_time_axis_view_is_visible (const TimeAxisView& tav, bool at_top = false) { 
-           _ensure_time_axis_view_is_visible (tav, at_top);
+       /* The virtual version, without a default argument, is protected below.
+        */
+        void ensure_time_axis_view_is_visible (TimeAxisView const & tav, bool at_top = false) { 
+               _ensure_time_axis_view_is_visible (tav, at_top);
        }
         virtual void override_visible_track_count () = 0;
        virtual void scroll_tracks_down_line () = 0;
@@ -318,6 +327,7 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulDestructible, publi
 
        virtual void reset_focus () = 0;
 
+       virtual bool canvas_scroll_event (GdkEventScroll* event, bool from_canvas) = 0;
        virtual bool canvas_control_point_event (GdkEvent* event, ArdourCanvas::Item*, ControlPoint*) = 0;
        virtual bool canvas_line_event (GdkEvent* event, ArdourCanvas::Item*, AutomationLine*) = 0;
        virtual bool canvas_selection_rect_event (GdkEvent* event, ArdourCanvas::Item*, SelectionRect*) = 0;
@@ -326,10 +336,11 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulDestructible, publi
        virtual bool canvas_start_xfade_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*) = 0;
        virtual bool canvas_end_xfade_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*) = 0;
        virtual bool canvas_fade_in_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*) = 0;
-       virtual bool canvas_fade_in_handle_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*) = 0;
+       virtual bool canvas_fade_in_handle_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*, bool) = 0;
        virtual bool canvas_fade_out_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*) = 0;
-       virtual bool canvas_fade_out_handle_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*) = 0;
+       virtual bool canvas_fade_out_handle_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*, bool) = 0;
        virtual bool canvas_region_view_event (GdkEvent* event, ArdourCanvas::Item*, RegionView*) = 0;
+       virtual bool canvas_wave_view_event (GdkEvent* event, ArdourCanvas::Item*, RegionView*) = 0;
        virtual bool canvas_frame_handle_event (GdkEvent* event, ArdourCanvas::Item*, RegionView*) = 0;
        virtual bool canvas_region_view_name_highlight_event (GdkEvent* event, ArdourCanvas::Item*, RegionView*) = 0;
        virtual bool canvas_region_view_name_event (GdkEvent* event, ArdourCanvas::Item*, RegionView*) = 0;
@@ -363,11 +374,11 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulDestructible, publi
        virtual Gtk::HBox& get_status_bar_packer() = 0;
 #endif
 
-       virtual ArdourCanvas::Group* get_trackview_group () const = 0;
-       virtual ArdourCanvas::Group* get_time_bars_group () const = 0;
-       virtual ArdourCanvas::Group* get_track_canvas_group () const = 0;
+       virtual ArdourCanvas::Container* get_trackview_group () const = 0;
+       virtual ArdourCanvas::ScrollGroup* get_hscroll_group () const = 0;
+       virtual ArdourCanvas::ScrollGroup* get_vscroll_group () const = 0;
+       virtual ArdourCanvas::ScrollGroup* get_hvscroll_group () const = 0;
 
-        virtual ArdourCanvas::GtkCanvasViewport* get_time_bars_canvas() const = 0;
         virtual ArdourCanvas::GtkCanvasViewport* get_track_canvas() const = 0;
 
        virtual TimeAxisView* axis_view_from_route (boost::shared_ptr<ARDOUR::Route>) const = 0;
@@ -406,14 +417,38 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulDestructible, publi
 
        static PublicEditor* _instance;
 
-       friend bool relay_key_press (GdkEventKey*, Gtk::Window*);
-       friend bool forward_key_press (GdkEventKey*);
+       friend bool ARDOUR_UI_UTILS::relay_key_press (GdkEventKey*, Gtk::Window*);
+       friend bool ARDOUR_UI_UTILS::forward_key_press (GdkEventKey*);
 
        PBD::Signal0<void> SnapChanged;
        PBD::Signal0<void> MouseModeChanged;
 
   protected:
-       virtual void _ensure_time_axis_view_is_visible (const TimeAxisView& tav, bool at_top) = 0;
+       /* This _ variant of ensure_time_axis_view_is_visible exists because
+          C++ doesn't really like default values for virtual methods. So the
+          public version is non-virtual, with a default value; the virtual
+          (and protected) method here does not have a default value.
+       */
+       virtual void _ensure_time_axis_view_is_visible (TimeAxisView const & tav, bool at_top) = 0;
+
+       friend class DisplaySuspender;
+       virtual void suspend_route_redisplay () = 0;
+       virtual void resume_route_redisplay () = 0;
+       gint _suspend_route_redisplay_counter;
+};
+
+class DisplaySuspender {
+       public:
+               DisplaySuspender() {
+                       if (g_atomic_int_add(&PublicEditor::instance()._suspend_route_redisplay_counter, 1) == 0) {
+                               PublicEditor::instance().suspend_route_redisplay ();
+                       }
+               }
+               ~DisplaySuspender () {
+                       if (g_atomic_int_dec_and_test (&PublicEditor::instance()._suspend_route_redisplay_counter)) {
+                               PublicEditor::instance().resume_route_redisplay ();
+                       }
+               }
 };
 
 #endif // __gtk_ardour_public_editor_h__
index 623a5d8033511bc60183afe5af6333c5e12fb23a..98514326d5e9b6ef7c7a7ea081ef4a8a930876ac 100644 (file)
@@ -57,7 +57,7 @@ QuantizeDialog::QuantizeDialog (PublicEditor& e)
        , swing_adjustment (100.0, -130.0, 130.0, 1.0, 10.0)
        , swing_spinner (swing_adjustment)
        , swing_button (_("Swing"))
-       , threshold_adjustment (0.0, -1920.0, 1920.0, 1.0, 10.0) // XXX MAGIC TICK NUMBER FIX ME
+       , threshold_adjustment (0.0, -Timecode::BBT_Time::ticks_per_beat, Timecode::BBT_Time::ticks_per_beat, 1.0, 10.0)
        , threshold_spinner (threshold_adjustment)
        , threshold_label (_("Threshold (ticks)"))
        , snap_start_button (_("Snap note start"))
index 378057b1b3b67b541a526aef68eda9991eec769e..240b094474c7d427dfdd15695bd90ad2d079fe6b 100644 (file)
@@ -44,6 +44,7 @@
 
 #include "canvas/wave_view.h"
 
+#include "ardour_ui.h"
 #include "ardour_window.h"
 #include "ardour_dialog.h"
 #include "gui_thread.h"
@@ -60,6 +61,7 @@ using namespace Gtk;
 using namespace Gtkmm2ext;
 using namespace PBD;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 
 class ClickOptions : public OptionEditorBox
 {
@@ -1358,6 +1360,7 @@ RCOptionEditor::RCOptionEditor ()
        Gtkmm2ext::UI::instance()->set_tip (tsf->tip_widget(), 
                                            string_compose (_("<b>When enabled</b> %1 will stop recording if an over- or underrun is detected by the audio engine"),
                                                            PROGRAM_NAME));
+       add_option (_("Transport"), tsf);
 
        tsf = new BoolOption (
                     "loop-is-mode",
@@ -1537,6 +1540,14 @@ RCOptionEditor::RCOptionEditor ()
 
        /* EDITOR */
 
+       add_option (S_("Editor"),
+            new BoolOption (
+                    "draggable-playhead",
+                    _("Allow dragging of playhead"),
+                    sigc::mem_fun (*ARDOUR_UI::config(), &UIConfiguration::get_draggable_playhead),
+                    sigc::mem_fun (*ARDOUR_UI::config(), &UIConfiguration::set_draggable_playhead)
+                    ));
+
        add_option (_("Editor"),
             new BoolOption (
                     "link-region-and-track-selection",
@@ -2174,6 +2185,21 @@ RCOptionEditor::RCOptionEditor ()
                            sigc::mem_fun (*_rc_config, &RCConfiguration::set_super_rapid_clock_update)
                            ));
 
+       /* Lock GUI timeout */
+
+       Gtk::Adjustment *lts = manage (new Gtk::Adjustment(0, 0, 1000, 1, 10));
+       HSliderOption *slts = new HSliderOption("lock-gui-after-seconds",
+                                               _("Lock timeout (seconds)"),
+                                               lts,
+                                               sigc::mem_fun (*ARDOUR_UI::config(), &UIConfiguration::get_lock_gui_after_seconds),
+                                               sigc::mem_fun (*ARDOUR_UI::config(), &UIConfiguration::set_lock_gui_after_seconds)
+                       );
+       slts->scale().set_digits (0);
+       Gtkmm2ext::UI::instance()->set_tip
+               (slts->tip_widget(),
+                _("Lock GUI after this many idle seconds (zero to never lock)"));
+       add_option (S_("Preferences|GUI"), slts);
+
        /* The names of these controls must be the same as those given in MixerStrip
           for the actual widgets being controlled.
        */
index 564938ad5c75479704ab3df4e585c9af3bce4154..511314dfc598212ef33cd1542bcbceb6d440aa39 100644 (file)
@@ -33,7 +33,6 @@
 #include "main_clock.h"
 #include "gui_thread.h"
 #include "region_editor.h"
-#include "utils.h"
 
 #include "i18n.h"
 
index 827ebde12d9babf60af2ef7092be4958796b9504..43cd0e5140c3d0f7362d10cbf37c11081d41b6d7 100644 (file)
@@ -27,7 +27,6 @@
 #include "control_point.h"
 #include "region_gain_line.h"
 #include "audio_region_view.h"
-#include "utils.h"
 
 #include "time_axis_view.h"
 #include "editor.h"
@@ -38,7 +37,7 @@ using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 
-AudioRegionGainLine::AudioRegionGainLine (const string & name, AudioRegionView& r, ArdourCanvas::Group& parent, boost::shared_ptr<AutomationList> l)
+AudioRegionGainLine::AudioRegionGainLine (const string & name, AudioRegionView& r, ArdourCanvas::Container& parent, boost::shared_ptr<AutomationList> l)
        : AutomationLine (name, r.get_time_axis_view(), parent, l)
        , rv (r)
 {
index 1eae91bc272ced52c75b05cf5cd97dc316d0f89b..1395bc3f6f07c49cc705a5a843fbbad6c957d2d1 100644 (file)
@@ -35,7 +35,7 @@ class AudioRegionView;
 class AudioRegionGainLine : public AutomationLine
 {
   public:
-       AudioRegionGainLine (const std::string & name, AudioRegionView&, ArdourCanvas::Group& parent, boost::shared_ptr<ARDOUR::AutomationList>);
+       AudioRegionGainLine (const std::string & name, AudioRegionView&, ArdourCanvas::Container& parent, boost::shared_ptr<ARDOUR::AutomationList>);
 
         void start_drag_single (ControlPoint*, double, float);
         void end_drag (bool with_push, uint32_t final_index);
index a7d73afc64a2fc5e479dfbc2e219af124ecb6f59..7a1a2c21405c964e417d657e09da7566abe3b107 100644 (file)
@@ -36,6 +36,7 @@
 using namespace std;
 using namespace Gtk;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 
 RegionLayeringOrderEditor::RegionLayeringOrderEditor (PublicEditor& pe)
        : ArdourWindow (_("RegionLayeringOrderEditor"))
index 127ce79b8462636498e832e890e509a3f3ea2a2d..d45164ee4279e1a17d344c30f9f2046d22996aa4 100644 (file)
@@ -33,6 +33,7 @@
 #include "canvas/pixbuf.h"
 #include "canvas/text.h"
 #include "canvas/line.h"
+#include "canvas/utils.h"
 
 #include "ardour_ui.h"
 #include "global_signals.h"
@@ -53,6 +54,7 @@
 
 using namespace std;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Editing;
 using namespace Gtk;
@@ -62,11 +64,11 @@ static const int32_t sync_mark_width = 9;
 
 PBD::Signal1<void,RegionView*> RegionView::RegionViewGoingAway;
 
-RegionView::RegionView (ArdourCanvas::Group*              parent,
+RegionView::RegionView (ArdourCanvas::Container*              parent,
                         TimeAxisView&                     tv,
                         boost::shared_ptr<ARDOUR::Region> r,
                         double                            spu,
-                        Gdk::Color const &                basic_color,
+                        uint32_t                          basic_color,
                        bool                              automation)
        : TimeAxisViewItem (r->name(), *parent, tv, spu, basic_color, r->position(), r->length(), false, automation,
                            (automation ? TimeAxisViewItem::ShowFrame :
@@ -127,11 +129,11 @@ RegionView::RegionView (const RegionView& other, boost::shared_ptr<Region> other
        GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
 }
 
-RegionView::RegionView (ArdourCanvas::Group*         parent,
+RegionView::RegionView (ArdourCanvas::Container*         parent,
                         TimeAxisView&                tv,
                         boost::shared_ptr<ARDOUR::Region> r,
                         double                       spu,
-                        Gdk::Color const &           basic_color,
+                        uint32_t                     basic_color,
                        bool                         recording,
                         TimeAxisViewItem::Visibility visibility)
        : TimeAxisViewItem (r->name(), *parent, tv, spu, basic_color, r->position(), r->length(), recording, false, visibility)
@@ -152,7 +154,7 @@ RegionView::RegionView (ArdourCanvas::Group*         parent,
 }
 
 void
-RegionView::init (Gdk::Color const & basic_color, bool wfd)
+RegionView::init (bool wfd)
 {
        editor        = 0;
        valid         = true;
@@ -163,8 +165,6 @@ RegionView::init (Gdk::Color const & basic_color, bool wfd)
        sync_mark     = 0;
        sync_line     = 0;
 
-       compute_colors (basic_color);
-
        if (name_highlight) {
                name_highlight->set_data ("regionview", this);
                name_highlight->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_region_view_name_highlight_event), name_highlight, this));
@@ -520,6 +520,18 @@ RegionView::set_duration (framecnt_t frames, void *src)
        return true;
 }
 
+uint32_t
+RegionView::fill_opacity () const
+{
+       if (!_region->opaque()) {
+               return 60;
+       }
+       
+       uint32_t normal_tavi_opacity = TimeAxisViewItem::fill_opacity ();
+
+       return normal_tavi_opacity;
+}
+
 void
 RegionView::set_colors ()
 {
@@ -527,35 +539,17 @@ RegionView::set_colors ()
 
        if (sync_mark) {
                /* XXX: make these colours themable */
-               sync_mark->set_fill_color (RGBA_TO_UINT (0, 255, 0, 255));
-               sync_line->set_outline_color (RGBA_TO_UINT (0, 255, 0, 255));
+               sync_mark->set_fill_color (ArdourCanvas::rgba_to_color (0, 1.0, 0, 1.0));
+               sync_line->set_outline_color (ArdourCanvas::rgba_to_color (0, 1.0, 0, 1.0));
        }
 }
 
 void
 RegionView::set_frame_color ()
 {
-       if (_region->opaque()) {
-               fill_opacity = 130;
-       } else {
-               fill_opacity = 60;
-       }
-
        TimeAxisViewItem::set_frame_color ();
 }
 
-void
-RegionView::fake_set_opaque (bool yn)
-{
-       if (yn) {
-               fill_opacity = 130;
-       } else {
-               fill_opacity = 60;
-       }
-
-       set_frame_color ();
-}
-
 void
 RegionView::show_region_editor ()
 {
@@ -638,11 +632,11 @@ RegionView::region_sync_changed ()
 
                sync_mark = new ArdourCanvas::Polygon (group);
                CANVAS_DEBUG_NAME (sync_mark, string_compose ("sync mark for %1", get_item_name()));
-               sync_mark->set_fill_color (RGBA_TO_UINT(0,255,0,255));    // FIXME make a themeable colour
+               sync_mark->set_fill_color (ArdourCanvas::rgba_to_color (0, 1.0, 0, 1.0));    // FIXME make a themeable colour
 
                sync_line = new ArdourCanvas::Line (group);
                CANVAS_DEBUG_NAME (sync_line, string_compose ("sync mark for %1", get_item_name()));
-               sync_line->set_outline_color (RGBA_TO_UINT(0,255,0,255)); // FIXME make a themeable colour
+               sync_line->set_outline_color (ArdourCanvas::rgba_to_color (0, 1.0, 0, 1.0)); // FIXME make a themeable colour
        }
 
        /* this has to handle both a genuine change of position, a change of samples_per_pixel
@@ -694,7 +688,18 @@ RegionView::move (double x_delta, double y_delta)
                return;
        }
 
-       get_canvas_group()->move (ArdourCanvas::Duple (x_delta, y_delta));
+       /* items will not prevent Item::move() moving
+        * them to a negative x-axis coordinate, which
+        * is legal, but we don't want that here.
+        */
+
+       ArdourCanvas::Item *item = get_canvas_group ();
+       
+       if (item->position().x + x_delta < 0) {
+               x_delta = -item->position().x; /* move it to zero */
+       }
+
+       item->move (ArdourCanvas::Duple (x_delta, y_delta));
 
        /* note: ghosts never leave their tracks so y_delta for them is always zero */
 
@@ -729,12 +734,6 @@ RegionView::remove_ghost (GhostRegion* ghost)
        }
 }
 
-uint32_t
-RegionView::get_fill_color ()
-{
-       return fill_color;
-}
-
 void
 RegionView::set_height (double h)
 {
index a5d5fddbd08acb5d2be6a0b4b82e0264e52a9a04..0837c075f0e3615cd8d593c5b639d63fe3ae23df 100644 (file)
@@ -49,11 +49,11 @@ namespace ArdourCanvas {
 class RegionView : public TimeAxisViewItem
 {
   public:
-       RegionView (ArdourCanvas::Group* parent,
+       RegionView (ArdourCanvas::Container* parent,
                    TimeAxisView&        time_view,
                    boost::shared_ptr<ARDOUR::Region> region,
                    double               samples_per_pixel,
-                   Gdk::Color const &   basic_color,
+                   uint32_t             base_color,
                    bool                 automation = false);
 
        RegionView (const RegionView& other);
@@ -61,7 +61,7 @@ class RegionView : public TimeAxisViewItem
 
        ~RegionView ();
 
-       virtual void init (Gdk::Color const & base_color, bool wait_for_data);
+       virtual void init (bool wait_for_data);
 
        boost::shared_ptr<ARDOUR::Region> region() const { return _region; }
 
@@ -79,7 +79,6 @@ class RegionView : public TimeAxisViewItem
        void lower_to_bottom ();
 
        bool set_position(framepos_t pos, void* src, double* delta = 0);
-       void fake_set_opaque (bool yn);
 
        virtual void show_region_editor ();
        void hide_region_editor ();
@@ -90,8 +89,6 @@ class RegionView : public TimeAxisViewItem
        void remove_ghost_in (TimeAxisView&);
        void remove_ghost (GhostRegion*);
 
-       uint32_t get_fill_color ();
-
        virtual void entered (bool) {}
        virtual void exited () {}
 
@@ -131,11 +128,11 @@ class RegionView : public TimeAxisViewItem
        /** Allows derived types to specify their visibility requirements
         * to the TimeAxisViewItem parent class
         */
-       RegionView (ArdourCanvas::Group *,
+       RegionView (ArdourCanvas::Container *,
                    TimeAxisView&,
                    boost::shared_ptr<ARDOUR::Region>,
                    double samples_per_pixel,
-                   Gdk::Color const & basic_color,
+                   uint32_t basic_color,
                    bool recording,
                    TimeAxisViewItem::Visibility);
 
@@ -157,6 +154,8 @@ class RegionView : public TimeAxisViewItem
        virtual void set_frame_color ();
        virtual void reset_width_dependent_items (double pixel_width);
 
+       uint32_t fill_opacity () const;
+
        virtual void color_handler () {}
 
        boost::shared_ptr<ARDOUR::Region> _region;
index 9e86abd17128a1425e393cdc9efc38cc2e4c8f73..16c4720577579e037514aab841e0e608a0b9dacc 100644 (file)
@@ -23,7 +23,6 @@
 #include "ardour/rc_configuration.h"
 #include "ardour/return.h"
 
-#include "utils.h"
 #include "return_ui.h"
 #include "io_selector.h"
 #include "ardour_ui.h"
index 5a59b5ce5dfc3979e6e06c37e2387626e52f8a83..ec5ebc93a1117d74419e556b506fbac559ab7a83 100644 (file)
@@ -31,7 +31,6 @@
 #include "rhythm_ferret.h"
 #include "audio_region_view.h"
 #include "editor.h"
-#include "utils.h"
 #include "time_axis_view.h"
 
 #include "i18n.h"
index bba72fb4a89c2545a487a5a8905dd71b07f70825..3addc1c5981fa0c07a04b99d20eea8405ec59cc2 100644 (file)
 
 */
 
+#include <iostream>
+
+#include "ardour/route_group.h"
+#include "ardour/session.h"
+
 #include <gtkmm/table.h>
 #include <gtkmm/stock.h>
 #include <gtkmm/messagedialog.h>
-#include "ardour/route_group.h"
-#include "ardour/session.h"
+
 #include "route_group_dialog.h"
 #include "group_tabs.h"
+#include "utils.h"
+
 #include "i18n.h"
-#include <iostream>
 
 using namespace Gtk;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace std;
 using namespace PBD;
 
@@ -82,9 +88,11 @@ RouteGroupDialog::RouteGroupDialog (RouteGroup* g, bool creating_new)
        
        main_vbox->pack_start (*top_vbox, false, false);
 
-       _name.set_text (_group->name ());
        _active.set_active (_group->is_active ());
-       _color.set_color (GroupTabs::group_color (_group));
+
+       Gdk::Color c;
+       set_color_from_rgba (c, GroupTabs::group_color (_group));
+       _color.set_color (c);
 
        VBox* options_box = manage (new VBox);
        options_box->set_spacing (6);
@@ -93,8 +101,6 @@ RouteGroupDialog::RouteGroupDialog (RouteGroup* g, bool creating_new)
        l->set_use_markup ();
        options_box->pack_start (*l, false, true);
 
-       _name.signal_activate ().connect (sigc::bind (sigc::mem_fun (*this, &Dialog::response), RESPONSE_OK));
-
        _gain.set_active (_group->is_gain());
        _relative.set_active (_group->is_relative());
        _mute.set_active (_group->is_mute());
@@ -105,6 +111,18 @@ RouteGroupDialog::RouteGroupDialog (RouteGroup* g, bool creating_new)
        _share_color.set_active (_group->is_color());
        _share_monitoring.set_active (_group->is_monitoring());
 
+       if (_group->name ().empty()) {
+               _initial_name = "1";
+               while (!unique_name (_initial_name)) {
+                       _initial_name = bump_name_number (_initial_name);
+               }
+               _name.set_text (_initial_name);
+               update();
+       } else {
+               _name.set_text (_initial_name);
+       }
+
+       _name.signal_activate ().connect (sigc::bind (sigc::mem_fun (*this, &Dialog::response), RESPONSE_OK));
        _name.signal_changed().connect (sigc::mem_fun (*this, &RouteGroupDialog::update));
        _active.signal_toggled().connect (sigc::mem_fun (*this, &RouteGroupDialog::update));
        _color.signal_color_set().connect (sigc::mem_fun (*this, &RouteGroupDialog::update));
@@ -172,14 +190,14 @@ RouteGroupDialog::do_run ()
                        return Gtk::RESPONSE_CANCEL;
                }
 
-               if (unique_name ()) {
+               if (unique_name (_name.get_text())) {
                        /* not cancelled and the name is ok, so all is well */
                        return false;
                }
 
                _group->set_name (_initial_name);
                MessageDialog msg (
-                       _("A route group of this name already exists.  Please use a different name."),
+                       _("The group name is not unique. Please use a different name."),
                        false,
                        Gtk::MESSAGE_ERROR,
                        Gtk::BUTTONS_OK,
@@ -212,7 +230,7 @@ RouteGroupDialog::update ()
 
        _group->apply_changes (plist);
        
-       GroupTabs::set_group_color (_group, _color.get_color ());
+       GroupTabs::set_group_color (_group, gdk_color_to_rgba (_color.get_color ()));
 }
 
 void
@@ -223,11 +241,12 @@ RouteGroupDialog::gain_toggled ()
 
 /** @return true if the current group's name is unique accross the session */
 bool
-RouteGroupDialog::unique_name () const
+RouteGroupDialog::unique_name (std::string const name) const
 {
+       if (name.empty()) return false; // do not allow empty name, empty means unset.
        list<RouteGroup*> route_groups = _group->session().route_groups ();
        list<RouteGroup*>::iterator i = route_groups.begin ();
-       while (i != route_groups.end() && ((*i)->name() != _name.get_text() || *i == _group)) {
+       while (i != route_groups.end() && ((*i)->name() != name || *i == _group)) {
                ++i;
        }
 
index 6440a7c6a233ad3ffd3d18c377d68d63159d9609..1200175e9992ac559473c484b2682335a1bfc2b9 100644 (file)
@@ -55,7 +55,7 @@ private:
 
        void gain_toggled ();
        void update ();
-       bool unique_name () const;
+       bool unique_name (std::string const name) const;
 };
 
 
index 76f3d4e264770fffcdfbaf80faafc78a911cfe04..f1677c4c2a44375218bdea2859213a586d9594c2 100644 (file)
@@ -25,6 +25,7 @@
 #include <gtkmm2ext/window_title.h>
 
 #include "ardour/audioengine.h"
+#include "ardour/audio_track.h"
 #include "ardour/plugin.h"
 #include "ardour/plugin_insert.h"
 #include "ardour/plugin_manager.h"
@@ -45,7 +46,6 @@
 #include "return_ui.h"
 #include "route_params_ui.h"
 #include "send_ui.h"
-#include "utils.h"
 
 #include "i18n.h"
 
@@ -214,6 +214,25 @@ RouteParams_UI::route_property_changed (const PropertyChange& what_changed, boos
        }
 }
 
+void
+RouteParams_UI::map_frozen()
+{
+       ENSURE_GUI_THREAD (*this, &RouteParams_UI::map_frozen)
+       boost::shared_ptr<AudioTrack> at = boost::dynamic_pointer_cast<AudioTrack>(_route);
+       if (at && insert_box) {
+               switch (at->freeze_state()) {
+                       case AudioTrack::Frozen:
+                               insert_box->set_sensitive (false);
+                               //hide_redirect_editors (); // TODO hide editor windows
+                               break;
+                       default:
+                               insert_box->set_sensitive (true);
+                               // XXX need some way, maybe, to retoggle redirect editors
+                               break;
+               }
+       }
+}
+
 void
 RouteParams_UI::setup_processor_boxes()
 {
@@ -226,6 +245,10 @@ RouteParams_UI::setup_processor_boxes()
                insert_box = new ProcessorBox (_session, boost::bind (&RouteParams_UI::plugin_selector, this), _rr_selection, 0);
                insert_box->set_route (_route);
 
+               boost::shared_ptr<AudioTrack> at = boost::dynamic_pointer_cast<AudioTrack>(_route);
+               if (at) {
+                       at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&RouteParams_UI::map_frozen, this), gui_context());
+               }
                redir_hpane.pack1 (*insert_box);
 
                insert_box->ProcessorSelected.connect (sigc::mem_fun(*this, &RouteParams_UI::redirect_selected));
index 4c8adfd9c66878e099ae09072435502d3c70a7c9..65bef26b5254a2c2aaea49dc41f18f7cdc4a5ac6 100644 (file)
@@ -127,6 +127,7 @@ class RouteParams_UI : public ArdourWindow, public PBD::ScopedConnectionList
 
        boost::shared_ptr<ARDOUR::Route> _route;
        PBD::ScopedConnection _route_processors_connection;
+       PBD::ScopedConnectionList route_connections;
 
        boost::shared_ptr<ARDOUR::Processor> _processor;
        PBD::ScopedConnection _processor_going_away_connection;
@@ -163,6 +164,7 @@ class RouteParams_UI : public ArdourWindow, public PBD::ScopedConnectionList
 
        void route_property_changed (const PBD::PropertyChange&, boost::weak_ptr<ARDOUR::Route> route);
        void route_removed (boost::weak_ptr<ARDOUR::Route> route);
+       void map_frozen ();
 
 
        void route_selected();
index ff898b7895e14ec44afc376d36f6b25cc96c9447..55dbec5f8bc0d186d958dd5360efe17e529d23a1 100644 (file)
@@ -80,6 +80,7 @@
 #include "i18n.h"
 
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtkmm2ext;
 using namespace Gtk;
@@ -107,6 +108,11 @@ RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCan
        , gm (sess, true, 125, 18)
        , _ignore_set_layer_display (false)
 {
+       number_label.set_corner_radius(2);
+       number_label.set_name("tracknumber label");
+       number_label.set_alignment(.5, .5);
+
+       sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
 }
 
 void
@@ -200,6 +206,7 @@ RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
        _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
        _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
        _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
+       _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
 
        controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
 
@@ -222,6 +229,7 @@ RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
                ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
        }
 
+       update_track_number_visibility();
        label_view ();
 
        if (!ARDOUR::Profile->get_trx()) {
@@ -336,12 +344,53 @@ RouteTimeAxisView::playlist_changed ()
 void
 RouteTimeAxisView::label_view ()
 {
-       string x = _route->name();
-
-       if (x != name_label.get_text()) {
+       string x = _route->name ();
+       if (x != name_label.get_text ()) {
                name_label.set_text (x);
        }
+       const int64_t track_number = _route->track_number ();
+       if (track_number == 0) {
+               number_label.set_text ("");
+       } else {
+               number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
+       }
+}
+
+void
+RouteTimeAxisView::update_track_number_visibility ()
+{
+       bool show_label = _session->config.get_track_name_number();
+
+       if (_route && _route->is_master()) {
+               show_label = false;
+       }
 
+       //if (show_label == number_label.is_visible()) { return; }
+       if (number_label.get_parent()) {
+               controls_table.remove (number_label);
+       }
+       if (name_hbox.get_parent()) {
+               controls_table.remove (name_hbox);
+       }
+       if (show_label) {
+               controls_table.attach (number_label, 0, 1, 0, 1,  Gtk::SHRINK, Gtk::FILL|Gtk::EXPAND, 3, 0);
+               controls_table.attach (name_hbox, 1, 5, 0, 1,  Gtk::FILL|Gtk::EXPAND,  Gtk::FILL|Gtk::EXPAND, 3, 0);
+               number_label.set_size_request(3 + _session->track_number_decimals() * 8, -1);
+               name_hbox.show ();
+               number_label.show ();
+       } else {
+               controls_table.attach (name_hbox, 0, 5, 0, 1,  Gtk::FILL|Gtk::EXPAND,  Gtk::FILL|Gtk::EXPAND, 3, 0);
+               name_hbox.show ();
+               number_label.hide ();
+       }
+}
+
+void
+RouteTimeAxisView::parameter_changed (string const & p)
+{
+       if (p == "track-name-number") {
+               update_track_number_visibility();
+       }
 }
 
 void
@@ -710,6 +759,7 @@ RouteTimeAxisView::build_display_menu ()
        } else if (active > 0 && inactive > 0) {
                i->set_inconsistent (true);
        }
+       i->set_sensitive(! _session->transport_rolling());
        i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
 
        items.push_back (SeparatorElem());
index 2d854c451adb714ac6c4083e64bfdbb40a2227f5..3a717f0953dd784f059115987a52d22ed6f92b63 100644 (file)
@@ -252,6 +252,7 @@ protected:
        ArdourButton route_group_button;
        ArdourButton playlist_button;
        ArdourButton automation_button;
+       ArdourButton number_label;
 
        Gtk::Menu           subplugin_menu;
        Gtk::Menu*          automation_action_menu;
@@ -303,6 +304,8 @@ private:
 
        void remove_child (boost::shared_ptr<TimeAxisView>);
        void update_playlist_tip ();
+       void parameter_changed (std::string const & p);
+       void update_track_number_visibility();
 };
 
 #endif /* __ardour_route_time_axis_h__ */
index d8db8b5bd987f09e847aea71d0b1d5bc2aea9614..01b2182e628efe033728899e7233c1642ba322c6 100644 (file)
@@ -58,6 +58,7 @@
 using namespace Gtk;
 using namespace Gtkmm2ext;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 
 uint32_t RouteUI::_max_invert_buttons = 3;
@@ -348,6 +349,7 @@ RouteUI::mute_press (GdkEventButton* ev)
                                                _mute_release->routes = copy;
                                        }
 
+                                       DisplaySuspender ds;
                                        _session->set_mute (copy, !_route->muted());
 
                                } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
@@ -372,6 +374,7 @@ RouteUI::mute_press (GdkEventButton* ev)
                                                        rl->push_back (_route);
                                                }
 
+                                               DisplaySuspender ds;
                                                _session->set_mute (rl, !_route->muted(), Session::rt_cleanup, true);
                                        }
 
@@ -402,6 +405,7 @@ RouteUI::mute_release (GdkEventButton*)
 {
        if (!_i_am_the_modifier) {
                if (_mute_release){
+                       DisplaySuspender ds;
                        _session->set_mute (_mute_release->routes, _mute_release->active, Session::rt_cleanup, true);
                        delete _mute_release;
                        _mute_release = 0;
@@ -460,6 +464,7 @@ RouteUI::solo_press(GdkEventButton* ev)
                                                _solo_release->routes = _session->get_routes ();
                                        }
 
+                                       DisplaySuspender ds;
                                        if (Config->get_solo_control_is_listen_control()) {
                                                _session->set_listen (_session->get_routes(), !_route->listening_via_monitor(),  Session::rt_cleanup, true);
                                        } else {
@@ -487,6 +492,7 @@ RouteUI::solo_press(GdkEventButton* ev)
                                        if (Config->get_solo_control_is_listen_control()) {
                                                /* ??? we need a just_one_listen() method */
                                        } else {
+                                               DisplaySuspender ds;
                                                _session->set_just_one_solo (_route, true);
                                        }
 
@@ -524,6 +530,7 @@ RouteUI::solo_press(GdkEventButton* ev)
                                                        rl->push_back (_route);
                                                }
 
+                                               DisplaySuspender ds;
                                                if (Config->get_solo_control_is_listen_control()) {
                                                        _session->set_listen (rl, !_route->listening_via_monitor(),  Session::rt_cleanup, true);
                                                } else {
@@ -542,6 +549,7 @@ RouteUI::solo_press(GdkEventButton* ev)
                                                _solo_release->routes = rl;
                                        }
 
+                                       DisplaySuspender ds;
                                        if (Config->get_solo_control_is_listen_control()) {
                                                _session->set_listen (rl, !_route->listening_via_monitor());
                                        } else {
@@ -565,11 +573,12 @@ RouteUI::solo_release (GdkEventButton*)
                        if (_solo_release->exclusive) {
 
                        } else {
-                                if (Config->get_solo_control_is_listen_control()) {
-                                        _session->set_listen (_solo_release->routes, _solo_release->active, Session::rt_cleanup, true);
-                                } else {
-                                        _session->set_solo (_solo_release->routes, _solo_release->active, Session::rt_cleanup, true);
-                                }
+                               DisplaySuspender ds;
+                               if (Config->get_solo_control_is_listen_control()) {
+                                       _session->set_listen (_solo_release->routes, _solo_release->active, Session::rt_cleanup, true);
+                               } else {
+                                       _session->set_solo (_solo_release->routes, _solo_release->active, Session::rt_cleanup, true);
+                               }
                        }
 
                        delete _solo_release;
@@ -612,6 +621,7 @@ RouteUI::rec_enable_press(GdkEventButton* ev)
 
                } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
 
+                       DisplaySuspender ds;
                        _session->set_record_enabled (_session->get_routes(), !rec_enable_button->active_state());
 
                } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
@@ -632,7 +642,8 @@ RouteUI::rec_enable_press(GdkEventButton* ev)
                                        rl.reset (new RouteList);
                                        rl->push_back (_route);
                                }
-                               
+
+                               DisplaySuspender ds;
                                _session->set_record_enabled (rl, !rec_enable_button->active_state(), Session::rt_cleanup, true);
                        }
 
@@ -644,6 +655,7 @@ RouteUI::rec_enable_press(GdkEventButton* ev)
 
                        boost::shared_ptr<RouteList> rl (new RouteList);
                        rl->push_back (route());
+                       DisplaySuspender ds;
                        _session->set_record_enabled (rl, !rec_enable_button->active_state());
                }
        }
@@ -761,6 +773,7 @@ RouteUI::monitor_release (GdkEventButton* ev, MonitorChoice monitor_choice)
                rl->push_back (route());
        }
 
+       DisplaySuspender ds;
        _session->set_monitoring (rl, mc, Session::rt_cleanup, true);           
 
        return true;
@@ -1319,36 +1332,38 @@ RouteUI::muting_change ()
 bool
 RouteUI::solo_isolate_button_release (GdkEventButton* ev)
 {
-        if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS) {
-                return true;
-        }
+       if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS) {
+               return true;
+       }
 
-        bool view = solo_isolated_led->active_state();
-        bool model = _route->solo_isolated();
+       bool view = solo_isolated_led->active_state();
+       bool model = _route->solo_isolated();
 
-        /* called BEFORE the view has changed */
+       /* called BEFORE the view has changed */
 
-        if (ev->button == 1) {
-                if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
+       if (ev->button == 1) {
+               if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
 
-                        if (model) {
-                                /* disable isolate for all routes */
-                                _session->set_solo_isolated (_session->get_routes(), false, Session::rt_cleanup, true);
-                        }
+                       if (model) {
+                               /* disable isolate for all routes */
+                               DisplaySuspender ds;
+                               _session->set_solo_isolated (_session->get_routes(), false, Session::rt_cleanup, true);
+                       }
 
-                } else {
-                        if (model == view) {
+               } else {
+                       if (model == view) {
 
-                                /* flip just this route */
+                               /* flip just this route */
 
-                                boost::shared_ptr<RouteList> rl (new RouteList);
-                                rl->push_back (_route);
-                                _session->set_solo_isolated (rl, !view, Session::rt_cleanup, true);
-                        }
-                }
-        }
+                               boost::shared_ptr<RouteList> rl (new RouteList);
+                               rl->push_back (_route);
+                               DisplaySuspender ds;
+                               _session->set_solo_isolated (rl, !view, Session::rt_cleanup, true);
+                       }
+               }
+       }
 
-        return true;
+       return true;
 }
 
 bool
@@ -2015,7 +2030,9 @@ RouteUI::color () const
        RouteGroup* g = _route->route_group ();
        
        if (g && g->is_color()) {
-               return GroupTabs::group_color (g);
+               Gdk::Color c;
+               set_color_from_rgba (c, GroupTabs::group_color (g));
+               return c;
        }
 
        return _color;
diff --git a/gtk2_ardour/ruler_dialog.cc b/gtk2_ardour/ruler_dialog.cc
new file mode 100644 (file)
index 0000000..991bd3e
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+    Copyright (C) 2014 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <gtkmm/stock.h>
+
+#include "actions.h"
+#include "ruler_dialog.h"
+
+#include "i18n.h"
+
+RulerDialog::RulerDialog ()
+       : ArdourDialog (_("Rulers"))
+{
+       add_button (Gtk::Stock::OK, Gtk::RESPONSE_ACCEPT);
+
+       get_vbox()->pack_start (samples_button);
+       get_vbox()->pack_start (timecode_button);
+       get_vbox()->pack_start (minsec_button);
+       get_vbox()->pack_start (bbt_button);
+       get_vbox()->pack_start (tempo_button);
+       get_vbox()->pack_start (meter_button);
+       get_vbox()->pack_start (loop_punch_button);
+       get_vbox()->pack_start (range_button);
+       get_vbox()->pack_start (mark_button);
+       get_vbox()->pack_start (cdmark_button);
+       get_vbox()->pack_start (video_button);
+
+       get_vbox()->show_all ();
+
+       connect_action (samples_button, "samples-ruler");
+       connect_action (timecode_button, "timecode-ruler");
+       connect_action (minsec_button, "minsec-ruler");
+       connect_action (bbt_button, "bbt-ruler");
+       connect_action (tempo_button, "tempo-ruler");
+       connect_action (meter_button, "meter-ruler");
+       connect_action (loop_punch_button, "loop-punch-ruler");
+       connect_action (range_button, "range-ruler");
+       connect_action (mark_button, "marker-ruler");
+       connect_action (cdmark_button, "cd-marker-ruler");
+       connect_action (video_button, "video-ruler");
+}
+
+RulerDialog::~RulerDialog ()
+{
+}
+
+void
+RulerDialog::on_response (int)
+{
+       hide ();
+}
+
+void
+RulerDialog::connect_action (Gtk::CheckButton& button, std::string const &action_name_part)
+{
+       std::string action_name = "toggle-";
+       action_name += action_name_part;
+
+       Glib::RefPtr<Gtk::Action> act = ActionManager::get_action ("Rulers", action_name.c_str());
+       if (!act) {
+               return;
+       }
+
+       Glib::RefPtr<Gtk::ToggleAction> tact = Glib::RefPtr<Gtk::ToggleAction>::cast_dynamic (act);
+       if (!tact) {
+               return;
+       }
+
+       tact->connect_proxy (button);
+}
diff --git a/gtk2_ardour/ruler_dialog.h b/gtk2_ardour/ruler_dialog.h
new file mode 100644 (file)
index 0000000..be1db14
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+    Copyright (C) 2014 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __gtk_ardour_ruler_h__
+#define __gtk_ardour_ruler _h__
+
+#include <string>
+#include <gtkmm/checkbutton.h>
+
+#include "ardour_dialog.h"
+
+
+class Editor;
+
+class RulerDialog : public ArdourDialog
+{
+  public:
+       RulerDialog ();
+       ~RulerDialog ();
+
+  private:
+       Gtk::CheckButton samples_button;
+       Gtk::CheckButton timecode_button;
+       Gtk::CheckButton minsec_button;
+       Gtk::CheckButton bbt_button;
+       Gtk::CheckButton tempo_button;
+       Gtk::CheckButton meter_button;
+       Gtk::CheckButton loop_punch_button;
+       Gtk::CheckButton range_button;
+       Gtk::CheckButton mark_button;
+       Gtk::CheckButton cdmark_button;
+       Gtk::CheckButton video_button;
+
+       void connect_action (Gtk::CheckButton& button, std::string const &action_name_part);
+       void on_response (int);
+};
+
+#endif /* __gtk_ardour_add_route_dialog_h__ */
index 7e3b0e775a932471c2b33087da5021165c546477..d571d018b73a705ef3d74fa7b77b22ebfa4c5a3d 100644 (file)
@@ -24,7 +24,6 @@
 #include "ardour/send.h"
 #include "ardour/rc_configuration.h"
 
-#include "utils.h"
 #include "send_ui.h"
 #include "io_selector.h"
 #include "ardour_ui.h"
index 996dab1cc4144f21eb93282b173fcfd3018938f1..7d88721eb0d61968207e8c9db8a3016a6c867d0a 100644 (file)
@@ -31,6 +31,7 @@
 #include "pbd/replace_all.h"
 #include "pbd/whitespace.h"
 #include "pbd/stacktrace.h"
+#include "pbd/stl_delete.h"
 #include "pbd/openuri.h"
 
 #include "ardour/audioengine.h"
@@ -54,6 +55,7 @@ using namespace Gdk;
 using namespace Glib;
 using namespace PBD;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 
 static string poor_mans_glob (string path)
 {
@@ -248,7 +250,9 @@ SessionDialog::session_folder ()
                /* existing session chosen from file chooser */
                return Glib::path_get_dirname (existing_session_chooser.get_current_folder ());
        } else {
-               std::string legal_session_folder_name = legalize_for_path (new_name_entry.get_text());
+               std::string val = new_name_entry.get_text();
+               strip_whitespace_edges (val);
+               std::string legal_session_folder_name = legalize_for_path (val);
                return Glib::build_filename (new_folder_chooser.get_current_folder(), legal_session_folder_name);
        }
 }
@@ -277,7 +281,7 @@ SessionDialog::setup_initial_choice_box ()
 
        string image_path;
 
-       if (find_file_in_search_path (ardour_data_search_path(), "small-splash.png", image_path)) {
+       if (find_file (ardour_data_search_path(), "small-splash.png", image_path)) {
                Gtk::Image* image;
                if ((image = manage (new Gtk::Image (image_path))) != 0) {
                        hbox->pack_start (*image, false, false);
@@ -340,7 +344,7 @@ SessionDialog::setup_initial_choice_box ()
        recent_session_display.signal_row_activated().connect (sigc::mem_fun (*this, &SessionDialog::recent_row_activated));
        
        centering_vbox->pack_start (recent_label, false, false, 12);
-       centering_vbox->pack_start (recent_scroller, false, true);
+       centering_vbox->pack_start (recent_scroller, true, true);
 
        /* Browse button */
        
@@ -618,7 +622,7 @@ SessionDialog::redisplay_recent_sessions ()
 
                get_state_files_in_directory (*i, state_file_paths);
 
-               vector<string*>* states;
+               vector<string> states;
                vector<const gchar*> item;
                string dirname = *i;
 
@@ -636,7 +640,9 @@ SessionDialog::redisplay_recent_sessions ()
 
                /* now get available states for this session */
 
-               if ((states = Session::possible_states (dirname)) == 0) {
+               states = Session::possible_states (dirname);
+
+               if (states.empty()) {
                        /* no state file? */
                        continue;
                }
@@ -651,9 +657,10 @@ SessionDialog::redisplay_recent_sessions ()
 
                float sr;
                SampleFormat sf;
-               std::string s = Glib::build_filename (dirname, state_file_names.front() + statefile_suffix);
+               std::string state_file_basename = state_file_names.front();
+
+               std::string s = Glib::build_filename (dirname, state_file_basename + statefile_suffix);
 
-               row[recent_session_columns.visible_name] = Glib::path_get_basename (dirname);
                row[recent_session_columns.fullpath] = dirname; /* just the dir, but this works too */
                row[recent_session_columns.tip] = Glib::Markup::escape_text (dirname);
 
@@ -678,9 +685,13 @@ SessionDialog::redisplay_recent_sessions ()
                ++session_snapshot_count;
 
                if (state_file_names.size() > 1) {
+                       // multiple session files in the session directory - show the directory name.
+                       // if there's not a session file with the same name as the session directory,
+                       // opening the parent item will fail, but expanding it will show the session
+                       // files that actually exist, and the right one can then be opened.
+                       row[recent_session_columns.visible_name] = Glib::path_get_basename (dirname);
 
                        // add the children
-
                        for (std::vector<std::string>::iterator i2 = state_file_names.begin(); i2 != state_file_names.end(); ++i2) {
 
                                Gtk::TreeModel::Row child_row = *(recent_session_model->append (row.children()));
@@ -710,6 +721,9 @@ SessionDialog::redisplay_recent_sessions ()
 
                                ++session_snapshot_count;
                        }
+               } else {
+                       // only a single session file in the directory - show its actual name.
+                       row[recent_session_columns.visible_name] = state_file_basename;
                }
        }
 
@@ -1034,6 +1048,7 @@ void
 SessionDialog::existing_session_selected ()
 {
        _existing_session_chooser_used = true;
+       recent_session_display.get_selection()->unselect_all();
        /* mark this sensitive in case we come back here after a failed open
         * attempt and the user has hacked up the fix. sigh.
         */
index 2178cfbe7364393778291a80ba83011f07b1d3a4..ef3717b778cbecaff676a95735fc0f1e7b350ef1 100644 (file)
@@ -104,7 +104,7 @@ class SessionDialog : public ArdourDialog {
 
        struct RecentSessionsSorter {
            bool operator() (std::pair<std::string,std::string> a, std::pair<std::string,std::string> b) const {
-                   return cmp_nocase(a.first, b.first) == -1;
+                   return ARDOUR::cmp_nocase(a.first, b.first) == -1;
            }
        };
 
index 9560fddc7902adf547e41dddaddce8688c455a7c..d8326a09cc8e71fb3f25088a1cdf8bcbaa5bcf29 100644 (file)
@@ -132,18 +132,6 @@ SessionOptionEditor::SessionOptionEditor (Session* s)
 
        /* FADES */
 
-       ComboOption<CrossfadeChoice>* cfc = new ComboOption<CrossfadeChoice> (
-               "xfade-choice",
-               _("Default crossfade type"),
-               sigc::mem_fun (*_session_config, &SessionConfiguration::get_xfade_choice),
-               sigc::mem_fun (*_session_config, &SessionConfiguration::set_xfade_choice)
-               );
-
-       cfc->add (ConstantPowerMinus3dB, _("Constant power (-3dB) crossfade"));
-       cfc->add (ConstantPowerMinus6dB, _("Linear (-6dB) crossfade"));
-
-       add_option (_("Fades"), cfc);
-
        add_option (_("Fades"), new SpinOption<float> (
                _("destructive-xfade-seconds"),
                _("Destructive crossfade length"),
@@ -198,20 +186,57 @@ SessionOptionEditor::SessionOptionEditor (Session* s)
 
        add_option (_("Media"), hf);
 
-       add_option (_("Media"), new OptionEditorHeading (_("File locations")));
+       add_option (_("Locations"), new OptionEditorHeading (_("File locations")));
 
         SearchPathOption* spo = new SearchPathOption ("audio-search-path", _("Search for audio files in:"),
                                                      _session->path(),
                                                       sigc::mem_fun (*_session_config, &SessionConfiguration::get_audio_search_path),
                                                       sigc::mem_fun (*_session_config, &SessionConfiguration::set_audio_search_path));
-        add_option (_("Media"), spo);
+        add_option (_("Locations"), spo);
 
         spo = new SearchPathOption ("midi-search-path", _("Search for MIDI files in:"),
                                    _session->path(),
                                     sigc::mem_fun (*_session_config, &SessionConfiguration::get_midi_search_path),
                                     sigc::mem_fun (*_session_config, &SessionConfiguration::set_midi_search_path));
 
-        add_option (_("Media"), spo);
+        add_option (_("Locations"), spo);
+
+       /* File Naming  */
+
+       add_option (_("Filenames"), new OptionEditorHeading (_("File Naming")));
+
+       BoolOption *bo;
+
+       bo = new BoolOption (
+                       "track-name-number",
+                       _("Prefix Track number"),
+                       sigc::mem_fun (*_session_config, &SessionConfiguration::get_track_name_number),
+                       sigc::mem_fun (*_session_config, &SessionConfiguration::set_track_name_number)
+                       );
+       Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
+                       _("Adds the current track number to the beginning of the recorded file name."));
+       add_option (_("Filenames"), bo);
+
+       bo = new BoolOption (
+                       "track-name-take",
+                       _("Prefix Take Name"),
+                       sigc::mem_fun (*_session_config, &SessionConfiguration::get_track_name_take),
+                       sigc::mem_fun (*_session_config, &SessionConfiguration::set_track_name_take)
+                       );
+       Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
+                       _("Adds the Take Name to the beginning of the recorded file name."));
+       add_option (_("Filenames"), bo);
+
+       _take_name = new EntryOption (
+               "take-name",
+               _("Take Name"),
+               sigc::mem_fun (*_session_config, &SessionConfiguration::get_take_name),
+               sigc::mem_fun (*_session_config, &SessionConfiguration::set_take_name)
+               );
+       _take_name->set_invalid_chars(".");
+       _take_name->set_sensitive(_session_config->get_track_name_take());
+
+       add_option (_("Filenames"), _take_name);
 
        /* Monitoring */
 
@@ -228,50 +253,7 @@ SessionOptionEditor::SessionOptionEditor (Session* s)
                            sigc::mem_fun (*this, &SessionOptionEditor::get_use_monitor_section),
                            sigc::mem_fun (*this, &SessionOptionEditor::set_use_monitor_section)
                            ));
-
-        /* Misc */
-
-       add_option (_("Misc"), new OptionEditorHeading (_("MIDI Options")));
-
-        add_option (_("Misc"), new BoolOption (
-                           "midi-copy-is-fork",
-                           _("MIDI region copies are independent"),
-                           sigc::mem_fun (*_session_config, &SessionConfiguration::get_midi_copy_is_fork),
-                           sigc::mem_fun (*_session_config, &SessionConfiguration::set_midi_copy_is_fork)
-                           ));
-
-       ComboOption<InsertMergePolicy>* li = new ComboOption<InsertMergePolicy> (
-               "insert-merge-policy",
-               _("Policy for handling overlapping notes\n on the same MIDI channel"),
-               sigc::mem_fun (*_session_config, &SessionConfiguration::get_insert_merge_policy),
-               sigc::mem_fun (*_session_config, &SessionConfiguration::set_insert_merge_policy)
-               );
-
-        li->add (InsertMergeReject, _("never allow them"));
-        li->add (InsertMergeRelax, _("don't do anything in particular"));
-        li->add (InsertMergeReplace, _("replace any overlapped existing note"));
-        li->add (InsertMergeTruncateExisting, _("shorten the overlapped existing note"));
-        li->add (InsertMergeTruncateAddition, _("shorten the overlapping new note"));
-        li->add (InsertMergeExtend, _("replace both overlapping notes with a single note"));
-
-       add_option (_("Misc"), li);
-
-       add_option (_("Misc"), new OptionEditorHeading (_("Glue to bars and beats")));
-
-       add_option (_("Misc"), new BoolOption (
-                           "glue-new-markers-to-bars-and-beats",
-                           _("Glue new markers to bars and beats"),
-                           sigc::mem_fun (*_session_config, &SessionConfiguration::get_glue_new_markers_to_bars_and_beats),
-                           sigc::mem_fun (*_session_config, &SessionConfiguration::set_glue_new_markers_to_bars_and_beats)
-                           ));
-
-       add_option (_("Misc"), new BoolOption (
-                           "glue-new-regions-to-bars-and-beats",
-                           _("Glue new regions to bars and beats"),
-                           sigc::mem_fun (*_session_config, &SessionConfiguration::get_glue_new_regions_to_bars_and_beats),
-                           sigc::mem_fun (*_session_config, &SessionConfiguration::set_glue_new_regions_to_bars_and_beats)
-                           ));
-
+       /* Meterbridge */
        add_option (_("Meterbridge"), new OptionEditorHeading (_("Route Display")));
 
        add_option (_("Meterbridge"), new BoolOption (
@@ -334,6 +316,55 @@ SessionOptionEditor::SessionOptionEditor (Session* s)
                            sigc::mem_fun (*_session_config, &SessionConfiguration::set_show_name_on_meterbridge)
                            ));
 
+       /* Misc */
+
+       add_option (_("Misc"), new OptionEditorHeading (_("MIDI Options")));
+
+       add_option (_("Misc"), new BoolOption (
+                               "midi-copy-is-fork",
+                               _("MIDI region copies are independent"),
+                               sigc::mem_fun (*_session_config, &SessionConfiguration::get_midi_copy_is_fork),
+                               sigc::mem_fun (*_session_config, &SessionConfiguration::set_midi_copy_is_fork)
+                               ));
+
+       ComboOption<InsertMergePolicy>* li = new ComboOption<InsertMergePolicy> (
+                       "insert-merge-policy",
+                       _("Policy for handling overlapping notes\n on the same MIDI channel"),
+                       sigc::mem_fun (*_session_config, &SessionConfiguration::get_insert_merge_policy),
+                       sigc::mem_fun (*_session_config, &SessionConfiguration::set_insert_merge_policy)
+                       );
+
+       li->add (InsertMergeReject, _("never allow them"));
+       li->add (InsertMergeRelax, _("don't do anything in particular"));
+       li->add (InsertMergeReplace, _("replace any overlapped existing note"));
+       li->add (InsertMergeTruncateExisting, _("shorten the overlapped existing note"));
+       li->add (InsertMergeTruncateAddition, _("shorten the overlapping new note"));
+       li->add (InsertMergeExtend, _("replace both overlapping notes with a single note"));
+
+       add_option (_("Misc"), li);
+
+       add_option (_("Misc"), new OptionEditorHeading (_("Glue to bars and beats")));
+
+       add_option (_("Misc"), new BoolOption (
+                               "glue-new-markers-to-bars-and-beats",
+                               _("Glue new markers to bars and beats"),
+                               sigc::mem_fun (*_session_config, &SessionConfiguration::get_glue_new_markers_to_bars_and_beats),
+                               sigc::mem_fun (*_session_config, &SessionConfiguration::set_glue_new_markers_to_bars_and_beats)
+                               ));
+
+       add_option (_("Misc"), new BoolOption (
+                               "glue-new-regions-to-bars-and-beats",
+                               _("Glue new regions to bars and beats"),
+                               sigc::mem_fun (*_session_config, &SessionConfiguration::get_glue_new_regions_to_bars_and_beats),
+                               sigc::mem_fun (*_session_config, &SessionConfiguration::set_glue_new_regions_to_bars_and_beats)
+                               ));
+
+       add_option (_("Misc"), new OptionEditorHeading (_("Defaults")));
+
+       Gtk::Button* btn = Gtk::manage (new Gtk::Button (_("Use these settings as defaults")));
+       btn->signal_clicked().connect (sigc::mem_fun (*this, &SessionOptionEditor::save_defaults));
+       add_option (_("Misc"), new FooOption (btn));
+
 }
 
 void
@@ -347,11 +378,14 @@ SessionOptionEditor::parameter_changed (std::string const & p)
                        _vpu->set_sensitive(true);
                }
        }
-       if (p == "timecode-format") {
+       else if (p == "timecode-format") {
                /* update offset clocks */
                parameter_changed("timecode-generator-offset");
                parameter_changed("slave-timecode-offset");
        }
+       else if (p == "track-name-take") {
+               _take_name->set_sensitive(_session_config->get_track_name_take());
+       }
 }
 
 /* the presence of absence of a monitor section is not really a regular session
@@ -381,3 +415,9 @@ SessionOptionEditor::get_use_monitor_section ()
 {
        return _session->monitor_out() != 0;
 }
+
+void
+SessionOptionEditor::save_defaults ()
+{
+       _session->save_default_options();
+}
index 2e7ba192b6dfb4fabc42e4a381ab83c582e1e6e9..425ec3707bcf40efcb06e40869c656cab74eaaa5 100644 (file)
@@ -41,6 +41,9 @@ private:
        bool get_use_monitor_section ();
 
        ComboOption<float>* _vpu;
+       EntryOption* _take_name;
+
+       void save_defaults ();
 };
 
 #endif /* __gtk_ardour_session_option_editor_h__ */
index e1501460cadd682e0c762c58d1cc78b0b29c7a72..c02059ba72b333a9c8a847af3f40a2a4ce2b8e0c 100644 (file)
-/* sfdb_freesound_mootcher.cpp **********************************************************************\r
-\r
-       Adapted for Ardour by Ben Loftis, March 2008\r
-       Updated to new Freesound API by Colin Fletcher, November 2011\r
-\r
-       Mootcher 23-8-2005\r
-\r
-       Mootcher Online Access to thefreesoundproject website\r
-       http://freesound.iua.upf.edu/\r
-\r
-       GPL 2005 Jorn Lemon\r
-       mail for questions/remarks: mootcher@twistedlemon.nl\r
-       or go to the freesound website forum\r
-\r
-       -----------------------------------------------------------------\r
-\r
-       Includes:\r
-               curl.h    (version 7.14.0)\r
-       Librarys:\r
-               libcurl.lib\r
-\r
-       -----------------------------------------------------------------\r
-       Licence GPL:\r
-\r
-       This program is free software; you can redistribute it and/or\r
-       modify it under the terms of the GNU General Public License\r
-       as published by the Free Software Foundation; either version 2\r
-       of the License, or (at your option) any later version.\r
-\r
-       This program is distributed in the hope that it will be useful,\r
-       but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-       GNU General Public License for more details.\r
-\r
-       You should have received a copy of the GNU General Public License\r
-       along with this program; if not, write to the Free Software\r
-       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\r
-\r
-\r
-*************************************************************************************/\r
-#include "sfdb_freesound_mootcher.h"\r
-\r
-#include "pbd/xml++.h"\r
-#include "pbd/error.h"\r
-\r
-#include <sys/stat.h>\r
-#include <sys/types.h>\r
-#include <iostream>\r
-\r
-#include <glib.h>\r
-#include <glib/gstdio.h>\r
-\r
-#include "i18n.h"\r
-\r
-#include "ardour/audio_library.h"\r
-#include "ardour/rc_configuration.h"\r
-#include "pbd/pthread_utils.h"\r
-#include "gui_thread.h"\r
-\r
-using namespace PBD;\r
-\r
-static const std::string base_url = "http://www.freesound.org/api";\r
-static const std::string api_key = "9d77cb8d841b4bcfa960e1aae62224eb"; // ardour3\r
-\r
-//------------------------------------------------------------------------\r
-Mootcher::Mootcher()\r
-       : curl(curl_easy_init())\r
-{\r
-       cancel_download_btn.set_label (_("Cancel"));\r
-       progress_hbox.pack_start (progress_bar, true, true);\r
-       progress_hbox.pack_end (cancel_download_btn, false, false);\r
-       progress_bar.show();\r
-       cancel_download_btn.show();\r
-       cancel_download_btn.signal_clicked().connect(sigc::mem_fun (*this, &Mootcher::cancelDownload));\r
-};\r
-//------------------------------------------------------------------------\r
-Mootcher:: ~Mootcher()\r
-{\r
-       curl_easy_cleanup(curl);\r
-}\r
-\r
-//------------------------------------------------------------------------\r
-\r
-void Mootcher::ensureWorkingDir ()\r
-{\r
-       std::string p = ARDOUR::Config->get_freesound_download_dir();\r
-\r
-       if (!Glib::file_test (p, Glib::FILE_TEST_IS_DIR)) {\r
-               if (g_mkdir_with_parents (p.c_str(), 0775) != 0) {\r
-                       PBD::error << "Unable to create Mootcher working dir" << endmsg;\r
-               }\r
-       }\r
-       basePath = p;\r
-#ifdef PLATFORM_WINDOWS\r
-       std::string replace = "/";\r
-       size_t pos = basePath.find("\\");\r
-       while( pos != std::string::npos ){\r
-               basePath.replace(pos, 1, replace);\r
-               pos = basePath.find("\\");\r
-       }\r
-#endif\r
-}\r
-       \r
-\r
-//------------------------------------------------------------------------\r
-size_t Mootcher::WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)\r
-{\r
-       register int realsize = (int)(size * nmemb);\r
-       struct MemoryStruct *mem = (struct MemoryStruct *)data;\r
-\r
-       mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);\r
-\r
-       if (mem->memory) {\r
-               memcpy(&(mem->memory[mem->size]), ptr, realsize);\r
-               mem->size += realsize;\r
-               mem->memory[mem->size] = 0;\r
-       }\r
-       return realsize;\r
-}\r
-\r
-\r
-//------------------------------------------------------------------------\r
-\r
-std::string Mootcher::sortMethodString(enum sortMethod sort)\r
-{\r
-// given a sort type, returns the string value to be passed to the API to\r
-// sort the results in the requested way.\r
-\r
-       switch (sort) {\r
-               case sort_duration_desc:        return "duration_desc"; \r
-               case sort_duration_asc:         return "duration_asc";\r
-               case sort_created_desc:         return "created_desc";\r
-               case sort_created_asc:          return "created_asc";\r
-               case sort_downloads_desc:       return "downloads_desc";\r
-               case sort_downloads_asc:        return "downloads_asc";\r
-               case sort_rating_desc:          return "rating_desc";\r
-               case sort_rating_asc:           return "rating_asc";\r
-               default:                        return "";      \r
-       }\r
-}\r
-\r
-//------------------------------------------------------------------------\r
-void Mootcher::setcUrlOptions()\r
-{\r
-       // basic init for curl\r
-       curl_global_init(CURL_GLOBAL_ALL);\r
-       // some servers don't like requests that are made without a user-agent field, so we provide one\r
-       curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");\r
-       // setup curl error buffer\r
-       curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer);\r
-       // Allow redirection\r
-       curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);\r
-       \r
-       // Allow connections to time out (without using signals)\r
-       curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);\r
-       curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30);\r
-\r
-\r
-}\r
-\r
-std::string Mootcher::doRequest(std::string uri, std::string params)\r
-{\r
-       std::string result;\r
-       struct MemoryStruct xml_page;\r
-       xml_page.memory = NULL;\r
-       xml_page.size = 0;\r
-\r
-       setcUrlOptions();\r
-       curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);\r
-       curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &xml_page);\r
-\r
-       // curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);\r
-       // curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postMessage.c_str());\r
-       // curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1);\r
-\r
-       // the url to get\r
-       std::string url = base_url + uri + "?";\r
-       if (params != "") {\r
-               url += params + "&api_key=" + api_key + "&format=xml";\r
-       } else {\r
-               url += "api_key=" + api_key + "&format=xml";\r
-       }\r
-               \r
-       curl_easy_setopt(curl, CURLOPT_URL, url.c_str() );\r
-       \r
-       // perform online request\r
-       CURLcode res = curl_easy_perform(curl);\r
-       if( res != 0 ) {\r
-               error << string_compose (_("curl error %1 (%2)"), res, curl_easy_strerror(res)) << endmsg;\r
-               return "";\r
-       }\r
-\r
-       // free the memory\r
-       if (xml_page.memory) {\r
-               result = xml_page.memory;\r
-       }\r
-       \r
-       free (xml_page.memory);\r
-       xml_page.memory = NULL;\r
-       xml_page.size = 0;\r
-\r
-       return result;\r
-}\r
-\r
-\r
-std::string Mootcher::searchSimilar(std::string id)\r
-{\r
-       std::string params = "";\r
-\r
-       params += "&fields=id,original_filename,duration,filesize,samplerate,license,serve";\r
-       params += "&num_results=100";\r
-\r
-       return doRequest("/sounds/" + id + "/similar", params);\r
-}\r
-\r
-//------------------------------------------------------------------------\r
-\r
-std::string Mootcher::searchText(std::string query, int page, std::string filter, enum sortMethod sort)\r
-{\r
-       std::string params = "";\r
-       char buf[24];\r
-\r
-       if (page > 1) {\r
-               snprintf(buf, 23, "p=%d&", page);\r
-               params += buf;\r
-       }\r
-\r
-       char *eq = curl_easy_escape(curl, query.c_str(), query.length());\r
-       params += "q=\"" + std::string(eq) + "\"";\r
-       free(eq);\r
-\r
-       if (filter != "") {\r
-               char *ef = curl_easy_escape(curl, filter.c_str(), filter.length());\r
-               params += "&f=" + std::string(ef);\r
-               free(ef);\r
-       }\r
-       \r
-       if (sort)\r
-               params += "&s=" + sortMethodString(sort);\r
-\r
-       params += "&fields=id,original_filename,duration,filesize,samplerate,license,serve";\r
-       params += "&sounds_per_page=100";\r
-\r
-       return doRequest("/sounds/search", params);\r
-}\r
-\r
-//------------------------------------------------------------------------\r
-\r
-std::string Mootcher::getSoundResourceFile(std::string ID)\r
-{\r
-\r
-       std::string originalSoundURI;\r
-       std::string audioFileName;\r
-       std::string xml;\r
-\r
-\r
-       // download the xmlfile into xml_page\r
-       xml = doRequest("/sounds/" + ID, "");\r
-\r
-       XMLTree doc;\r
-       doc.read_buffer( xml.c_str() );\r
-       XMLNode *freesound = doc.root();\r
-\r
-       // if the page is not a valid xml document with a 'freesound' root\r
-       if (freesound == NULL) {\r
-               error << _("getSoundResourceFile: There is no valid root in the xml file") << endmsg;\r
-               return "";\r
-       }\r
-\r
-       if (strcmp(doc.root()->name().c_str(), "response") != 0) {\r
-               error << string_compose (_("getSoundResourceFile: root = %1, != response"), doc.root()->name()) << endmsg;\r
-               return "";\r
-       }\r
-\r
-       XMLNode *name = freesound->child("original_filename");\r
-\r
-       // get the file name and size from xml file\r
-       if (name) {\r
-\r
-               audioFileName = Glib::build_filename (basePath, ID + "-" + name->child("text")->content());\r
-\r
-               //store all the tags in the database\r
-               XMLNode *tags = freesound->child("tags");\r
-               if (tags) {\r
-                       XMLNodeList children = tags->children();\r
-                       XMLNodeConstIterator niter;\r
-                       std::vector<std::string> strings;\r
-                       for (niter = children.begin(); niter != children.end(); ++niter) {\r
-                               XMLNode *node = *niter;\r
-                               if( strcmp( node->name().c_str(), "resource") == 0 ) {\r
-                                       XMLNode *text = node->child("text");\r
-                                       if (text) {\r
-                                               // std::cerr << "tag: " << text->content() << std::endl;\r
-                                               strings.push_back(text->content());\r
-                                       }\r
-                               }\r
-                       }\r
-                       ARDOUR::Library->set_tags (std::string("//")+audioFileName, strings);\r
-                       ARDOUR::Library->save_changes ();\r
-               }\r
-       }\r
-\r
-       return audioFileName;\r
-}\r
-\r
-int audioFileWrite(void *buffer, size_t size, size_t nmemb, void *file)\r
-{\r
-       return (int)fwrite(buffer, size, nmemb, (FILE*) file);\r
-};\r
-\r
-//------------------------------------------------------------------------\r
-\r
-void *\r
-Mootcher::threadFunc() {\r
-CURLcode res;\r
-\r
-       res = curl_easy_perform (curl);\r
-       fclose (theFile);\r
-       curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1); // turn off the progress bar\r
-       \r
-       if (res != CURLE_OK) {\r
-               /* it's not an error if the user pressed the stop button */\r
-               if (res != CURLE_ABORTED_BY_CALLBACK) {\r
-                       error <<  string_compose (_("curl error %1 (%2)"), res, curl_easy_strerror(res)) << endmsg;\r
-               }\r
-               remove ( (audioFileName+".part").c_str() );  \r
-       } else {\r
-               rename ( (audioFileName+".part").c_str(), audioFileName.c_str() );\r
-               // now download the tags &c.\r
-               getSoundResourceFile(ID);\r
-       }\r
-\r
-       return (void *) res;\r
-}\r
\r
-void\r
-Mootcher::doneWithMootcher()\r
-{\r
-\r
-       // update the sound info pane if the selection in the list box is still us \r
-       sfb->refresh_display(ID, audioFileName);\r
-\r
-       delete this; // this should be OK to do as long as Progress and Finished signals are always received in the order in which they are emitted\r
-}\r
-\r
-static void *\r
-freesound_download_thread_func(void *arg) \r
-{ \r
-       Mootcher *thisMootcher = (Mootcher *) arg;\r
-       void *res;\r
-\r
-       // std::cerr << "freesound_download_thread_func(" << arg << ")" << std::endl;\r
-       res = thisMootcher->threadFunc();\r
-\r
-       thisMootcher->Finished(); /* EMIT SIGNAL */\r
-       return res;\r
-}\r
-\r
-\r
-//------------------------------------------------------------------------\r
-\r
-bool Mootcher::checkAudioFile(std::string originalFileName, std::string theID)\r
-{\r
-       ensureWorkingDir();\r
-       ID = theID;\r
-       audioFileName = Glib::build_filename (basePath, ID + "-" + originalFileName);\r
-\r
-       // check to see if audio file already exists\r
-       FILE *testFile = g_fopen(audioFileName.c_str(), "r");\r
-       if (testFile) {  \r
-               fseek (testFile , 0 , SEEK_END);\r
-               if (ftell (testFile) > 256) {\r
-                       fclose (testFile);\r
-                       return true;\r
-               }\r
-               \r
-               // else file was small, probably an error, delete it \r
-               fclose(testFile);\r
-               remove( audioFileName.c_str() );  \r
-       }\r
-       return false;\r
-}\r
-\r
-\r
-bool Mootcher::fetchAudioFile(std::string originalFileName, std::string theID, std::string audioURL, SoundFileBrowser *caller)\r
-{\r
-       ensureWorkingDir();\r
-       ID = theID;\r
-       audioFileName = Glib::build_filename (basePath, ID + "-" + originalFileName);\r
-\r
-       if (!curl) {\r
-               return false;\r
-       }\r
-       // now download the actual file\r
-       theFile = g_fopen( (audioFileName + ".part").c_str(), "wb" );\r
-\r
-       if (!theFile) {\r
-               return false;\r
-       }\r
-       \r
-       // create the download url\r
-       audioURL += "?api_key=" + api_key;\r
-\r
-       setcUrlOptions();\r
-       curl_easy_setopt(curl, CURLOPT_URL, audioURL.c_str() );\r
-       curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, audioFileWrite);\r
-       curl_easy_setopt(curl, CURLOPT_WRITEDATA, theFile);\r
-\r
-       std::string prog;\r
-       prog = string_compose (_("%1"), originalFileName);\r
-       progress_bar.set_text(prog);\r
-\r
-       Gtk::VBox *freesound_vbox = dynamic_cast<Gtk::VBox *> (caller->notebook.get_nth_page(2));\r
-       freesound_vbox->pack_start(progress_hbox, Gtk::PACK_SHRINK);\r
-       progress_hbox.show();\r
-       cancel_download = false;\r
-       sfb = caller;\r
-\r
-       curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0); // turn on the progress bar\r
-       curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_callback);\r
-       curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, this);\r
-\r
-       Progress.connect(*this, invalidator (*this), boost::bind(&Mootcher::updateProgress, this, _1, _2), gui_context());\r
-       Finished.connect(*this, invalidator (*this), boost::bind(&Mootcher::doneWithMootcher, this), gui_context());\r
-       pthread_t freesound_download_thread;\r
-       pthread_create_and_store("freesound_import", &freesound_download_thread, freesound_download_thread_func, this);\r
-\r
-       return true;\r
-}\r
-\r
-//---------\r
-\r
-void \r
-Mootcher::updateProgress(double dlnow, double dltotal) \r
-{\r
-       if (dltotal > 0) {\r
-               double fraction = dlnow / dltotal;\r
-               // std::cerr << "progress idle: " << progress->bar->get_text() << ". " << progress->dlnow << " / " << progress->dltotal << " = " << fraction << std::endl;\r
-               if (fraction > 1.0) {\r
-                       fraction = 1.0;\r
-               } else if (fraction < 0.0) {\r
-                       fraction = 0.0;\r
-               }\r
-               progress_bar.set_fraction(fraction);\r
-       }\r
-}\r
-\r
-int \r
-Mootcher::progress_callback(void *caller, double dltotal, double dlnow, double /*ultotal*/, double /*ulnow*/)\r
-{\r
-       // It may seem curious to pass a pointer to an instance of an object to a static\r
-       // member function, but we can't use a normal member function as a curl progress callback,\r
-       // and we want access to some private members of Mootcher.\r
-\r
-       Mootcher *thisMootcher = (Mootcher *) caller;\r
-       \r
-       if (thisMootcher->cancel_download) {\r
-               return -1;\r
-       }\r
-\r
-       thisMootcher->Progress(dlnow, dltotal); /* EMIT SIGNAL */\r
-       return 0;\r
-}\r
-\r
+/* sfdb_freesound_mootcher.cpp **********************************************************************
+
+       Adapted for Ardour by Ben Loftis, March 2008
+       Updated to new Freesound API by Colin Fletcher, November 2011
+
+       Mootcher 23-8-2005
+
+       Mootcher Online Access to thefreesoundproject website
+       http://freesound.iua.upf.edu/
+
+       GPL 2005 Jorn Lemon
+       mail for questions/remarks: mootcher@twistedlemon.nl
+       or go to the freesound website forum
+
+       -----------------------------------------------------------------
+
+       Includes:
+               curl.h    (version 7.14.0)
+       Librarys:
+               libcurl.lib
+
+       -----------------------------------------------------------------
+       Licence GPL:
+
+       This program is free software; you can redistribute it and/or
+       modify it under the terms of the GNU General Public License
+       as published by the Free Software Foundation; either version 2
+       of the License, or (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with this program; if not, write to the Free Software
+       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+
+*************************************************************************************/
+#include "sfdb_freesound_mootcher.h"
+
+#include "pbd/xml++.h"
+#include "pbd/error.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <iostream>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "i18n.h"
+
+#include "ardour/audio_library.h"
+#include "ardour/rc_configuration.h"
+#include "pbd/pthread_utils.h"
+#include "gui_thread.h"
+
+using namespace PBD;
+
+static const std::string base_url = "http://www.freesound.org/api";
+static const std::string api_key = "9d77cb8d841b4bcfa960e1aae62224eb"; // ardour3
+
+//------------------------------------------------------------------------
+Mootcher::Mootcher()
+       : curl(curl_easy_init())
+{
+       cancel_download_btn.set_label (_("Cancel"));
+       progress_hbox.pack_start (progress_bar, true, true);
+       progress_hbox.pack_end (cancel_download_btn, false, false);
+       progress_bar.show();
+       cancel_download_btn.show();
+       cancel_download_btn.signal_clicked().connect(sigc::mem_fun (*this, &Mootcher::cancelDownload));
+};
+//------------------------------------------------------------------------
+Mootcher:: ~Mootcher()
+{
+       curl_easy_cleanup(curl);
+}
+
+//------------------------------------------------------------------------
+
+void Mootcher::ensureWorkingDir ()
+{
+       std::string p = ARDOUR::Config->get_freesound_download_dir();
+
+       if (!Glib::file_test (p, Glib::FILE_TEST_IS_DIR)) {
+               if (g_mkdir_with_parents (p.c_str(), 0775) != 0) {
+                       PBD::error << "Unable to create Mootcher working dir" << endmsg;
+               }
+       }
+       basePath = p;
+#ifdef PLATFORM_WINDOWS
+       std::string replace = "/";
+       size_t pos = basePath.find("\\");
+       while( pos != std::string::npos ){
+               basePath.replace(pos, 1, replace);
+               pos = basePath.find("\\");
+       }
+#endif
+}
+       
+
+//------------------------------------------------------------------------
+size_t Mootcher::WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
+{
+       register int realsize = (int)(size * nmemb);
+       struct MemoryStruct *mem = (struct MemoryStruct *)data;
+
+       mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
+
+       if (mem->memory) {
+               memcpy(&(mem->memory[mem->size]), ptr, realsize);
+               mem->size += realsize;
+               mem->memory[mem->size] = 0;
+       }
+       return realsize;
+}
+
+
+//------------------------------------------------------------------------
+
+std::string Mootcher::sortMethodString(enum sortMethod sort)
+{
+// given a sort type, returns the string value to be passed to the API to
+// sort the results in the requested way.
+
+       switch (sort) {
+               case sort_duration_desc:        return "duration_desc"; 
+               case sort_duration_asc:         return "duration_asc";
+               case sort_created_desc:         return "created_desc";
+               case sort_created_asc:          return "created_asc";
+               case sort_downloads_desc:       return "downloads_desc";
+               case sort_downloads_asc:        return "downloads_asc";
+               case sort_rating_desc:          return "rating_desc";
+               case sort_rating_asc:           return "rating_asc";
+               default:                        return "";      
+       }
+}
+
+//------------------------------------------------------------------------
+void Mootcher::setcUrlOptions()
+{
+       // basic init for curl
+       curl_global_init(CURL_GLOBAL_ALL);
+       // some servers don't like requests that are made without a user-agent field, so we provide one
+       curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");
+       // setup curl error buffer
+       curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer);
+       // Allow redirection
+       curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
+       
+       // Allow connections to time out (without using signals)
+       curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+       curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30);
+
+
+}
+
+std::string Mootcher::doRequest(std::string uri, std::string params)
+{
+       std::string result;
+       struct MemoryStruct xml_page;
+       xml_page.memory = NULL;
+       xml_page.size = 0;
+
+       setcUrlOptions();
+       curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+       curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &xml_page);
+
+       // curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
+       // curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postMessage.c_str());
+       // curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1);
+
+       // the url to get
+       std::string url = base_url + uri + "?";
+       if (params != "") {
+               url += params + "&api_key=" + api_key + "&format=xml";
+       } else {
+               url += "api_key=" + api_key + "&format=xml";
+       }
+               
+       curl_easy_setopt(curl, CURLOPT_URL, url.c_str() );
+       
+       // perform online request
+       CURLcode res = curl_easy_perform(curl);
+       if( res != 0 ) {
+               error << string_compose (_("curl error %1 (%2)"), res, curl_easy_strerror(res)) << endmsg;
+               return "";
+       }
+
+       // free the memory
+       if (xml_page.memory) {
+               result = xml_page.memory;
+       }
+       
+       free (xml_page.memory);
+       xml_page.memory = NULL;
+       xml_page.size = 0;
+
+       return result;
+}
+
+
+std::string Mootcher::searchSimilar(std::string id)
+{
+       std::string params = "";
+
+       params += "&fields=id,original_filename,duration,filesize,samplerate,license,serve";
+       params += "&num_results=100";
+
+       return doRequest("/sounds/" + id + "/similar", params);
+}
+
+//------------------------------------------------------------------------
+
+std::string Mootcher::searchText(std::string query, int page, std::string filter, enum sortMethod sort)
+{
+       std::string params = "";
+       char buf[24];
+
+       if (page > 1) {
+               snprintf(buf, 23, "p=%d&", page);
+               params += buf;
+       }
+
+       char *eq = curl_easy_escape(curl, query.c_str(), query.length());
+       params += "q=\"" + std::string(eq) + "\"";
+       free(eq);
+
+       if (filter != "") {
+               char *ef = curl_easy_escape(curl, filter.c_str(), filter.length());
+               params += "&f=" + std::string(ef);
+               free(ef);
+       }
+       
+       if (sort)
+               params += "&s=" + sortMethodString(sort);
+
+       params += "&fields=id,original_filename,duration,filesize,samplerate,license,serve";
+       params += "&sounds_per_page=100";
+
+       return doRequest("/sounds/search", params);
+}
+
+//------------------------------------------------------------------------
+
+std::string Mootcher::getSoundResourceFile(std::string ID)
+{
+
+       std::string originalSoundURI;
+       std::string audioFileName;
+       std::string xml;
+
+
+       // download the xmlfile into xml_page
+       xml = doRequest("/sounds/" + ID, "");
+
+       XMLTree doc;
+       doc.read_buffer( xml.c_str() );
+       XMLNode *freesound = doc.root();
+
+       // if the page is not a valid xml document with a 'freesound' root
+       if (freesound == NULL) {
+               error << _("getSoundResourceFile: There is no valid root in the xml file") << endmsg;
+               return "";
+       }
+
+       if (strcmp(doc.root()->name().c_str(), "response") != 0) {
+               error << string_compose (_("getSoundResourceFile: root = %1, != response"), doc.root()->name()) << endmsg;
+               return "";
+       }
+
+       XMLNode *name = freesound->child("original_filename");
+
+       // get the file name and size from xml file
+       if (name) {
+
+               audioFileName = Glib::build_filename (basePath, ID + "-" + name->child("text")->content());
+
+               //store all the tags in the database
+               XMLNode *tags = freesound->child("tags");
+               if (tags) {
+                       XMLNodeList children = tags->children();
+                       XMLNodeConstIterator niter;
+                       std::vector<std::string> strings;
+                       for (niter = children.begin(); niter != children.end(); ++niter) {
+                               XMLNode *node = *niter;
+                               if( strcmp( node->name().c_str(), "resource") == 0 ) {
+                                       XMLNode *text = node->child("text");
+                                       if (text) {
+                                               // std::cerr << "tag: " << text->content() << std::endl;
+                                               strings.push_back(text->content());
+                                       }
+                               }
+                       }
+                       ARDOUR::Library->set_tags (std::string("//")+audioFileName, strings);
+                       ARDOUR::Library->save_changes ();
+               }
+       }
+
+       return audioFileName;
+}
+
+int audioFileWrite(void *buffer, size_t size, size_t nmemb, void *file)
+{
+       return (int)fwrite(buffer, size, nmemb, (FILE*) file);
+};
+
+//------------------------------------------------------------------------
+
+void *
+Mootcher::threadFunc() {
+CURLcode res;
+
+       res = curl_easy_perform (curl);
+       fclose (theFile);
+       curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1); // turn off the progress bar
+       
+       if (res != CURLE_OK) {
+               /* it's not an error if the user pressed the stop button */
+               if (res != CURLE_ABORTED_BY_CALLBACK) {
+                       error <<  string_compose (_("curl error %1 (%2)"), res, curl_easy_strerror(res)) << endmsg;
+               }
+               remove ( (audioFileName+".part").c_str() );  
+       } else {
+               rename ( (audioFileName+".part").c_str(), audioFileName.c_str() );
+               // now download the tags &c.
+               getSoundResourceFile(ID);
+       }
+
+       return (void *) res;
+}
+void
+Mootcher::doneWithMootcher()
+{
+
+       // update the sound info pane if the selection in the list box is still us 
+       sfb->refresh_display(ID, audioFileName);
+
+       delete this; // this should be OK to do as long as Progress and Finished signals are always received in the order in which they are emitted
+}
+
+static void *
+freesound_download_thread_func(void *arg) 
+{ 
+       Mootcher *thisMootcher = (Mootcher *) arg;
+       void *res;
+
+       // std::cerr << "freesound_download_thread_func(" << arg << ")" << std::endl;
+       res = thisMootcher->threadFunc();
+
+       thisMootcher->Finished(); /* EMIT SIGNAL */
+       return res;
+}
+
+
+//------------------------------------------------------------------------
+
+bool Mootcher::checkAudioFile(std::string originalFileName, std::string theID)
+{
+       ensureWorkingDir();
+       ID = theID;
+       audioFileName = Glib::build_filename (basePath, ID + "-" + originalFileName);
+
+       // check to see if audio file already exists
+       FILE *testFile = g_fopen(audioFileName.c_str(), "r");
+       if (testFile) {  
+               fseek (testFile , 0 , SEEK_END);
+               if (ftell (testFile) > 256) {
+                       fclose (testFile);
+                       return true;
+               }
+               
+               // else file was small, probably an error, delete it 
+               fclose(testFile);
+               remove( audioFileName.c_str() );  
+       }
+       return false;
+}
+
+
+bool Mootcher::fetchAudioFile(std::string originalFileName, std::string theID, std::string audioURL, SoundFileBrowser *caller)
+{
+       ensureWorkingDir();
+       ID = theID;
+       audioFileName = Glib::build_filename (basePath, ID + "-" + originalFileName);
+
+       if (!curl) {
+               return false;
+       }
+       // now download the actual file
+       theFile = g_fopen( (audioFileName + ".part").c_str(), "wb" );
+
+       if (!theFile) {
+               return false;
+       }
+       
+       // create the download url
+       audioURL += "?api_key=" + api_key;
+
+       setcUrlOptions();
+       curl_easy_setopt(curl, CURLOPT_URL, audioURL.c_str() );
+       curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, audioFileWrite);
+       curl_easy_setopt(curl, CURLOPT_WRITEDATA, theFile);
+
+       std::string prog;
+       prog = string_compose (_("%1"), originalFileName);
+       progress_bar.set_text(prog);
+
+       Gtk::VBox *freesound_vbox = dynamic_cast<Gtk::VBox *> (caller->notebook.get_nth_page(2));
+       freesound_vbox->pack_start(progress_hbox, Gtk::PACK_SHRINK);
+       progress_hbox.show();
+       cancel_download = false;
+       sfb = caller;
+
+       curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0); // turn on the progress bar
+       curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_callback);
+       curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, this);
+
+       Progress.connect(*this, invalidator (*this), boost::bind(&Mootcher::updateProgress, this, _1, _2), gui_context());
+       Finished.connect(*this, invalidator (*this), boost::bind(&Mootcher::doneWithMootcher, this), gui_context());
+       pthread_t freesound_download_thread;
+       pthread_create_and_store("freesound_import", &freesound_download_thread, freesound_download_thread_func, this);
+
+       return true;
+}
+
+//---------
+
+void 
+Mootcher::updateProgress(double dlnow, double dltotal) 
+{
+       if (dltotal > 0) {
+               double fraction = dlnow / dltotal;
+               // std::cerr << "progress idle: " << progress->bar->get_text() << ". " << progress->dlnow << " / " << progress->dltotal << " = " << fraction << std::endl;
+               if (fraction > 1.0) {
+                       fraction = 1.0;
+               } else if (fraction < 0.0) {
+                       fraction = 0.0;
+               }
+               progress_bar.set_fraction(fraction);
+       }
+}
+
+int 
+Mootcher::progress_callback(void *caller, double dltotal, double dlnow, double /*ultotal*/, double /*ulnow*/)
+{
+       // It may seem curious to pass a pointer to an instance of an object to a static
+       // member function, but we can't use a normal member function as a curl progress callback,
+       // and we want access to some private members of Mootcher.
+
+       Mootcher *thisMootcher = (Mootcher *) caller;
+       
+       if (thisMootcher->cancel_download) {
+               return -1;
+       }
+
+       thisMootcher->Progress(dlnow, dltotal); /* EMIT SIGNAL */
+       return 0;
+}
+
index 8011268335938569bfe7c31583df7221556f687f..da29eb75a84a17cca6bb1f193d678a8f4c98a483 100644 (file)
@@ -64,7 +64,6 @@
 #include "prompter.h"
 #include "sfdb_ui.h"
 #include "editing.h"
-#include "utils.h"
 #include "gain_meter.h"
 #include "main_clock.h"
 #include "public_editor.h"
@@ -281,7 +280,7 @@ SoundFileBox::setup_labels (const string& filename)
 
        string error_msg;
 
-       if (SMFSource::safe_midi_file_extension (path)) {
+       if (SMFSource::valid_midi_file (path)) {
 
                boost::shared_ptr<SMFSource> ms =
                        boost::dynamic_pointer_cast<SMFSource> (
@@ -406,7 +405,7 @@ SoundFileBox::audition ()
 
        boost::shared_ptr<Region> r;
 
-       if (SMFSource::safe_midi_file_extension (path)) {
+       if (SMFSource::valid_midi_file (path)) {
 
                boost::shared_ptr<SMFSource> ms =
                        boost::dynamic_pointer_cast<SMFSource> (
@@ -1326,7 +1325,7 @@ SoundFileOmega::reset_options ()
 
        /* See if we are thinking about importing any MIDI files */
        vector<string>::iterator i = paths.begin ();
-       while (i != paths.end() && SMFSource::safe_midi_file_extension (*i) == false) {
+       while (i != paths.end() && SMFSource::valid_midi_file (*i) == false) {
                ++i;
        }
        bool const have_a_midi_file = (i != paths.end ());
@@ -1554,7 +1553,7 @@ SoundFileOmega::check_info (const vector<string>& paths, bool& same_size, bool&
                                src_needed = true;
                        }
 
-               } else if (SMFSource::safe_midi_file_extension (*i)) {
+               } else if (SMFSource::valid_midi_file (*i)) {
 
                        Evoral::SMF reader;
                        reader.open(*i);
diff --git a/gtk2_ardour/soundcloud_export_selector.cc b/gtk2_ardour/soundcloud_export_selector.cc
new file mode 100644 (file)
index 0000000..e9ef5a3
--- /dev/null
@@ -0,0 +1,113 @@
+/* soundcloud_export_selector.cpp ***************************************************
+
+       Adapted for Ardour by Ben Loftis, March 2012
+
+       Licence GPL:
+
+       This program is free software; you can redistribute it and/or
+       modify it under the terms of the GNU General Public License
+       as published by the Free Software Foundation; either version 2
+       of the License, or (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with this program; if not, write to the Free Software
+       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+
+*************************************************************************************/
+#include "ardour/debug.h"
+#include "ardour/soundcloud_upload.h"
+#include "soundcloud_export_selector.h"
+
+#include <pbd/error.h>
+#include "pbd/openuri.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <iostream>
+#include <glib/gstdio.h>
+
+#include "i18n.h"
+
+using namespace PBD;
+
+#include "ardour/session_metadata.h"
+#include "utils.h"
+
+SoundcloudExportSelector::SoundcloudExportSelector () :
+         sc_table (4, 3),
+         soundcloud_username_label (_("User Email"), 1.0, 0.5),
+         soundcloud_password_label (_("Password"), 1.0, 0.5),
+         soundcloud_public_checkbox (_("Make files public")),
+         soundcloud_open_checkbox (_("Open uploaded files in browser")),
+         soundcloud_download_checkbox (_("Make files downloadable")),
+         progress_bar()
+{
+
+
+       soundcloud_public_checkbox.set_name ("ExportCheckbox");
+       soundcloud_download_checkbox.set_name ("ExportCheckbox");
+       soundcloud_username_label.set_name ("ExportFormatLabel");
+       soundcloud_username_entry.set_name ("ExportFormatDisplay");
+       soundcloud_password_label.set_name ("ExportFormatLabel");
+       soundcloud_password_entry.set_name ("ExportFormatDisplay");
+
+       soundcloud_username_entry.set_text (ARDOUR::SessionMetadata::Metadata()->user_email());
+       soundcloud_password_entry.set_visibility (false);
+
+       Gtk::Frame *sc_frame = manage (new Gtk::Frame);
+       sc_frame->set_border_width (4);
+       sc_frame->set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
+       sc_frame->set_name ("soundcloud_export_box");
+       pack_start (*sc_frame, false, false);
+
+       sc_table.set_border_width (4);
+       sc_table.set_col_spacings (5);
+       sc_table.set_row_spacings (5);
+       sc_frame->add (sc_table);
+
+       sc_table.attach ( *(Gtk::manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon (X_("soundcloud"))))) , 0, 1,  0, 2);
+
+       sc_table.attach (soundcloud_username_label,    0, 1,  1, 2);
+       sc_table.attach (soundcloud_username_entry,    1, 3,  1, 2);
+       sc_table.attach (soundcloud_password_label,    0, 1,  2, 3);
+       sc_table.attach (soundcloud_password_entry,    1, 3,  2, 3);
+       sc_table.attach (soundcloud_public_checkbox,   2, 3,  3, 4);
+       sc_table.attach (soundcloud_open_checkbox,     2, 3,  4, 5);
+       sc_table.attach (soundcloud_download_checkbox, 2, 3,  5, 6);
+
+       pack_end (progress_bar, false, false);
+       sc_frame->show_all ();
+}
+
+
+int
+SoundcloudExportSelector::do_progress_callback (double ultotal, double ulnow, const std::string &filename)
+{
+       DEBUG_TRACE (DEBUG::Soundcloud, string_compose ("SoundcloudExportSelector::do_progress_callback(%1, %2, %3)", ultotal, ulnow, filename));
+       if (soundcloud_cancel) {
+               progress_bar.set_fraction (0);
+               // cancel_button.set_label ("");
+               return -1;
+       }
+
+       double fraction = 0.0;
+       if (ultotal != 0) {
+               fraction = ulnow / ultotal;
+       }
+
+       progress_bar.set_fraction ( fraction );
+
+       std::string prog;
+       prog = string_compose (_("%1: %2 of %3 bytes uploaded"), filename, ulnow, ultotal);
+       progress_bar.set_text (prog);
+
+
+       return 0;
+}
+
diff --git a/gtk2_ardour/soundcloud_export_selector.h b/gtk2_ardour/soundcloud_export_selector.h
new file mode 100644 (file)
index 0000000..fcfbdfe
--- /dev/null
@@ -0,0 +1,46 @@
+/*soundcloud_export_selector.h***********************************************
+
+       Adapted for Ardour by Ben Loftis, March 2012
+
+*****************************************************************************/
+#ifndef __soundcloud_export_selector_h__
+#define __soundcloud_export_selector_h__
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <stdio.h>
+#include <cstring>
+#include <string>
+#include <sstream>
+#include <vector>
+#include <gtkmm.h>
+#include <gtkmm/progressbar.h>
+
+class SoundcloudExportSelector : public Gtk::VBox, public ARDOUR::SessionHandlePtr
+{
+  public:
+       SoundcloudExportSelector ();
+       int do_progress_callback (double ultotal, double ulnow, const std::string &filename);
+       std::string username () { return soundcloud_username_entry.get_text (); }
+       std::string password () { return soundcloud_password_entry.get_text (); }
+       bool make_public  () { return soundcloud_public_checkbox.get_active (); }
+       bool open_page    () { return soundcloud_open_checkbox.get_active (); }
+       bool downloadable () { return soundcloud_download_checkbox.get_active (); }
+       void cancel () { soundcloud_cancel = true; }
+
+  private:
+       Gtk::Table  sc_table;
+       Gtk::Label soundcloud_username_label;
+       Gtk::Entry soundcloud_username_entry;
+       Gtk::Label soundcloud_password_label;
+       Gtk::Entry soundcloud_password_entry;
+       Gtk::CheckButton soundcloud_public_checkbox;
+       Gtk::CheckButton soundcloud_open_checkbox;
+       Gtk::CheckButton soundcloud_download_checkbox;
+       bool soundcloud_cancel;
+       Gtk::ProgressBar progress_bar;
+       
+};
+
+#endif // __soundcloud_export_selector_h__
index 66412d4881a052440847986dd8f38981ba0ec0d0..be34f2dbbd59e10564e1202cd19619dd7534b730 100644 (file)
@@ -48,7 +48,7 @@ Splash::Splash ()
        
        std::string splash_file;
 
-       if (!find_file_in_search_path (ardour_data_search_path(), "splash.png", splash_file)) {
+       if (!find_file (ardour_data_search_path(), "splash.png", splash_file)) {
                 cerr << "Cannot find splash screen image file\n";
                throw failed_constructor();
        }
index 8482a6cb2268bcd5517d28bdd800914ff1931bde..38074e932a00ffeaa5c26441a9ea6fbc541c6051 100644 (file)
@@ -55,6 +55,7 @@ using namespace Gdk;
 using namespace Glib;
 using namespace PBD;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 
 ArdourStartup* ArdourStartup::the_startup = 0;
 
index fd53fd55ff3704fb912c8514ab9a8f644389387a..2039f50a0a416aa423143e344e4793f687464888 100644 (file)
@@ -42,6 +42,7 @@ using namespace Glib;
 using namespace Gtkmm2ext;
 using namespace PBD;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 
 static void
 _note_off_event_handler (GtkWidget* /*widget*/, int note, gpointer arg)
@@ -709,7 +710,7 @@ StepEntry::load_bindings ()
 
        std::string binding_file;
 
-       if (find_file_in_search_path (ardour_config_search_path(), "step_editing.bindings", binding_file)) {
+       if (find_file (ardour_config_search_path(), "step_editing.bindings", binding_file)) {
                 bindings.load (binding_file);
         }
 }
index 718d5f38ee9465d4e3cebcbc66aa60deaa590651..ab923a47be737fece200eed8b01b2792b2f71b12 100644 (file)
@@ -49,6 +49,7 @@
 using namespace std;
 using namespace Gtk;
 using namespace Gtkmm2ext;
+using namespace ARDOUR_UI_UTILS;
 
 static const int pos_box_size = 8;
 static const int lr_box_size = 15;
@@ -86,9 +87,7 @@ StereoPanner::StereoPanner (boost::shared_ptr<PannerShell> p)
        if (!have_font) {
                Pango::FontDescription font;
                Pango::AttrFontDesc* font_attr;
-               font = Pango::FontDescription ("ArdourMono");
-               font.set_weight (Pango::WEIGHT_BOLD);
-               font.set_size(9 * PANGO_SCALE);
+               font = Pango::FontDescription (ARDOUR_UI::config()->get_canvasvar_SmallBoldMonospaceFont());
                font_attr = new Pango::AttrFontDesc (Pango::Attribute::create_attr_font_desc (font));
                panner_font_attributes.change(*font_attr);
                delete font_attr;
@@ -178,6 +177,11 @@ StereoPanner::on_expose_event (GdkEventExpose*)
                r  = 0x606060ff;
        }
 
+       if (_send_mode) {
+               b = rgba_from_style("SendStripBase",
+                               UINT_RGBA_R(b), UINT_RGBA_G(b), UINT_RGBA_B(b), 255,
+                               "fg");
+       }
        /* background */
 
        context->set_source_rgba (UINT_RGBA_R_FLT(b), UINT_RGBA_G_FLT(b), UINT_RGBA_B_FLT(b), UINT_RGBA_A_FLT(b));
index 404b934562b60cf0d53d50db33247b8c24f843b4..9eecc80dd7fd80de28ced468cb2bb81c02453a48 100644 (file)
 
 using namespace std;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Editing;
 
-StreamView::StreamView (RouteTimeAxisView& tv, ArdourCanvas::Group* canvas_group)
+StreamView::StreamView (RouteTimeAxisView& tv, ArdourCanvas::Container* canvas_group)
        : _trackview (tv)
-       , _canvas_group (canvas_group ? canvas_group : new ArdourCanvas::Group (_trackview.canvas_display()))
+       , _canvas_group (canvas_group ? canvas_group : new ArdourCanvas::Container (_trackview.canvas_display()))
        , _samples_per_pixel (_trackview.editor().get_current_zoom ())
        , rec_updating(false)
        , rec_active(false)
@@ -343,7 +344,13 @@ StreamView::diskstream_changed ()
 }
 
 void
-StreamView::apply_color (Gdk::Color color, ColorTarget target)
+StreamView::apply_color (Gdk::Color const& c, ColorTarget target)
+{
+       return apply_color (gdk_color_to_rgba (c), target);
+}
+
+void
+StreamView::apply_color (uint32_t color, ColorTarget target)
 {
        list<RegionView *>::iterator i;
 
@@ -356,7 +363,7 @@ StreamView::apply_color (Gdk::Color color, ColorTarget target)
                break;
 
        case StreamBaseColor:
-               stream_base_color = RGBA_TO_UINT (color.get_red_p(), color.get_green_p(), color.get_blue_p(), 255);
+               stream_base_color = color;
                canvas_rect->set_fill_color (stream_base_color);
                break;
        }
index e7608cb02ca79db26de6f11ce4587f453bcd6622..b3ae34e4d4fd683742f63d144af9c2745d2897b3 100644 (file)
@@ -42,7 +42,7 @@ namespace ARDOUR {
 
 namespace ArdourCanvas {
        class Rectangle;
-       class Group;
+       class Container;
 }
 
 struct RecBoxInfo {
@@ -82,15 +82,16 @@ public:
        void set_layer_display (LayerDisplay);
        LayerDisplay layer_display () const { return _layer_display; }
 
-       ArdourCanvas::Group* canvas_item() { return _canvas_group; }
+       ArdourCanvas::Container* canvas_item() { return _canvas_group; }
 
        enum ColorTarget {
                RegionColor,
                StreamBaseColor
        };
 
-       Gdk::Color get_region_color () const { return region_color; }
-       void       apply_color (Gdk::Color, ColorTarget t);
+       uint32_t get_region_color () const { return region_color; }
+       void     apply_color (uint32_t, ColorTarget t);
+       void     apply_color (Gdk::Color const &, ColorTarget t);
 
        uint32_t     num_selected_regionviews () const;
 
@@ -127,7 +128,7 @@ public:
        sigc::signal<void> ContentsHeightChanged;
 
 protected:
-       StreamView (RouteTimeAxisView&, ArdourCanvas::Group* canvas_group = 0);
+       StreamView (RouteTimeAxisView&, ArdourCanvas::Container* canvas_group = 0);
 
        void         transport_changed();
        void         transport_looped();
@@ -150,7 +151,7 @@ protected:
        virtual void color_handler () = 0;
 
        RouteTimeAxisView&        _trackview;
-       ArdourCanvas::Group*      _canvas_group;
+       ArdourCanvas::Container*      _canvas_group;
        ArdourCanvas::Rectangle*   canvas_rect; /* frame around the whole thing */
 
        typedef std::list<RegionView* > RegionViewList;
@@ -164,8 +165,8 @@ protected:
        bool                   rec_updating;
        bool                   rec_active;
 
-       Gdk::Color region_color;      ///< Contained region color
-       uint32_t   stream_base_color; ///< Background color
+       uint32_t region_color;      ///< Contained region color
+       uint32_t stream_base_color; ///< Background color
 
        PBD::ScopedConnectionList playlist_connections;
        PBD::ScopedConnection playlist_switched_connection;
index 2097de4fa91f42733e0a307ae2365acf0c2bd866..ffa06aa8ae4fbd0d7c4acf0b611d439c6abfd9f3 100644 (file)
@@ -26,7 +26,7 @@ using namespace std;
 
 SysEx::SysEx (
        MidiRegionView& region,
-       ArdourCanvas::Group* parent,
+       ArdourCanvas::Container* parent,
        string&         text,
        double          height,
        double          x,
index 1d6787a75e29a2d11967c36f153a4b05d87d4656..a0bb1df347acf68f403ca6bed7695ed1eedbb024 100644 (file)
@@ -31,7 +31,7 @@ class SysEx
 public:
        SysEx (
                        MidiRegionView& region,
-                       ArdourCanvas::Group* parent,
+                       ArdourCanvas::Container* parent,
                        std::string&    text,
                        double          height,
                        double          x,
index d626f3df496880fb12bf127e929855811841b83d..f40ccb60142ac30dcef59f915494b1ef1a9d3860 100644 (file)
@@ -46,10 +46,10 @@ const TimeAxisViewItem::Visibility TapeAudioRegionView::default_tape_visibility
                TimeAxisViewItem::HideFrameRight |
                TimeAxisViewItem::FullWidthNameHighlight);
 
-TapeAudioRegionView::TapeAudioRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
+TapeAudioRegionView::TapeAudioRegionView (ArdourCanvas::Container *parent, RouteTimeAxisView &tv,
                                          boost::shared_ptr<AudioRegion> r,
                                          double spu,
-                                         Gdk::Color const & basic_color)
+                                         uint32_t basic_color)
 
        : AudioRegionView (parent, tv, r, spu, basic_color, false,
                           TimeAxisViewItem::Visibility ((r->position() != 0) ? default_tape_visibility :
@@ -58,13 +58,13 @@ TapeAudioRegionView::TapeAudioRegionView (ArdourCanvas::Group *parent, RouteTime
 }
 
 void
-TapeAudioRegionView::init (Gdk::Color const & basic_color, bool /*wfw*/)
+TapeAudioRegionView::init (bool /*wfw*/)
 {
        /* never wait for data: always just create the waves, connect once and then
           we'll update whenever we need to.
        */
 
-       AudioRegionView::init(basic_color, false);
+       AudioRegionView::init (false);
 
        /* every time the wave data changes and peaks are ready, redraw */
 
@@ -91,10 +91,3 @@ TapeAudioRegionView::update (uint32_t /*n*/)
        // CAIROCANVAS
        // waves[n]->rebuild ();
 }
-
-void
-TapeAudioRegionView::set_frame_color ()
-{
-       fill_opacity = 255;
-       AudioRegionView::set_frame_color ();
-}
index c0e04cd8da43a40e578168baabe6b9f8df97ca8c..6361683362849e7a09cae36e15e272c940d4a63c 100644 (file)
 class TapeAudioRegionView : public AudioRegionView
 {
   public:
-       TapeAudioRegionView (ArdourCanvas::Group *,
+       TapeAudioRegionView (ArdourCanvas::Container *,
                             RouteTimeAxisView&,
                             boost::shared_ptr<ARDOUR::AudioRegion>,
                             double initial_samples_per_pixel,
-                            Gdk::Color const & base_color);
+                            uint32_t base_color);
        ~TapeAudioRegionView ();
 
   protected:
-       void init (Gdk::Color const & base_color, bool wait_for_waves);
+       void init (bool wait_for_waves);
 
-       void set_frame_color ();
        void update (uint32_t n);
 
        static const TimeAxisViewItem::Visibility default_tape_visibility;
index b601734beeae4ee4a442d547ad63d287f3e72e0e..7c0e510aff8fd92dbcddb388788db25d7d109f6c 100644 (file)
@@ -26,7 +26,6 @@
 #include "ardour/rc_configuration.h"
 
 #include "tempo_dialog.h"
-#include "utils.h"
 
 #include "i18n.h"
 
index 828a4cc82ba8a390af7b320bfcf37d4c8663319b..1a3fdaed2e0c82432a75260c39cbc6eaa70f07ec 100644 (file)
@@ -19,7 +19,6 @@
 
 #include "pbd/compose.h"
 
-#include "canvas/line.h"
 #include "canvas/canvas.h"
 #include "canvas/debug.h"
 
 
 using namespace std;
 
-TempoLines::TempoLines (ArdourCanvas::Canvas& canvas, ArdourCanvas::Group* group, double h)
-       : _canvas (canvas)
-       , _group (group)
-       , _height (h)
+TempoLines::TempoLines (ArdourCanvas::Container* group, double)
+       : lines (group, ArdourCanvas::LineSet::Vertical)
 {
+       lines.set_extent (ArdourCanvas::COORD_MAX);
 }
 
 void
 TempoLines::tempo_map_changed()
 {
-       /* remove all lines from the group, put them in the cache (to avoid
-        * unnecessary object destruction+construction later), and clear _lines
-        */
-        
-       _group->clear ();
-       _cache.insert (_cache.end(), _lines.begin(), _lines.end());
-       _lines.clear ();
+       lines.clear ();
 }
 
 void
 TempoLines::show ()
 {
-       _group->show ();
+       lines.show ();
 }
 
 void
 TempoLines::hide ()
 {
-       _group->hide ();
+       lines.hide ();
 }
 
 void
@@ -65,7 +57,6 @@ TempoLines::draw (const ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
                  const ARDOUR::TempoMap::BBTPointList::const_iterator& end)
 {
        ARDOUR::TempoMap::BBTPointList::const_iterator i;
-       ArdourCanvas::Rect const visible = _canvas.visible_area ();
        double  beat_density;
 
        uint32_t beats = 0;
@@ -79,22 +70,22 @@ TempoLines::draw (const ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
        bars = (*i).bar - (*begin).bar; 
        beats = distance (begin, end) - bars;
 
-       beat_density = (beats * 10.0f) / visible.width ();
+       beat_density = (beats * 10.0f) / lines.canvas()->width();
 
        if (beat_density > 4.0f) {
                /* if the lines are too close together, they become useless */
-               tempo_map_changed();
+               lines.clear ();
                return;
        }
 
-       tempo_map_changed ();
+       lines.clear ();
 
        for (i = begin; i != end; ++i) {
 
                if ((*i).is_bar()) {
                        color = ARDOUR_UI::config()->get_canvasvar_MeasureLineBar();
                } else {
-                       if (beat_density > 2.0) {
+                       if (beat_density > 0.3) {
                                continue; /* only draw beat lines if the gaps between beats are large. */
                        }
                        color = ARDOUR_UI::config()->get_canvasvar_MeasureLineBeat();
@@ -102,24 +93,7 @@ TempoLines::draw (const ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
 
                ArdourCanvas::Coord xpos = PublicEditor::instance().sample_to_pixel_unrounded ((*i).frame);
 
-               ArdourCanvas::Line* line;
-
-               if (!_cache.empty()) {
-                       line = _cache.back ();
-                       _cache.pop_back ();
-                       line->reparent (_group);
-               } else {
-                       line = new ArdourCanvas::Line (_group);
-                       CANVAS_DEBUG_NAME (line, string_compose ("tempo measure line @ %1", (*i).frame));
-                       line->set_ignore_events (true);
-               }
-
-               line->set_x0 (xpos);
-               line->set_x1 (xpos);
-               line->set_y0 (0.0);
-               line->set_y1 (_height);
-               line->set_outline_color (color);
-               line->show ();
+               lines.add (xpos, 1.0, color);
        }
 }
 
index 912a77aea2cbd0795b8cf3d640eae8c702f6edb4..99a6a6b93f03ddf704fb80834d282c9508b985d0 100644 (file)
 #ifndef __ardour_tempo_lines_h__
 #define __ardour_tempo_lines_h__
 
-#include <list>
 #include "ardour/tempo.h"
 
+#include "canvas/line_set.h"
+
 class TempoLines {
 public:
-       TempoLines(ArdourCanvas::Canvas& canvas, ArdourCanvas::Group* group, double screen_height);
+       TempoLines (ArdourCanvas::Container* group, double screen_height);
 
        void tempo_map_changed();
 
@@ -35,12 +36,7 @@ public:
        void hide();
 
 private:
-        typedef std::list<ArdourCanvas::Line*> Lines;
-       Lines _lines;
-        Lines _cache;
-
-        ArdourCanvas::Canvas& _canvas;
-       ArdourCanvas::Group*  _group;
+       ArdourCanvas::LineSet lines;
        double                _height;
 };
 
index 3514d918bd051e360a07743eefe69de04ff9a754..38f8aac4c91b1a2dbe18a2832b772a50d8f5e325 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "gtkmm2ext/gtk_ui.h"
 #include "gtkmm2ext/cell_renderer_color_selector.h"
+#include "gtkmm2ext/utils.h"
 
 #include "pbd/file_utils.h"
 #include "pbd/compose.h"
@@ -42,6 +43,7 @@
 #include "rgb_macros.h"
 #include "ardour_ui.h"
 #include "global_signals.h"
+#include "utils.h"
 
 #include "i18n.h"
 
@@ -49,9 +51,12 @@ using namespace std;
 using namespace Gtk;
 using namespace PBD;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 
-sigc::signal<void> ColorsChanged;
-sigc::signal<void,uint32_t> ColorChanged;
+namespace ARDOUR_UI_UTILS {
+       sigc::signal<void> ColorsChanged;
+       sigc::signal<void,uint32_t> ColorChanged;
+}
 
 ThemeManager::ThemeManager()
        : ArdourWindow (_("Theme Manager"))
@@ -66,6 +71,7 @@ ThemeManager::ThemeManager()
        , timeline_item_gradient_depth (0, 1.0, 0.05)
        , timeline_item_gradient_depth_label (_("Timeline item gradient depth"))
        , all_dialogs (_("All floating windows are dialogs"))
+       , icon_set_label (_("Icon Set"))
 {
        set_title (_("Theme Manager"));
 
@@ -107,7 +113,23 @@ ThemeManager::ThemeManager()
        vbox->pack_start (region_color_button, PACK_SHRINK);
        vbox->pack_start (show_clipping_button, PACK_SHRINK);
 
-       Gtk::HBox* hbox = Gtk::manage (new Gtk::HBox());
+       Gtk::HBox* hbox;
+
+       vector<string> icon_sets = ::get_icon_sets ();
+
+       if (icon_sets.size() > 1) {
+               Gtkmm2ext::set_popdown_strings (icon_set_dropdown, icon_sets);
+               icon_set_dropdown.set_active_text (ARDOUR_UI::config()->get_icon_set());
+
+               hbox = Gtk::manage (new Gtk::HBox());
+               hbox->set_spacing (6);
+               hbox->pack_start (icon_set_label, false, false);
+               hbox->pack_start (icon_set_dropdown, true, true);
+               vbox->pack_start (*hbox, PACK_SHRINK);
+       }
+
+       
+       hbox = Gtk::manage (new Gtk::HBox());
        hbox->set_spacing (6);
        hbox->pack_start (waveform_gradient_depth, true, true);
        hbox->pack_start (waveform_gradient_depth_label, false, false);
@@ -117,8 +139,8 @@ ThemeManager::ThemeManager()
        hbox->set_spacing (6);
        hbox->pack_start (timeline_item_gradient_depth, true, true);
        hbox->pack_start (timeline_item_gradient_depth_label, false, false);
-
        vbox->pack_start (*hbox, PACK_SHRINK);
+
        vbox->pack_start (scroller);
 
        vbox->show_all ();
@@ -148,6 +170,7 @@ ThemeManager::ThemeManager()
        waveform_gradient_depth.signal_value_changed().connect (sigc::mem_fun (*this, &ThemeManager::on_waveform_gradient_depth_change));
        timeline_item_gradient_depth.signal_value_changed().connect (sigc::mem_fun (*this, &ThemeManager::on_timeline_item_gradient_depth_change));
        all_dialogs.signal_toggled().connect (sigc::mem_fun (*this, &ThemeManager::on_all_dialogs_toggled));
+       icon_set_dropdown.signal_changed().connect (sigc::mem_fun (*this, &ThemeManager::on_icon_set_changed));
 
        Gtkmm2ext::UI::instance()->set_tip (all_dialogs, 
                                            string_compose (_("Mark all floating windows to be type \"Dialog\" rather than using \"Utility\" for some.\n"
@@ -177,7 +200,7 @@ ThemeManager::button_press_event (GdkEventButton* ev)
        int cellx;
        int celly;
 
-       UIConfigVariable<uint32_t> *ccvar;
+       ColorVariable<uint32_t> *ccvar;
 
        if (!color_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
                return false;
@@ -191,7 +214,7 @@ ThemeManager::button_press_event (GdkEventButton* ev)
        case 1: /* color */
                if ((iter = color_list->get_iter (path))) {
 
-                       UIConfigVariable<uint32_t>* var = (*iter)[columns.pVar];
+                       ColorVariable<uint32_t>* var = (*iter)[columns.pVar];
                        if (!var) {
                                /* parent row, do nothing */
                                return false;
@@ -255,7 +278,7 @@ load_rc_file (const string& filename, bool themechange)
 {
        std::string rc_file_path;
 
-       if (!find_file_in_search_path (ardour_config_search_path(), filename, rc_file_path)) {
+       if (!find_file (ardour_config_search_path(), filename, rc_file_path)) {
                warning << string_compose (_("Unable to find UI style file %1 in search path %2. %3 will look strange"),
                                            filename, ardour_config_search_path().to_string(), PROGRAM_NAME)
                                << endmsg;
@@ -329,6 +352,13 @@ ThemeManager::on_timeline_item_gradient_depth_change ()
        ARDOUR_UI::config()->set_dirty ();
 }
 
+void
+ThemeManager::on_icon_set_changed ()
+{
+       string new_set = icon_set_dropdown.get_active_text();
+       ARDOUR_UI::config()->set_icon_set (new_set);
+}
+
 void
 ThemeManager::on_dark_theme_button_toggled()
 {
@@ -365,10 +395,10 @@ ThemeManager::setup_theme ()
 
        color_list->clear();
 
-       for (std::map<std::string,UIConfigVariable<uint32_t> *>::iterator i = ARDOUR_UI::config()->canvas_colors.begin(); i != ARDOUR_UI::config()->canvas_colors.end(); i++) {
+       for (std::map<std::string,ColorVariable<uint32_t> *>::iterator i = ARDOUR_UI::config()->canvas_colors.begin(); i != ARDOUR_UI::config()->canvas_colors.end(); i++) {
 
 
-               UIConfigVariable<uint32_t>* var = i->second;
+               ColorVariable<uint32_t>* var = i->second;
 
                TreeModel::Children rows = color_list->children();
                TreeModel::Row row;
index 7c8d81fef9d9ec0a61687920b548b34669348c88..e52c4630f3c5a1264b5a8fb72e8b3e40f40ed06b 100644 (file)
@@ -49,6 +49,7 @@ class ThemeManager : public ArdourWindow
         void on_waveform_gradient_depth_change ();
         void on_timeline_item_gradient_depth_change ();
        void on_all_dialogs_toggled ();
+       void on_icon_set_changed ();
 
   private:
        struct ColorDisplayModelColumns : public Gtk::TreeModel::ColumnRecord {
@@ -61,7 +62,7 @@ class ThemeManager : public ArdourWindow
 
            Gtk::TreeModelColumn<std::string>  name;
            Gtk::TreeModelColumn<Gdk::Color>   gdkcolor;
-           Gtk::TreeModelColumn<UIConfigVariable<uint32_t> *> pVar;
+           Gtk::TreeModelColumn<ColorVariable<uint32_t> *> pVar;
            Gtk::TreeModelColumn<uint32_t>     rgba;
        };
 
@@ -83,6 +84,8 @@ class ThemeManager : public ArdourWindow
         Gtk::Label timeline_item_gradient_depth_label;
        Gtk::CheckButton all_dialogs;
        Gtk::CheckButton gradient_waveforms;
+       Gtk::Label icon_set_label;
+       Gtk::ComboBoxText icon_set_dropdown;
 
        bool button_press_event (GdkEventButton*);
 };
index f67ca6a3cea5e70c0eda4861612a17912732b588..9cc691ad08f03ddc60c9910edef73bf96e3fe990 100644 (file)
@@ -58,6 +58,7 @@ using namespace std;
 using namespace Gtk;
 using namespace Gdk;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Editing;
 using namespace ArdourCanvas;
@@ -97,16 +98,16 @@ TimeAxisView::TimeAxisView (ARDOUR::Session* sess, PublicEditor& ed, TimeAxisVie
                compute_heights ();
        }
 
-       _canvas_display = new Group (ed.get_trackview_group (), ArdourCanvas::Duple (0.0, 0.0));
+       _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group (), ArdourCanvas::Duple (0.0, 0.0));
        CANVAS_DEBUG_NAME (_canvas_display, "main for TAV");
        _canvas_display->hide(); // reveal as needed
 
-       selection_group = new Group (_canvas_display);
+       selection_group = new ArdourCanvas::Container (_canvas_display);
        CANVAS_DEBUG_NAME (selection_group, "selection for TAV");
        selection_group->set_data (X_("timeselection"), (void *) 1);
        selection_group->hide();
-
-       _ghost_group = new Group (_canvas_display);
+       
+       _ghost_group = new ArdourCanvas::Container (_canvas_display);
        CANVAS_DEBUG_NAME (_ghost_group, "ghost for TAV");
        _ghost_group->lower_to_bottom();
        _ghost_group->show();
@@ -297,10 +298,7 @@ TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
                        }
                        e.stepping_axis_view()->step_height (false);
                        return true;
-               } else if (Keyboard::no_modifiers_active (ev->state)) {
-                       _editor.scroll_up_one_track();
-                       return true;
-               }
+               } 
                break;
 
        case GDK_SCROLL_DOWN:
@@ -312,10 +310,7 @@ TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
                        }
                        e.stepping_axis_view()->step_height (true);
                        return true;
-               } else if (Keyboard::no_modifiers_active (ev->state)) {
-                       _editor.scroll_down_one_track();
-                       return true;
-               }
+               } 
                break;
 
        default:
@@ -323,7 +318,14 @@ TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
                break;
        }
 
-       return false;
+       /* Just forward to the normal canvas scroll method. The coordinate
+          systems are different but since the canvas is always larger than the
+          track headers, and aligned with the trackview area, this will work.
+
+          In the not too distant future this layout is going away anyway and
+          headers will be on the canvas.
+       */
+       return _editor.canvas_scroll_event (ev, false);
 }
 
 bool
@@ -845,9 +847,9 @@ TimeAxisView::show_selection (TimeSelection& ts)
 
                x1 = _editor.sample_to_pixel (start);
                x2 = _editor.sample_to_pixel (start + cnt - 1);
-               y2 = current_height();
+               y2 = current_height() - 1;
 
-               rect->rect->set (ArdourCanvas::Rect (x1, 1, x2, y2));
+               rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
 
                // trim boxes are at the top for selections
 
@@ -954,12 +956,12 @@ TimeAxisView::get_selection_rect (uint32_t id)
                rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
 
                rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
-               CANVAS_DEBUG_NAME (rect->rect, "selection rect start trim");
+               CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
                rect->start_trim->set_outline (false);
                rect->start_trim->set_fill (false);
 
                rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
-               CANVAS_DEBUG_NAME (rect->rect, "selection rect end trim");
+               CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
                rect->end_trim->set_outline (false);
                rect->end_trim->set_fill (false);
 
@@ -1151,9 +1153,12 @@ TimeAxisView::color_handler ()
 }
 
 /** @return Pair: TimeAxisView, layer index.
- * TimeAxisView is non-0 if this object covers y, or one of its children does.
+ * TimeAxisView is non-0 if this object covers @param y, or one of its children
+ * does. @param y is an offset from the top of the trackview area.
+ *
  * If the covering object is a child axis, then the child is returned.
  * TimeAxisView is 0 otherwise.
+ *
  * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
  * and is in stacked or expanded * region display mode, otherwise 0.
  */
index 38626a080d880b5dd9d30a360484b178d22a394e..4c183ddc6a6f0a84d842cb49650799c445f883ec 100644 (file)
@@ -59,7 +59,7 @@ namespace Gtk {
 
 namespace ArdourCanvas {
        class Canvas;
-       class Group;
+       class Container;
        class Item;
 }
 
@@ -104,8 +104,8 @@ class TimeAxisView : public virtual AxisView
         virtual void enter_internal_edit_mode () {}
         virtual void leave_internal_edit_mode () {}
 
-       ArdourCanvas::Group* canvas_display () { return _canvas_display; }
-       ArdourCanvas::Group* ghost_group () { return _ghost_group; }
+       ArdourCanvas::Container* canvas_display () { return _canvas_display; }
+       ArdourCanvas::Container* ghost_group () { return _ghost_group; }
 
        /** @return effective height (taking children into account) in canvas units, or
            0 if this TimeAxisView has not yet been shown */
@@ -212,15 +212,15 @@ class TimeAxisView : public virtual AxisView
        std::string            controls_base_selected_name;
        Gtk::Menu*             display_menu; /* The standard LHS Track control popup-menus */
        TimeAxisView*          parent;
-       ArdourCanvas::Group*   selection_group;
-       ArdourCanvas::Group*  _ghost_group;
+       ArdourCanvas::Container*   selection_group;
+       ArdourCanvas::Container*  _ghost_group;
        std::list<GhostRegion*> ghosts;
        std::list<SelectionRect*> free_selection_rects;
        std::list<SelectionRect*> used_selection_rects;
        bool                  _hidden;
        bool                   in_destructor;
        Gtk::Menu*            _size_menu;
-       ArdourCanvas::Group*  _canvas_display;
+       ArdourCanvas::Container*  _canvas_display;
        double                _y_position;
        PublicEditor&         _editor;
 
index b697db733f8254bd4f09687e1d8fab53a2338805..009dda1e6762c1bc66b47ab042ed9a692528e848 100644 (file)
 #include "gtkmm2ext/utils.h"
 #include "gtkmm2ext/gui_thread.h"
 
-#include "canvas/group.h"
+#include "canvas/container.h"
 #include "canvas/rectangle.h"
 #include "canvas/debug.h"
-#include "canvas/drag_handle.h"
 #include "canvas/text.h"
 #include "canvas/utils.h"
 
@@ -57,6 +56,7 @@ using namespace Editing;
 using namespace Glib;
 using namespace PBD;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace Gtkmm2ext;
 
 Pango::FontDescription TimeAxisViewItem::NAME_FONT;
@@ -73,7 +73,7 @@ double TimeAxisViewItem::NAME_HIGHLIGHT_THRESH;
 void
 TimeAxisViewItem::set_constant_heights ()
 {
-        NAME_FONT = get_font_for_style (X_("TimeAxisViewItemName"));
+        NAME_FONT = Pango::FontDescription (ARDOUR_UI::config()->get_canvasvar_SmallFont());
 
         Gtk::Window win;
         Gtk::Label foo;
@@ -120,7 +120,7 @@ TimeAxisViewItem::set_constant_heights ()
  * @param automation true if this is an automation region view
  */
 TimeAxisViewItem::TimeAxisViewItem(
-       const string & it_name, ArdourCanvas::Group& parent, TimeAxisView& tv, double spu, Gdk::Color const & base_color,
+       const string & it_name, ArdourCanvas::Item& parent, TimeAxisView& tv, double spu, uint32_t base_color,
        framepos_t start, framecnt_t duration, bool recording, bool automation, Visibility vis
        )
        : trackview (tv)
@@ -148,37 +148,30 @@ TimeAxisViewItem::TimeAxisViewItem (const TimeAxisViewItem& other)
        , _dragging (other._dragging)
        , _width (0.0)
 {
-
-       Gdk::Color c;
-       int r,g,b,a;
-
-       UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
-       c.set_rgb_p (r/255.0, g/255.0, b/255.0);
-
        /* share the other's parent, but still create a new group */
 
-       ArdourCanvas::Group* parent = other.group->parent();
+       ArdourCanvas::Item* parent = other.group->parent();
        
        _selected = other._selected;
        
-       init (parent, other.samples_per_pixel, c, other.frame_position,
+       init (parent, other.samples_per_pixel, other.fill_color, other.frame_position,
              other.item_duration, other.visibility, other.wide_enough_for_name, other.high_enough_for_name);
 }
 
 void
-TimeAxisViewItem::init (ArdourCanvas::Group* parent, double fpp, Gdk::Color const & base_color, 
+TimeAxisViewItem::init (ArdourCanvas::Item* parent, double fpp, uint32_t base_color, 
                        framepos_t start, framepos_t duration, Visibility vis, 
                        bool wide, bool high)
 {
-       group = new ArdourCanvas::Group (parent);
+       group = new ArdourCanvas::Container (parent);
        CANVAS_DEBUG_NAME (group, string_compose ("TAVI group for %1", get_item_name()));
        group->Event.connect (sigc::mem_fun (*this, &TimeAxisViewItem::canvas_group_event));
 
+       fill_color = base_color;
        samples_per_pixel = fpp;
        frame_position = start;
        item_duration = duration;
        name_connected = false;
-       fill_opacity = 60;
        position_locked = false;
        max_item_duration = ARDOUR::max_framepos;
        min_item_duration = 0;
@@ -262,6 +255,7 @@ TimeAxisViewItem::init (ArdourCanvas::Group* parent, double fpp, Gdk::Color cons
                        name_text->set_position (ArdourCanvas::Duple (NAME_X_OFFSET, NAME_Y_OFFSET));
                }
                name_text->set_font_description (NAME_FONT);
+               name_text->set_ignore_events (true);
        } else {
                name_text = 0;
        }
@@ -271,13 +265,13 @@ TimeAxisViewItem::init (ArdourCanvas::Group* parent, double fpp, Gdk::Color cons
                double top   = TimeAxisViewItem::GRAB_HANDLE_TOP;
                double width = TimeAxisViewItem::GRAB_HANDLE_WIDTH;
 
-               frame_handle_start = new ArdourCanvas::DragHandle (group, ArdourCanvas::Rect (0.0, top, width, trackview.current_height()), true);
+               frame_handle_start = new ArdourCanvas::Rectangle (group, ArdourCanvas::Rect (0.0, top, width, trackview.current_height()));
                CANVAS_DEBUG_NAME (frame_handle_start, "TAVI frame handle start");
                frame_handle_start->set_outline (false);
                frame_handle_start->set_fill (false);
                frame_handle_start->Event.connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisViewItem::frame_handle_crossing), frame_handle_start));
 
-               frame_handle_end = new ArdourCanvas::DragHandle (group, ArdourCanvas::Rect (0.0, top, width, trackview.current_height()), false);
+               frame_handle_end = new ArdourCanvas::Rectangle (group, ArdourCanvas::Rect (0.0, top, width, trackview.current_height()));
                CANVAS_DEBUG_NAME (frame_handle_end, "TAVI frame handle end");
                frame_handle_end->set_outline (false);
                frame_handle_end->set_fill (false);
@@ -548,6 +542,7 @@ TimeAxisViewItem::set_selected(bool yn)
        if (_selected != yn) {
                Selectable::set_selected (yn);
                set_frame_color ();
+               set_name_text_color ();
        }
 }
 
@@ -642,9 +637,9 @@ TimeAxisViewItem::manage_name_highlight ()
 }
 
 void
-TimeAxisViewItem::set_color (Gdk::Color const & base_color)
+TimeAxisViewItem::set_color (uint32_t base_color)
 {
-       compute_colors (base_color);
+       fill_color = base_color;
        set_colors ();
 }
 
@@ -654,7 +649,7 @@ TimeAxisViewItem::get_canvas_frame()
        return frame;
 }
 
-ArdourCanvas::Group*
+ArdourCanvas::Item*
 TimeAxisViewItem::get_canvas_group()
 {
        return group;
@@ -666,185 +661,93 @@ TimeAxisViewItem::get_name_highlight()
        return name_highlight;
 }
 
-/**
- * Calculate some contrasting color for displaying various parts of this item, based upon the base color.
- *
- * @param color the base color of the item
- */
-void
-TimeAxisViewItem::compute_colors (Gdk::Color const & base_color)
-{
-       unsigned char radius;
-       char minor_shift;
-
-       unsigned char r,g,b;
-
-       /* FILL: this is simple */
-       r = base_color.get_red()/256;
-       g = base_color.get_green()/256;
-       b = base_color.get_blue()/256;
-       fill_color = RGBA_TO_UINT(r,g,b,160);
-
-       /*  for minor colors:
-               if the overall saturation is strong, make the minor colors light.
-               if its weak, make them dark.
-
-               we do this by moving an equal distance to the other side of the
-               central circle in the color wheel from where we started.
-       */
-
-       radius = (unsigned char) rint (floor (sqrt (static_cast<double>(r*r + g*g + b+b))/3.0f));
-       minor_shift = 125 - radius;
-
-       /* LABEL: rotate around color wheel by 120 degrees anti-clockwise */
-
-       r = base_color.get_red()/256;
-       g = base_color.get_green()/256;
-       b = base_color.get_blue()/256;
-
-       if (r > b)
-       {
-               if (r > g)
-               {
-                       /* red sector => green */
-                       swap (r,g);
-               }
-               else
-               {
-                       /* green sector => blue */
-                       swap (g,b);
-               }
-       }
-       else
-       {
-               if (b > g)
-               {
-                       /* blue sector => red */
-                       swap (b,r);
-               }
-               else
-               {
-                       /* green sector => blue */
-                       swap (g,b);
-               }
-       }
-
-       r += minor_shift;
-       b += minor_shift;
-       g += minor_shift;
-
-       label_color = RGBA_TO_UINT(r,g,b,255);
-       r = (base_color.get_red()/256)   + 127;
-       g = (base_color.get_green()/256) + 127;
-       b = (base_color.get_blue()/256)  + 127;
-
-       label_color = RGBA_TO_UINT(r,g,b,255);
-
-       /* XXX can we do better than this ? */
-       /* We're trying;) */
-       /* NUKECOLORS */
-
-       //frame_color_r = 192;
-       //frame_color_g = 192;
-       //frame_color_b = 194;
-
-       //selected_frame_color_r = 182;
-       //selected_frame_color_g = 145;
-       //selected_frame_color_b = 168;
-
-       //handle_color_r = 25;
-       //handle_color_g = 0;
-       //handle_color_b = 255;
-       //lock_handle_color_r = 235;
-       //lock_handle_color_g = 16;
-       //lock_handle_color_b = 16;
-}
-
 /**
  * Convenience method to set the various canvas item colors
  */
 void
 TimeAxisViewItem::set_colors()
 {
-       set_frame_color();
+       set_frame_color ();
 
        if (name_highlight) {
                 name_highlight->set_fill_color (fill_color);
        }
 
-       if (name_text) {
-               double r, g, b, a;
-
-               const double black_r = 0.0;
-               const double black_g = 0.0;
-               const double black_b = 0.0;
-
-               const double white_r = 1.0;
-               const double white_g = 1.0;
-               const double white_b = 1.0;
-
-               ArdourCanvas::color_to_rgba (fill_color, r, g, b, a);
-               
-               /* Use W3C contrast guideline calculation */
-
-               double white_contrast = (max (r, white_r) - min (r, white_r)) +
-                       (max (g, white_g) - min (g, white_g)) + 
-                       (max (b, white_b) - min (b, white_b));
-
-               double black_contrast = (max (r, black_r) - min (r, black_r)) +
-                       (max (g, black_g) - min (g, black_g)) + 
-                       (max (b, black_b) - min (b, black_b));
-
-               if (white_contrast > black_contrast) {          
-                       /* use white */
-                       name_text->set_color (ArdourCanvas::rgba_to_color (1.0, 1.0, 1.0, 1.0));
-               } else {
-                       /* use black */
-                       name_text->set_color (ArdourCanvas::rgba_to_color (0.0, 0.0, 0.0, 1.0));
-               }
-
-#if 0
-               double h, s, v;
+       set_name_text_color ();
+       set_trim_handle_colors();
+}
 
-               ArdourCanvas::color_to_hsv (fill_color, h, s, v);
+void
+TimeAxisViewItem::set_name_text_color ()
+{
+       if (!name_text) {
+               return;
+       }
+       
 
-               if (v == 0.0) {
-                       /* fill is black, set text to white */
-                       name_text->set_color (ArdourCanvas::rgba_to_color (1.0, 1.0, 1.0, 1.0));
-               } else if (v == 1.0) {
-                       /* fill is white, set text to black */
-                       name_text->set_color (ArdourCanvas::rgba_to_color (0.0, 0.0, 0.0, 1.0));
-               } else {
+       uint32_t f;
+       
+       if (Config->get_show_name_highlight()) {
+               /* name text will always be on top of name highlight, which
+                  will always use our fill color.
+               */
+               f = fill_color;
+       } else {
+               /* name text will be on top of the item, whose color
+                  may vary depending on various conditions.
+               */
+               f = get_fill_color ();
+       }
 
-                       h = fabs (fmod ((h - 180), 360.0)); /* complementary color */
-                       s = 1.0; /* fully saturate */
-                       v = 0.9; /* increase lightness/brightness/value */
+       name_text->set_color (ArdourCanvas::contrasting_text_color (f));
+}
 
-                       name_text->set_color (ArdourCanvas::hsv_to_color (h, s, v, 1.0));
-               }
-#endif
+uint32_t
+TimeAxisViewItem::fill_opacity () const
+{
+       if (!rect_visible) {
+               /* if the frame/rect is marked as invisible, then the
+                * fill should be transparent. simplest: set
+                
+                * alpha/opacity to zero.
+                */
+               return 0;
+       }
 
+       if (_dragging) {
+               return 130;
        }
-       
-       set_trim_handle_colors();
+
+       uint32_t col = ARDOUR_UI::config()->get_canvasvar_FrameBase();
+       return UINT_RGBA_A (col);
 }
 
 uint32_t
 TimeAxisViewItem::get_fill_color () const
 {
-        uint32_t f = 0;
+        uint32_t f;
+       uint32_t o;
+
+       o = fill_opacity ();
 
        if (_selected) {
 
                 f = ARDOUR_UI::config()->get_canvasvar_SelectedFrameBase();
 
+               if (o == 0) {
+                       /* some condition of this item has set fill opacity to
+                        * zero, but it has been selected, so use a mid-way
+                        * alpha value to make it reasonably visible.
+                        */
+                       o = 130;
+               }
+               
        } else {
 
                if (_recregion) {
                        f = ARDOUR_UI::config()->get_canvasvar_RecordingRect();
                } else {
-
-                       if (high_enough_for_name && !ARDOUR_UI::config()->get_color_regions_using_track_color()) {
+                       if ((!Config->get_show_name_highlight() || high_enough_for_name) && !ARDOUR_UI::config()->get_color_regions_using_track_color()) {
                                f = ARDOUR_UI::config()->get_canvasvar_FrameBase();
                        } else {
                                f = fill_color;
@@ -852,7 +755,7 @@ TimeAxisViewItem::get_fill_color () const
                }
        }
 
-       return f;
+       return UINT_RGBA_CHANGE_A (f, o);
 }
 
 /**
@@ -861,26 +764,16 @@ TimeAxisViewItem::get_fill_color () const
 void
 TimeAxisViewItem::set_frame_color()
 {
-        uint32_t f = 0;
-
        if (!frame) {
                return;
        }
 
-       f = get_fill_color ();
-
-       if (fill_opacity) {
-               f = UINT_RGBA_CHANGE_A (f, fill_opacity);
-       }
-       
-       if (!rect_visible) {
-               f = UINT_RGBA_CHANGE_A (f, 0);
-       }
-
-        frame->set_fill_color (f);
+        frame->set_fill_color (get_fill_color());
        set_frame_gradient ();
 
         if (!_recregion) {
+               uint32_t f;
+
                 if (_selected) {
                         f = ARDOUR_UI::config()->get_canvasvar_SelectedTimeAxisFrame();
                 } else {
@@ -888,6 +781,7 @@ TimeAxisViewItem::set_frame_color()
                 }
 
                 if (!rect_visible) {
+                       /* make the frame outline be visible but rather transparent */
                         f = UINT_RGBA_CHANGE_A (f, 64);
                 }
 
@@ -1002,6 +896,7 @@ TimeAxisViewItem::reset_width_dependent_items (double pixel_width)
        _width = pixel_width;
 
        manage_name_highlight ();
+       manage_name_text ();
 
        if (pixel_width < 2.0) {
 
@@ -1110,3 +1005,17 @@ TimeAxisViewItem::parameter_changed (string p)
                set_frame_gradient ();
        }
 }
+
+void
+TimeAxisViewItem::drag_start ()
+{
+       _dragging = true;
+       set_frame_color ();
+}
+
+void
+TimeAxisViewItem::drag_end ()
+{
+       _dragging = false;
+       set_frame_color ();
+}
index ff353739950e6f66683e2100c41e6e3a82e8f141..e78b9c9488fab1c15f7232897361325622e132c1 100644 (file)
@@ -33,8 +33,8 @@ namespace ArdourCanvas {
        class Pixbuf;
        class Rectangle;
        class Item;
-       class Group;
-       class Text;
+        class Container;
+       class Text;
 }
 
 using ARDOUR::framepos_t;
@@ -71,21 +71,23 @@ class TimeAxisViewItem : public Selectable, public PBD::ScopedConnectionList
        TimeAxisView& get_time_axis_view () const;
        void set_name_text(const std::string&);
        virtual void set_height(double h);
+       virtual double height() const { return _height; }
        void set_y (double);
-       void set_color (Gdk::Color const &);
+       void set_color (uint32_t);
+       void set_name_text_color ();
 
         uint32_t get_fill_color () const;
 
        ArdourCanvas::Item* get_canvas_frame();
-       ArdourCanvas::Group* get_canvas_group();
+       ArdourCanvas::Item* get_canvas_group();
        ArdourCanvas::Item* get_name_highlight();
 
        virtual void set_samples_per_pixel (double);
 
        double get_samples_per_pixel () const;
 
-       virtual void drag_start() { _dragging = true; }
-       virtual void drag_end() { _dragging = false; }
+       virtual void drag_start();
+       virtual void drag_end();
        bool dragging() const { return _dragging; }
 
        virtual void raise () { return; }
@@ -168,16 +170,15 @@ class TimeAxisViewItem : public Selectable, public PBD::ScopedConnectionList
        };
 
   protected:
-       TimeAxisViewItem(const std::string &, ArdourCanvas::Group&, TimeAxisView&, double, Gdk::Color const &,
-                        framepos_t, framecnt_t, bool recording = false, bool automation = false, Visibility v = Visibility (0));
+       TimeAxisViewItem (const std::string &, ArdourCanvas::Item&, TimeAxisView&, double, uint32_t fill_color,
+                         framepos_t, framecnt_t, bool recording = false, bool automation = false, Visibility v = Visibility (0));
 
        TimeAxisViewItem (const TimeAxisViewItem&);
 
-        void init (ArdourCanvas::Group*, double, Gdk::Color const &, framepos_t, framepos_t, Visibility, bool, bool);
+        void init (ArdourCanvas::Item*, double, uint32_t, framepos_t, framepos_t, Visibility, bool, bool);
 
         virtual bool canvas_group_event (GdkEvent*);
 
-       virtual void compute_colors (Gdk::Color const &);
        virtual void set_colors();
        virtual void set_frame_color();
        virtual void set_frame_gradient ();
@@ -229,29 +230,17 @@ class TimeAxisViewItem : public Selectable, public PBD::ScopedConnectionList
        /** true if a small vestigial rect should be shown when the item gets very narrow */
        bool show_vestigial;
 
-       uint32_t fill_opacity;
        uint32_t fill_color;
-       uint32_t frame_color_r;
-       uint32_t frame_color_g;
-       uint32_t frame_color_b;
-       uint32_t selected_frame_color_r;
-       uint32_t selected_frame_color_g;
-       uint32_t selected_frame_color_b;
-       uint32_t label_color;
-
-       uint32_t handle_color_r;
-       uint32_t handle_color_g;
-       uint32_t handle_color_b;
-       uint32_t lock_handle_color_r;
-       uint32_t lock_handle_color_g;
-       uint32_t lock_handle_color_b;
+
+       virtual uint32_t fill_opacity() const;
+
        uint32_t last_item_width;
        int name_text_width;
        bool wide_enough_for_name;
        bool high_enough_for_name;
         bool rect_visible;
 
-       ArdourCanvas::Group*      group;
+       ArdourCanvas::Container*      group;
        ArdourCanvas::Rectangle* vestigial_frame;
        ArdourCanvas::Rectangle* frame;
        ArdourCanvas::Text*      name_text;
index d94ec3c8c1773bf39aa1edf6c09014a0704b35a9..2e9927ab420ad89a82e535c7a3eaa0eeab4e2266 100644 (file)
@@ -46,41 +46,54 @@ public:
 
        template <typename Function>
        void foreach_route_ui (Function f) {
-               for (iterator i = begin(); i != end(); ++i) {
+               for (iterator i = begin(); i != end(); ) {
+                       iterator tmp = i;
+                       ++tmp;
+
                        RouteUI* t = dynamic_cast<RouteUI*> (*i);
                        if (t) {
                                f (t);
                        }
+                       i = tmp;
                }
        }
 
        template <typename Function>
        void foreach_route_time_axis (Function f) {
-               for (iterator i = begin(); i != end(); ++i) {
+               for (iterator i = begin(); i != end(); ) {
+                       iterator tmp = i;
+                       ++tmp;
                        RouteTimeAxisView* t = dynamic_cast<RouteTimeAxisView*> (*i);
                        if (t) {
                                f (t);
                        }
+                       i = tmp;
                }
        }
 
        template <typename Function>
        void foreach_audio_time_axis (Function f) {
-               for (iterator i = begin(); i != end(); ++i) {
+               for (iterator i = begin(); i != end(); ) {
+                       iterator tmp = i;
+                       ++tmp;
                        AudioTimeAxisView* t = dynamic_cast<AudioTimeAxisView*> (*i);
                        if (t) {
                                f (t);
                        }
+                       i = tmp;
                }
        }
 
        template <typename Function>
        void foreach_midi_time_axis (Function f) {
-               for (iterator i = begin(); i != end(); ++i) {
+               for (iterator i = begin(); i != end(); ) {
+                       iterator tmp = i;
+                       ++tmp;
                        MidiTimeAxisView* t = dynamic_cast<MidiTimeAxisView*> (*i);
                        if (t) {
                                f (t);
                        }
+                       i = tmp;
                }
        }
 
index 4a0fc4b342670025f35ca4afb73d794523fa42c6..cee42163f117c5fd100e1fbfbaffd6e7171a02a7 100644 (file)
@@ -52,7 +52,7 @@ TranscodeFfmpeg::TranscodeFfmpeg (std::string f)
 #endif
 
        std::string ff_file_path;
-       if (find_file_in_search_path (Searchpath(Glib::getenv("PATH")), X_("ffmpeg_harvid"), ff_file_path)) { ffmpeg_exe = ff_file_path; }
+       if (find_file (Searchpath(Glib::getenv("PATH")), X_("ffmpeg_harvid"), ff_file_path)) { ffmpeg_exe = ff_file_path; }
        else if (Glib::file_test(X_("C:\\Program Files\\harvid\\ffmpeg.exe"), Glib::FILE_TEST_EXISTS)) {
                ffmpeg_exe = X_("C:\\Program Files\\ffmpeg\\ffmpeg.exe");
        }
@@ -60,7 +60,7 @@ TranscodeFfmpeg::TranscodeFfmpeg (std::string f)
                ffmpeg_exe = X_("C:\\Program Files\\ffmpeg\\ffmpeg.exe");
        }
 
-       if (find_file_in_search_path (Searchpath(Glib::getenv("PATH")), X_("ffprobe_harvid"), ff_file_path)) { ffprobe_exe = ff_file_path; }
+       if (find_file (Searchpath(Glib::getenv("PATH")), X_("ffprobe_harvid"), ff_file_path)) { ffprobe_exe = ff_file_path; }
        else if (Glib::file_test(X_("C:\\Program Files\\harvid\\ffprobe.exe"), Glib::FILE_TEST_EXISTS)) {
                ffprobe_exe = X_("C:\\Program Files\\ffmpeg\\ffprobe.exe");
        }
@@ -71,17 +71,19 @@ TranscodeFfmpeg::TranscodeFfmpeg (std::string f)
        if (ffmpeg_exe.empty() || ffprobe_exe.empty()) {
                warning << string_compose(
                                _(
-                               "No ffprobe or ffmpeg executables could be found on this system.\n"
-                               "Video import and export is not possible until you install those tools.\n"
-                               "%1 requires ffmpeg and ffprobe from ffmpeg.org - version 1.1 or newer.\n"
-                               "\n"
-                               "The tools are included with the %1 releases from ardour.org "
-                               "and also available with the video-server at http://x42.github.com/harvid/\n"
-                               "\n"
-                               "Important: the files need to be installed in $PATH and named ffmpeg_harvid and ffprobe_harvid.\n"
-                               "If you already have a suitable ffmpeg installation on your system, we recommend creating "
-                               "symbolic links from ffmpeg to ffmpeg_harvid and from ffprobe to ffprobe_harvid.\n"
-                               ), PROGRAM_NAME) << endmsg;
+                                       "No ffprobe or ffmpeg executables could be found on this system.\n"
+                                       "Video import and export is not possible until you install those tools.\n"
+                                       "%1 requires ffmpeg and ffprobe from ffmpeg.org - version 1.1 or newer.\n"
+                                       "\n"
+                                       "The tools are included with the %1 releases from ardour.org "
+                                       "and also available with the video-server at http://x42.github.com/harvid/\n"
+                                       "\n"
+                                       "Important: the files need to be installed in $PATH and named ffmpeg_harvid and ffprobe_harvid.\n"
+                                       "If you already have a suitable ffmpeg installation on your system, we recommend creating "
+                                       "symbolic links from ffmpeg to ffmpeg_harvid and from ffprobe to ffprobe_harvid.\n"
+                                       "\n"
+                                       "see also http://manual.ardour.org/video-timeline/setup/"
+                                ), PROGRAM_NAME) << endmsg;
                return;
        }
        ffexecok = true;
index 7917668d1b13a47f561762926105f1ca2222bcad..16bfc36995c0268145c9c7aefea0cf4e8080de94 100644 (file)
@@ -41,7 +41,6 @@
 #include "ardour_ui.h"
 #include "gui_thread.h"
 
-#include "utils.h"
 #include "opts.h"
 #include "transcode_video_dialog.h"
 #include "utils_videotl.h"
index 215df25e7f2ea179c79d1a834d091c4b6a1837c4..71e0808c63902190ad8083b7bb95a1d21a22be2e 100644 (file)
@@ -46,10 +46,14 @@ UIConfiguration::UIConfiguration ()
 #undef  CANVAS_VARIABLE
 #define UI_CONFIG_VARIABLE(Type,var,name,val) var (name,val),
 #define CANVAS_VARIABLE(var,name) var (name),
+#define CANVAS_STRING_VARIABLE(var,name) var (name),
+#define CANVAS_FONT_VARIABLE(var,name) var (name),
 #include "ui_config_vars.h"
 #include "canvas_vars.h"
 #undef  UI_CONFIG_VARIABLE
 #undef  CANVAS_VARIABLE
+#undef  CANVAS_STRING_VARIABLE
+#undef  CANVAS_FONT_VARIABLE
        _dirty (false)
 {
        load_state();
@@ -82,7 +86,7 @@ UIConfiguration::load_defaults ()
                rcfile = "ardour3_ui_default.conf";
        }
 
-       if (find_file_in_search_path (ardour_config_search_path(), rcfile, default_ui_rc_file) ) {
+       if (find_file (ardour_config_search_path(), rcfile, default_ui_rc_file) ) {
                XMLTree tree;
                found = 1;
 
@@ -113,7 +117,7 @@ UIConfiguration::load_state ()
 
        std::string default_ui_rc_file;
 
-       if ( find_file_in_search_path (ardour_config_search_path(), "ardour3_ui_default.conf", default_ui_rc_file)) {
+       if ( find_file (ardour_config_search_path(), "ardour3_ui_default.conf", default_ui_rc_file)) {
                XMLTree tree;
                found = true;
 
@@ -134,7 +138,7 @@ UIConfiguration::load_state ()
 
        std::string user_ui_rc_file;
 
-       if (find_file_in_search_path (ardour_config_search_path(), "ardour3_ui.conf", user_ui_rc_file)) {
+       if (find_file (ardour_config_search_path(), "ardour3_ui.conf", user_ui_rc_file)) {
                XMLTree tree;
                found = true;
 
@@ -215,10 +219,14 @@ UIConfiguration::get_variables (std::string which_node)
 #undef  CANVAS_VARIABLE
 #define UI_CONFIG_VARIABLE(Type,var,Name,value) if (node->name() == "UI") { var.add_to_node (*node); }
 #define CANVAS_VARIABLE(var,Name) if (node->name() == "Canvas") { var.add_to_node (*node); }
+#define CANVAS_STRING_VARIABLE(var,Name) if (node->name() == "Canvas") { var.add_to_node (*node); }
+#define CANVAS_FONT_VARIABLE(var,Name) if (node->name() == "Canvas") { var.add_to_node (*node); }
 #include "ui_config_vars.h"
 #include "canvas_vars.h"
 #undef  UI_CONFIG_VARIABLE
 #undef  CANVAS_VARIABLE
+#undef  CANVAS_STRING_VARIABLE
+#undef  CANVAS_FONT_VARIABLE
 
        return *node;
 }
@@ -262,25 +270,39 @@ UIConfiguration::set_variables (const XMLNode& node)
          if (var.set_from_node (node)) { \
                 ParameterChanged (name); \
                 }
+#define CANVAS_STRING_VARIABLE(var,name)       \
+         if (var.set_from_node (node)) { \
+                ParameterChanged (name); \
+                }
+#define CANVAS_FONT_VARIABLE(var,name) \
+         if (var.set_from_node (node)) { \
+                ParameterChanged (name); \
+                }
 #include "ui_config_vars.h"
 #include "canvas_vars.h"
 #undef  UI_CONFIG_VARIABLE
 #undef  CANVAS_VARIABLE
+#undef  CANVAS_STRING_VARIABLE
+#undef  CANVAS_FONT_VARIABLE
 }
 
 void
 UIConfiguration::pack_canvasvars ()
 {
 #undef  CANVAS_VARIABLE
-#define CANVAS_VARIABLE(var,name) canvas_colors.insert (std::pair<std::string,UIConfigVariable<uint32_t>* >(name,&var));
+#define CANVAS_VARIABLE(var,name) canvas_colors.insert (std::pair<std::string,ColorVariable<uint32_t>* >(name,&var));
+#define CANVAS_STRING_VARIABLE(var,name) 
+#define CANVAS_FONT_VARIABLE(var,name) 
 #include "canvas_vars.h"
 #undef  CANVAS_VARIABLE
+#undef  CANVAS_STRING_VARIABLE
+#undef  CANVAS_FONT_VARIABLE
 }
 
 uint32_t
 UIConfiguration::color_by_name (const std::string& name)
 {
-       map<std::string,UIConfigVariable<uint32_t>* >::iterator i = canvas_colors.find (name);
+       map<std::string,ColorVariable<uint32_t>* >::iterator i = canvas_colors.find (name);
 
        if (i != canvas_colors.end()) {
                return i->second->get();
index bfa8859afdc671994b52935dfaae33a06e604bf5..0000eb63f50652fd0d21ab0240860fe67f18084b 100644 (file)
 #include "pbd/xml++.h"
 #include "ardour/configuration_variable.h"
 
+#include "utils.h"
+
+/* This is very similar to ARDOUR::ConfigVariable but expects numeric values to
+ * be in hexadecimal. This is because it is intended for use with color
+ * specifications which are easier to scan for issues in "rrggbbaa" format than
+ * as decimals.
+ */
 template<class T>
-class UIConfigVariable : public ARDOUR::ConfigVariableBase
+class ColorVariable : public ARDOUR::ConfigVariableBase
 {
   public:
-       UIConfigVariable (std::string str) : ARDOUR::ConfigVariableBase (str) {}
-       UIConfigVariable (std::string str, T val) : ARDOUR::ConfigVariableBase (str), value (val) {}
+       ColorVariable (std::string str) : ARDOUR::ConfigVariableBase (str) {}
+       ColorVariable (std::string str, T val) : ARDOUR::ConfigVariableBase (str), value (val) {}
 
        bool set (T val) {
                if (val == value) {
@@ -74,7 +81,7 @@ class UIConfiguration : public PBD::Stateful
        UIConfiguration();
        ~UIConfiguration();
 
-       std::map<std::string,UIConfigVariable<uint32_t> *> canvas_colors;
+       std::map<std::string,ColorVariable<uint32_t> *> canvas_colors;
 
        bool dirty () const;
        void set_dirty ();
@@ -101,25 +108,38 @@ class UIConfiguration : public PBD::Stateful
 #include "ui_config_vars.h"
 #undef  UI_CONFIG_VARIABLE
 #undef CANVAS_VARIABLE
+#undef CANVAS_STRING_VARIABLE
 #define CANVAS_VARIABLE(var,name) \
        uint32_t get_##var () const { return var.get(); } \
        bool set_##var (uint32_t val) { bool ret = var.set (val); if (ret) { ParameterChanged (name); } return ret;  }
+#define CANVAS_STRING_VARIABLE(var,name) \
+       std::string get_##var () const { return var.get(); }                    \
+       bool set_##var (const std::string& val) { bool ret = var.set (val); if (ret) { ParameterChanged (name); } return ret;  }
+#define CANVAS_FONT_VARIABLE(var,name) \
+       Pango::FontDescription get_##var () const { return ARDOUR_UI_UTILS::sanitized_font (var.get()); } \
+       bool set_##var (const std::string& val) { bool ret = var.set (val); if (ret) { ParameterChanged (name); } return ret;  }
 #include "canvas_vars.h"
 #undef  CANVAS_VARIABLE
+#undef CANVAS_STRING_VARIABLE
+#undef CANVAS_FONT_VARIABLE
 
   private:
 
        /* declare variables */
 
 #undef  UI_CONFIG_VARIABLE
-#define UI_CONFIG_VARIABLE(Type,var,name,value) UIConfigVariable<Type> var;
+#define UI_CONFIG_VARIABLE(Type,var,name,value) ARDOUR::ConfigVariable<Type> var;
 #include "ui_config_vars.h"
 #undef UI_CONFIG_VARIABLE
 
 #undef CANVAS_VARIABLE
-#define CANVAS_VARIABLE(var,name) UIConfigVariable<uint32_t> var;
+#define CANVAS_VARIABLE(var,name) ColorVariable<uint32_t> var;
+#define CANVAS_STRING_VARIABLE(var,name) ARDOUR::ConfigVariable<std::string> var;
+#define CANVAS_FONT_VARIABLE(var,name) ARDOUR::ConfigVariable<std::string> var;
 #include "canvas_vars.h"
 #undef  CANVAS_VARIABLE
+#undef CANVAS_STRING_VARIABLE
+#undef CANVAS_FONT_VARIABLE
 
        XMLNode& state ();
        bool _dirty;
index 18bda554574bcf98727e7d4d55453746b616837a..44c2837abe87bfa2df2a923c037989a2836550e9 100644 (file)
 
 */
 
+UI_CONFIG_VARIABLE(std::string, icon_set, "icon-set", "default")
 UI_CONFIG_VARIABLE(std::string, ui_rc_file, "ui-rc-file", "ardour3_ui_dark.rc")
 UI_CONFIG_VARIABLE(bool, flat_buttons, "flat-buttons", false)
-UI_CONFIG_VARIABLE(float, waveform_gradient_depth, "waveform-gradient-depth", 0.6)
-UI_CONFIG_VARIABLE(float, timeline_item_gradient_depth, "timeline-item-gradient-depth", 1.3)
+UI_CONFIG_VARIABLE(float, waveform_gradient_depth, "waveform-gradient-depth", 0)
+UI_CONFIG_VARIABLE(float, timeline_item_gradient_depth, "timeline-item-gradient-depth", 0.5)
 UI_CONFIG_VARIABLE(bool, all_floating_windows_are_dialogs, "all-floating-windows-are-dialogs", false)
 UI_CONFIG_VARIABLE (bool, color_regions_using_track_color, "color-regions-using-track-color", false)
 UI_CONFIG_VARIABLE (bool, show_waveform_clipping, "show-waveform-clipping", true)
+UI_CONFIG_VARIABLE (uint32_t, lock_gui_after_seconds, "lock-gui-after-seconds", 0)
+UI_CONFIG_VARIABLE (bool, draggable_playhead, "draggable-playhead", true)
 
index a8b46f1f57853496f91d028ce831208b2e5961ae..417e9504312cc91fdfaa56c0791769d1642c94f6 100644 (file)
@@ -45,7 +45,9 @@
 #include <gtkmm2ext/utils.h>
 #include "ardour/rc_configuration.h"
 #include "ardour/filesystem_paths.h"
+
 #include "canvas/item.h"
+#include "canvas/utils.h"
 
 #include "ardour_ui.h"
 #include "debug.h"
@@ -62,7 +64,9 @@ using namespace Glib;
 using namespace PBD;
 using Gtkmm2ext::Keyboard;
 
-sigc::signal<void>  DPIReset;
+namespace ARDOUR_UI_UTILS {
+       sigc::signal<void>  DPIReset;
+}
 
 #ifdef PLATFORM_WINDOWS
 #define random() rand()
@@ -75,7 +79,7 @@ sigc::signal<void>  DPIReset;
  * @param s true to make sensitive, false to make insensitive
  */
 void
-add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, bool s)
+ARDOUR_UI_UTILS::add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, bool s)
 {
        m.push_back (e);
        if (!s) {
@@ -85,7 +89,7 @@ add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e,
 
 
 gint
-just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win)
+ARDOUR_UI_UTILS::just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win)
 {
        win->hide ();
        return 0;
@@ -100,7 +104,7 @@ just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win)
 */
 
 unsigned char*
-xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
+ARDOUR_UI_UTILS::xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
 {
        static long vals[256], val;
        uint32_t t, x, y, colors, cpp;
@@ -142,7 +146,7 @@ xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
 }
 
 unsigned char*
-xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
+ARDOUR_UI_UTILS::xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
 {
        static long vals[256], val;
        uint32_t t, x, y, colors, cpp;
@@ -204,8 +208,34 @@ xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
        return (savergb);
 }
 
+/** Returns a Pango::FontDescription given a string describing the font. 
+ *
+ * If the returned FontDescription does not specify a family, then
+ * the family is set to "Sans". This mirrors GTK's behaviour in
+ * gtkstyle.c. 
+ *
+ * Some environments will force Pango to specify the family
+ * even if it was not specified in the string describing the font.
+ * Such environments should be left unaffected by this function, 
+ * since the font family will be left alone.
+ *
+ * There may be other similar font specification enforcement
+ * that we might add here later.
+ */
+Pango::FontDescription
+ARDOUR_UI_UTILS::sanitized_font (std::string const& name)
+{
+       Pango::FontDescription fd (name);
+
+       if (fd.get_family().empty()) {
+               fd.set_family ("Sans");
+       }
+
+       return fd;
+}
+
 Pango::FontDescription
-get_font_for_style (string widgetname)
+ARDOUR_UI_UTILS::get_font_for_style (string widgetname)
 {
        Gtk::Window window (WINDOW_TOPLEVEL);
        Gtk::Label foobar;
@@ -234,7 +264,7 @@ get_font_for_style (string widgetname)
 }
 
 uint32_t
-rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
+ARDOUR_UI_UTILS::rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
 {
        /* In GTK+2, styles aren't set up correctly if the widget is not
           attached to a toplevel window that has a screen pointer.
@@ -288,12 +318,12 @@ rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, s
        if (state == Gtk::STATE_NORMAL && rgba) {
                return (uint32_t) RGBA_TO_UINT(r,g,b,a);
        } else {
-               return (uint32_t) RGB_TO_UINT(r,g,b);
+               return (uint32_t) RGBA_TO_UINT(r,g,b,255);
        }
 }
 
 bool
-rgba_p_from_style (string style, float *r, float *g, float *b, string attr, int state)
+ARDOUR_UI_UTILS::rgba_p_from_style (string style, float *r, float *g, float *b, string attr, int state)
 {
        static Gtk::Window* window = 0;
        assert (r && g && b);
@@ -340,13 +370,41 @@ rgba_p_from_style (string style, float *r, float *g, float *b, string attr, int
 }
 
 void
-set_color (Gdk::Color& c, int rgb)
+ARDOUR_UI_UTILS::set_color_from_rgb (Gdk::Color& c, uint32_t rgb)
+{
+       /* Gdk::Color color ranges are 16 bit, so scale from 8 bit by
+          multiplying by 256.
+       */
+       c.set_rgb ((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
+}
+
+void
+ARDOUR_UI_UTILS::set_color_from_rgba (Gdk::Color& c, uint32_t rgba)
+{
+       /* Gdk::Color color ranges are 16 bit, so scale from 8 bit by
+          multiplying by 256.
+       */
+       c.set_rgb ((rgba >> 24)*256, ((rgba & 0xff0000) >> 16)*256, ((rgba & 0xff00) >> 8)*256);
+}
+
+uint32_t
+ARDOUR_UI_UTILS::gdk_color_to_rgba (Gdk::Color const& c)
 {
-       c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
+       /* since alpha value is not available from a Gdk::Color, it is
+          hardcoded as 0xff (aka 255 or 1.0)
+       */
+
+       const uint32_t r = c.get_red_p () * 255.0;
+       const uint32_t g = c.get_green_p () * 255.0;
+       const uint32_t b = c.get_blue_p () * 255.0;
+       const uint32_t a = 0xff;
+
+       return RGBA_TO_UINT (r,g,b,a);
 }
 
+
 bool
-relay_key_press (GdkEventKey* ev, Gtk::Window* win)
+ARDOUR_UI_UTILS::relay_key_press (GdkEventKey* ev, Gtk::Window* win)
 {
        PublicEditor& ed (PublicEditor::instance());
 
@@ -362,13 +420,13 @@ relay_key_press (GdkEventKey* ev, Gtk::Window* win)
 }
 
 bool
-forward_key_press (GdkEventKey* ev)
+ARDOUR_UI_UTILS::forward_key_press (GdkEventKey* ev)
 {
-        return PublicEditor::instance().on_key_press_event(ev);
+       return PublicEditor::instance().on_key_press_event(ev);
 }
 
 bool
-emulate_key_event (Gtk::Widget* w, unsigned int keyval)
+ARDOUR_UI_UTILS::emulate_key_event (Gtk::Widget* w, unsigned int keyval)
 {
        GdkDisplay  *display = gtk_widget_get_display (GTK_WIDGET(w->gobj()));
        GdkKeymap   *keymap  = gdk_keymap_get_for_display (display);
@@ -397,7 +455,7 @@ emulate_key_event (Gtk::Widget* w, unsigned int keyval)
 }
 
 bool
-key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
+ARDOUR_UI_UTILS::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
 {
        GtkWindow* win = window.gobj();
        GtkWidget* focus = gtk_window_get_focus (win);
@@ -568,7 +626,7 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
 }
 
 Glib::RefPtr<Gdk::Pixbuf>
-get_xpm (std::string name)
+ARDOUR_UI_UTILS::get_xpm (std::string name)
 {
        if (!xpm_map[name]) {
 
@@ -578,7 +636,7 @@ get_xpm (std::string name)
 
                std::string data_file_path;
 
-               if(!find_file_in_search_path (spath, name, data_file_path)) {
+               if(!find_file (spath, name, data_file_path)) {
                        fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
                }
 
@@ -592,25 +650,83 @@ get_xpm (std::string name)
        return xpm_map[name];
 }
 
+vector<string>
+ARDOUR_UI_UTILS::get_icon_sets ()
+{
+       Searchpath spath(ARDOUR::ardour_data_search_path());
+       spath.add_subdirectory_to_paths ("icons");
+       vector<string> r;
+       
+       r.push_back (_("default"));
+
+       for (vector<string>::iterator s = spath.begin(); s != spath.end(); ++s) {
+
+               vector<string> entries;
+
+               get_paths (entries, *s, false, false);
+
+               for (vector<string>::iterator e = entries.begin(); e != entries.end(); ++e) {
+                       if (Glib::file_test (*e, Glib::FILE_TEST_IS_DIR)) {
+                               r.push_back (Glib::filename_to_utf8 (Glib::path_get_basename(*e)));
+                       }
+               }
+       }
+
+       return r;
+}
+
 std::string
-get_icon_path (const char* cname)
+ARDOUR_UI_UTILS::get_icon_path (const char* cname, string icon_set)
 {
+       std::string data_file_path;
        string name = cname;
        name += X_(".png");
 
        Searchpath spath(ARDOUR::ardour_data_search_path());
 
-       spath.add_subdirectory_to_paths("icons");
-
-       std::string data_file_path;
+       if (!icon_set.empty() && icon_set != _("default")) {
 
-       if (!find_file_in_search_path (spath, name, data_file_path)) {
-               fatal << string_compose (_("cannot find icon image for %1 using %2"), name, spath.to_string()) << endmsg;
+               /* add "icons/icon_set" but .. not allowed to add both of these at once */
+               spath.add_subdirectory_to_paths ("icons");
+               spath.add_subdirectory_to_paths (icon_set);
+               
+               find_file (spath, name, data_file_path);
+       }
+       
+       if (data_file_path.empty()) {
+               
+               if (!icon_set.empty() && icon_set != _("default")) {
+                       warning << string_compose (_("icon \"%1\" not found for icon set \"%2\", fallback to default"), cname, icon_set) << endmsg;
+               }
+               
+               Searchpath def (ARDOUR::ardour_data_search_path());
+               def.add_subdirectory_to_paths ("icons");
+       
+               if (!find_file (def, name, data_file_path)) {
+                       fatal << string_compose (_("cannot find icon image for %1 using %2"), name, spath.to_string()) << endmsg;
+                       /*NOTREACHED*/
+               }
        }
 
        return data_file_path;
 }
 
+Glib::RefPtr<Gdk::Pixbuf>
+ARDOUR_UI_UTILS::get_icon (const char* cname, string icon_set)
+{
+       Glib::RefPtr<Gdk::Pixbuf> img;
+       try {
+               img = Gdk::Pixbuf::create_from_file (get_icon_path (cname, icon_set));
+       } catch (const Gdk::PixbufError &e) {
+               cerr << "Caught PixbufError: " << e.what() << endl;
+       } catch (...) {
+               error << string_compose (_("Caught exception while loading icon named %1"), cname) << endmsg;
+       }
+
+       return img;
+}
+
+namespace ARDOUR_UI_UTILS {
 Glib::RefPtr<Gdk::Pixbuf>
 get_icon (const char* cname)
 {
@@ -625,9 +741,10 @@ get_icon (const char* cname)
 
        return img;
 }
+}
 
 string
-longest (vector<string>& strings)
+ARDOUR_UI_UTILS::longest (vector<string>& strings)
 {
        if (strings.empty()) {
                return string ("");
@@ -655,7 +772,7 @@ longest (vector<string>& strings)
 }
 
 bool
-key_is_legal_for_numeric_entry (guint keyval)
+ARDOUR_UI_UTILS::key_is_legal_for_numeric_entry (guint keyval)
 {
        /* we assume that this does not change over the life of the process 
         */
@@ -738,8 +855,9 @@ key_is_legal_for_numeric_entry (guint keyval)
 
        return false;
 }
+
 void
-set_pango_fontsize ()
+ARDOUR_UI_UTILS::set_pango_fontsize ()
 {
        long val = ARDOUR::Config->get_font_scale();
 
@@ -755,7 +873,7 @@ set_pango_fontsize ()
 }
 
 void
-reset_dpi ()
+ARDOUR_UI_UTILS::reset_dpi ()
 {
        long val = ARDOUR::Config->get_font_scale();
        set_pango_fontsize ();
@@ -767,7 +885,7 @@ reset_dpi ()
 }
 
 void
-resize_window_to_proportion_of_monitor (Gtk::Window* window, int max_width, int max_height)
+ARDOUR_UI_UTILS::resize_window_to_proportion_of_monitor (Gtk::Window* window, int max_width, int max_height)
 {
        Glib::RefPtr<Gdk::Screen> screen = window->get_screen ();
        Gdk::Rectangle monitor_rect;
@@ -782,7 +900,7 @@ resize_window_to_proportion_of_monitor (Gtk::Window* window, int max_width, int
 
 /** Replace _ with __ in a string; for use with menu item text to make underscores displayed correctly */
 string
-escape_underscores (string const & s)
+ARDOUR_UI_UTILS::escape_underscores (string const & s)
 {
        string o;
        string::size_type const N = s.length ();
@@ -800,7 +918,7 @@ escape_underscores (string const & s)
 
 /** Replace < and > with &lt; and &gt; respectively to make < > display correctly in markup strings */
 string
-escape_angled_brackets (string const & s)
+ARDOUR_UI_UTILS::escape_angled_brackets (string const & s)
 {
        string o = s;
        boost::replace_all (o, "<", "&lt;");
@@ -809,7 +927,7 @@ escape_angled_brackets (string const & s)
 }
 
 Gdk::Color
-unique_random_color (list<Gdk::Color>& used_colors)
+ARDOUR_UI_UTILS::unique_random_color (list<Gdk::Color>& used_colors)
 {
        Gdk::Color newcolor;
 
@@ -850,7 +968,7 @@ unique_random_color (list<Gdk::Color>& used_colors)
 }
 
 string 
-rate_as_string (float r)
+ARDOUR_UI_UTILS::rate_as_string (float r)
 {
        char buf[32];
        if (fmod (r, 1000.0f)) {
@@ -860,3 +978,28 @@ rate_as_string (float r)
        }
        return buf;
 }
+
+
+string
+ARDOUR_UI_UTILS::track_number_to_string (
+               int64_t tracknumber,
+               std::string sep,
+               std::string postfix
+               )
+{
+       string rv;
+       if (tracknumber > 0) {
+               rv = "<span weight=\"bold\" font_family=\"ArdourMono, Mono\">";
+               rv += PBD::to_string (tracknumber, std::dec);
+               rv += "</span>";
+               rv += sep;
+       }
+       else if (tracknumber < 0) {
+               rv = "<span weight=\"bold\" font_family=\"ArdourMono, Mono\">";
+               rv += PBD::to_string (-tracknumber, std::dec);
+               rv += "</span>";
+               rv += sep;
+       }
+       rv += Glib::Markup::escape_text(postfix);
+       return rv;
+}
index 30ccd9c43d53d1dd0711ed57f5991f6f55b98424..59f986e81d61f53985e59ccaf4a575ab96c74fc7 100644 (file)
@@ -47,6 +47,8 @@ namespace ArdourCanvas {
        class Item;
 }
 
+namespace ARDOUR_UI_UTILS {
+
 extern sigc::signal<void>  DPIReset;
 
 gint   just_hide_it (GdkEventAny*, Gtk::Window*);
@@ -57,6 +59,7 @@ unsigned char* xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h);
 
 ArdourCanvas::Points* get_canvas_points (std::string who, uint32_t npoints);
 
+Pango::FontDescription sanitized_font (std::string const&);
 Pango::FontDescription get_font_for_style (std::string widgetname);
 
 uint32_t rgba_from_style (std::string, uint32_t, uint32_t, uint32_t, uint32_t, std::string = "fg", int = Gtk::STATE_NORMAL, bool = true);
@@ -65,7 +68,10 @@ bool rgba_p_from_style (std::string, float*, float*, float*, std::string = "fg",
 
 void decorate (Gtk::Window& w, Gdk::WMDecoration d);
 
-void set_color (Gdk::Color&, int);
+void set_color_from_rgb (Gdk::Color&, uint32_t);
+void set_color_from_rgba (Gdk::Color&, uint32_t);
+uint32_t gdk_color_to_rgba (Gdk::Color const&);
+uint32_t contrasting_text_color (uint32_t c);
 
 bool relay_key_press (GdkEventKey* ev, Gtk::Window* win);
 bool forward_key_press (GdkEventKey* ev);
@@ -73,8 +79,9 @@ bool key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev);
 bool emulate_key_event (Gtk::Widget*, unsigned int);
 
 Glib::RefPtr<Gdk::Pixbuf> get_xpm (std::string);
-std::string get_icon_path (const char*);
-Glib::RefPtr<Gdk::Pixbuf> get_icon (const char*);
+std::vector<std::string> get_icon_sets ();
+std::string get_icon_path (const char*, std::string icon_set = std::string());
+Glib::RefPtr<Gdk::Pixbuf> get_icon (const char*, std::string icon_set = std::string());
 static std::map<std::string, Glib::RefPtr<Gdk::Pixbuf> > xpm_map;
 const char* const *get_xpm_data (std::string path);
 std::string longest (std::vector<std::string>&);
@@ -91,4 +98,7 @@ Gdk::Color unique_random_color (std::list<Gdk::Color> &);
 
 std::string rate_as_string (float r);
 
+std::string track_number_to_string (int64_t tracknumber, std::string sep = "", std::string postfix = "");
+
+} // namespace
 #endif /* __ardour_gtk_utils_h__ */
index fec9e80ae11d82b59a1753cda04d29a7b0d467ec..48ac41c694698a8309ac9b54ea89b6a5165c39d5 100644 (file)
 #include "ardour/profile.h"
 
 #include "canvas/debug.h"
+#include "canvas/scroll_group.h"
+#include "canvas/tracking_text.h"
 
 #include "ardour_ui.h"
 #include "audio_clock.h"
 #include "editor.h"
 #include "editor_drag.h"
+#include "global_signals.h"
 #include "main_clock.h"
-#include "utils.h"
 #include "verbose_cursor.h"
 
 #include "i18n.h"
@@ -39,77 +41,59 @@ using namespace ARDOUR;
 
 VerboseCursor::VerboseCursor (Editor* editor)
        : _editor (editor)
-       , _visible (false)
-       , _xoffset (0)
-       , _yoffset (0)
 {
-       _canvas_item = new ArdourCanvas::Text (_editor->_track_canvas->root());
+       _canvas_item = new ArdourCanvas::TrackingText (_editor->get_noscroll_group());
        CANVAS_DEBUG_NAME (_canvas_item, "verbose canvas cursor");
-       _canvas_item->set_ignore_events (true);
-       _canvas_item->set_font_description (get_font_for_style (N_("VerboseCanvasCursor")));
+       _canvas_item->set_font_description (Pango::FontDescription (ARDOUR_UI::config()->get_canvasvar_LargerBoldFont()));
+       color_handler ();
+
+       ARDOUR_UI_UTILS::ColorsChanged.connect (sigc::mem_fun (*this, &VerboseCursor::color_handler));
 }
 
-ArdourCanvas::Item *
-VerboseCursor::canvas_item () const
+void
+VerboseCursor::color_handler ()
 {
-       return _canvas_item;
+       _canvas_item->set_color (ARDOUR_UI::config()->get_canvasvar_VerboseCanvasCursor());
 }
 
-void
-VerboseCursor::set (string const & text, double x, double y)
+ArdourCanvas::Item *
+VerboseCursor::canvas_item () const
 {
-       set_text (text);
-       set_position (x, y);
+       return _canvas_item;
 }
 
+/** Set the contents of the cursor.
+ */
 void
-VerboseCursor::set_text (string const & text)
+VerboseCursor::set (string const & text)
 {
        _canvas_item->set (text);
 }
 
-/** @param xoffset x offset to be applied on top of any set_position() call
- *  before the next show ().
- *  @param yoffset y offset as above.
- */
 void
-VerboseCursor::show (double xoffset, double yoffset)
+VerboseCursor::show ()
 {
-       _xoffset = xoffset;
-       _yoffset = yoffset;
-
-       if (_visible) {
-               return;
-       }
-
-       _canvas_item->raise_to_top ();
-       _canvas_item->show ();
-       _visible = true;
+       _canvas_item->show_and_track (true, true);
+       _canvas_item->parent()->raise_to_top ();
 }
 
 void
 VerboseCursor::hide ()
 {
        _canvas_item->hide ();
-       _visible = false;
+       _canvas_item->parent()->lower_to_bottom ();
+       /* reset back to a sensible default for the next time we display the VC */
+       _canvas_item->set_offset (ArdourCanvas::Duple (10, 10));
 }
 
-double
-VerboseCursor::clamp_x (double x)
-{
-       _editor->clamp_verbose_cursor_x (x);
-       return x;
-}
-
-double
-VerboseCursor::clamp_y (double y)
+void
+VerboseCursor::set_offset (ArdourCanvas::Duple const & d)
 {
-       _editor->clamp_verbose_cursor_y (y);
-       return y;
+       _canvas_item->set_offset (d);
 }
 
 void
-VerboseCursor::set_time (framepos_t frame, double x, double y)
+VerboseCursor::set_time (framepos_t frame)
 {
        char buf[128];
        Timecode::Time timecode;
@@ -122,13 +106,9 @@ VerboseCursor::set_time (framepos_t frame, double x, double y)
                return;
        }
 
-       AudioClock::Mode m;
+       /* Take clock mode from the primary clock */
 
-       if (Profile->get_sae() || Profile->get_small_screen()) {
-               m = ARDOUR_UI::instance()->primary_clock->mode();
-       } else {
-               m = ARDOUR_UI::instance()->secondary_clock->mode();
-       }
+       AudioClock::Mode m = ARDOUR_UI::instance()->primary_clock->mode();
 
        switch (m) {
        case AudioClock::BBT:
@@ -157,11 +137,11 @@ VerboseCursor::set_time (framepos_t frame, double x, double y)
                break;
        }
 
-       set (buf, x, y);
+       _canvas_item->set (buf);
 }
 
 void
-VerboseCursor::set_duration (framepos_t start, framepos_t end, double x, double y)
+VerboseCursor::set_duration (framepos_t start, framepos_t end)
 {
        char buf[128];
        Timecode::Time timecode;
@@ -178,7 +158,7 @@ VerboseCursor::set_duration (framepos_t start, framepos_t end, double x, double
 
        AudioClock::Mode m;
 
-       if (Profile->get_sae() || Profile->get_small_screen()) {
+       if (Profile->get_sae() || Profile->get_small_screen() || Profile->get_trx()) {
                m = ARDOUR_UI::instance()->primary_clock->mode ();
        } else {
                m = ARDOUR_UI::instance()->secondary_clock->mode ();
@@ -243,28 +223,11 @@ VerboseCursor::set_duration (framepos_t start, framepos_t end, double x, double
                break;
        }
 
-       set (buf, x, y);
-}
-
-void
-VerboseCursor::set_color (uint32_t color)
-{
-       _canvas_item->set_color (color);
-}
-
-/** Set the position of the verbose cursor.  Any x/y offsets
- *  passed to the last call to show() will be applied to the
- *  coordinates passed in here.
- */
-void
-VerboseCursor::set_position (double x, double y)
-{
-       _canvas_item->set_x_position (clamp_x (x + _xoffset));
-       _canvas_item->set_y_position (clamp_y (y + _yoffset));
+       _canvas_item->set (buf);
 }
 
 bool
 VerboseCursor::visible () const
 {
-       return _visible;
+       return _canvas_item->visible();
 }
index 8db45608cdc3bac2e44f2f3ad4ab47b15ce290ad..7d12a2a13ea386eec26e43dbbd5733274b8a94ae 100644 (file)
 */
 
 #include "ardour/types.h"
-#include "canvas/text.h"
 #include "canvas/canvas.h"
 
 class Editor;
 
+namespace ArdourCanvas {
+     class TrackingText;
+}
+
 class VerboseCursor
 {
 public:
@@ -31,26 +34,17 @@ public:
        ArdourCanvas::Item* canvas_item () const;
        bool visible () const;
 
-       void set_color (uint32_t);
-
-       void set (std::string const &, double, double);
-       void set_text (std::string const &);
-       void set_position (double, double);
-       void set_time (framepos_t, double, double);
-       void set_duration (framepos_t, framepos_t, double, double);
+       void set (std::string const &);
+       void set_time (framepos_t);
+       void set_duration (framepos_t, framepos_t);
+       void set_offset (ArdourCanvas::Duple const&);
 
-       void show (double xoffset = 0, double yoffset = 0);
+       void show ();
        void hide ();
-
-         ArdourCanvas::Item& item() { return *_canvas_item; }
-
+       
 private:
-       double clamp_x (double);
-       double clamp_y (double);
-
-       Editor*             _editor;
-       ArdourCanvas::Text* _canvas_item;
-       bool                _visible;
-       double              _xoffset;
-       double              _yoffset;
+       Editor*                     _editor;
+       ArdourCanvas::TrackingText* _canvas_item;
+
+       void color_handler ();
 };
index 1ae69f02e1268482ee75e9f90d2a7ff41edb6e59..43f12aa9f35aa0754bd698554560181e5e24d12b 100644 (file)
@@ -23,8 +23,7 @@
 #include "ardour_ui.h"
 #include "video_image_frame.h"
 #include "public_editor.h"
-#include "utils.h"
-#include "canvas/group.h"
+#include "canvas/container.h"
 #include "utils_videotl.h"
 
 #include <gtkmm2ext/utils.h>
@@ -43,7 +42,7 @@ static void freedata_cb (uint8_t *d, void* /*arg*/) {
        free (d);
 }
 
-VideoImageFrame::VideoImageFrame (PublicEditor& ed, ArdourCanvas::Group& parent, int w, int h, std::string vsurl, std::string vfn)
+VideoImageFrame::VideoImageFrame (PublicEditor& ed, ArdourCanvas::Container& parent, int w, int h, std::string vsurl, std::string vfn)
        : editor (ed)
        , _parent(&parent)
        , clip_width(w)
index b611ff0d9e6886364d4025831f9e00a65bd766fb..8f9e004c67bcc1ea6cb3c2aa9fd407ae7feae96c 100644 (file)
@@ -32,7 +32,7 @@
 #include "ardour/ardour.h"
 #include "pbd/signals.h"
 
-#include "canvas/group.h"
+#include "canvas/container.h"
 #include "canvas/pixbuf.h"
 #include "canvas/image.h"
 
@@ -49,7 +49,7 @@ class PublicEditor;
 class VideoImageFrame : public sigc::trackable
 {
        public:
-       VideoImageFrame (PublicEditor&, ArdourCanvas::Group&, int, int, std::string, std::string);
+       VideoImageFrame (PublicEditor&, ArdourCanvas::Container&, int, int, std::string, std::string);
        virtual ~VideoImageFrame ();
 
        void set_position (framepos_t);
@@ -69,7 +69,7 @@ class VideoImageFrame : public sigc::trackable
        protected:
 
        PublicEditor& editor;
-       ArdourCanvas::Group *_parent;
+       ArdourCanvas::Container *_parent;
        ArdourCanvas::Image *image;
        boost::shared_ptr<ArdourCanvas::Image::Data> img;
 
index 279a3fc9952677d3d1297795460c7456ac6e2f23..fe91554158873d3e92608bd41da43cd076c77a7e 100644 (file)
 
 using namespace std;
 using namespace PBD;
+using namespace ARDOUR_UI_UTILS;
 
 VideoMonitor::VideoMonitor (PublicEditor *ed, std::string xjadeo_bin_path)
        : editor (ed)
 {
        manually_seeked_frame = 0;
        fps =0.0; // = _session->timecode_frames_per_second();
-       sync_by_manual_seek = false;
+       sync_by_manual_seek = true;
        _restore_settings_mask = 0;
        clock_connection = sigc::connection();
        state_connection = sigc::connection();
@@ -47,7 +48,7 @@ VideoMonitor::VideoMonitor (PublicEditor *ed, std::string xjadeo_bin_path)
        starting = 0;
        osdmode = 10; // 1: frameno, 2: timecode, 8: box
 
-       process = new ARDOUR::SystemExec(xjadeo_bin_path, X_("-R"));
+       process = new ARDOUR::SystemExec(xjadeo_bin_path, X_("-R -J"));
        process->ReadStdout.connect_same_thread (*this, boost::bind (&VideoMonitor::parse_output, this, _1 ,_2));
        process->Terminated.connect (*this, invalidator (*this), boost::bind (&VideoMonitor::terminated, this), gui_context());
        XJKeyEvent.connect (*this, invalidator (*this), boost::bind (&VideoMonitor::forward_keyevent, this, _1), gui_context());
index e4c9a7f51abf0d5e07a698c5477ce4f9e8fdac26..de6af96f0463af4b011f9741f999a15eeec20723 100644 (file)
 #include "utils_videotl.h"
 #include "i18n.h"
 
+#ifdef PLATFORM_WINDOWS
+#include <windows.h>
+#endif
+
 using namespace Gtk;
 using namespace std;
 using namespace PBD;
@@ -97,7 +101,7 @@ VideoServerDialog::VideoServerDialog (Session* s)
 #endif
 
        std::string icsd_file_path;
-       if (find_file_in_search_path (PBD::Searchpath(Glib::getenv("PATH")), X_("harvid"), icsd_file_path)) {
+       if (find_file (PBD::Searchpath(Glib::getenv("PATH")), X_("harvid"), icsd_file_path)) {
                path_entry.set_text(icsd_file_path);
        }
 #ifdef PLATFORM_WINDOWS
@@ -119,8 +123,13 @@ VideoServerDialog::VideoServerDialog (Session* s)
        else {
                PBD::warning <<
                        string_compose(
-                       _("The external video server 'harvid' can not be found. The tool is included with the %1 releases from ardour.org, "
-                         "alternatively you can download it from http://x42.github.com/harvid/ or acquire it from your distribution."), PROGRAM_NAME)
+                                       _("The external video server 'harvid' can not be found.\n"
+                                               "The tool is included with the %1 releases from ardour.org, "
+                                               "alternatively you can download it from http://x42.github.com/harvid/ "
+                                               "or acquire it from your distribution.\n"
+                                               "\n"
+                                               "see also http://manual.ardour.org/video-timeline/setup/"
+                                        ), PROGRAM_NAME)
                        << endmsg;
        }
 
index 0defa56be7a3448ec215de34af7c126cab81d3a5..815d19b7e782cce95b799c85cad77226d3f47814 100644 (file)
@@ -34,7 +34,6 @@
 #include "ardour_ui.h"
 #include "public_editor.h"
 #include "gui_thread.h"
-#include "utils.h"
 #include "utils_videotl.h"
 #include "rgb_macros.h"
 #include "video_timeline.h"
@@ -51,7 +50,7 @@ using namespace PBD;
 using namespace Timecode;
 using namespace VideoUtils;
 
-VideoTimeLine::VideoTimeLine (PublicEditor *ed, ArdourCanvas::Group *vbg, int initial_height)
+VideoTimeLine::VideoTimeLine (PublicEditor *ed, ArdourCanvas::Container *vbg, int initial_height)
        : editor (ed)
                , videotl_group(vbg)
                , bar_height(initial_height)
@@ -719,6 +718,11 @@ VideoTimeLine::set_video_server_docroot(std::string vsr) {
 }
 
 /* video-monitor for this timeline */
+void
+VideoTimeLine::xjadeo_readversion (std::string d, size_t /* s */) {
+       xjadeo_version += d;
+}
+
 void
 VideoTimeLine::find_xjadeo () {
        std::string xjadeo_file_path;
@@ -730,7 +734,7 @@ VideoTimeLine::find_xjadeo () {
 #endif
        if (getenv("XJREMOTE")) {
                _xjadeo_bin = getenv("XJREMOTE");
-       } else if (find_file_in_search_path (Searchpath(Glib::getenv("PATH")), X_("xjremote"), xjadeo_file_path)) {
+       } else if (find_file (Searchpath(Glib::getenv("PATH")), X_("xjremote"), xjadeo_file_path)) {
                _xjadeo_bin = xjadeo_file_path;
        }
 #ifdef PLATFORM_WINDOWS
@@ -765,9 +769,50 @@ VideoTimeLine::find_xjadeo () {
                _xjadeo_bin = X_("");
                warning << _("Video-monitor 'xjadeo' was not found. Please install http://xjadeo.sf.net/ "
                                "(a custom path to xjadeo can be specified by setting the XJREMOTE environment variable. "
-                               "It should point to an application compatible with xjadeo's remote-control interface 'xjremote').")
+                               "It should point to an application compatible with xjadeo's remote-control interface 'xjremote').\n"
+                               "\n"
+                               "see also http://manual.ardour.org/video-timeline/setup/")
                        << endmsg;
        }
+       if (found_xjadeo ()) {
+               ARDOUR::SystemExec version_check(_xjadeo_bin, X_("--version"));
+               xjadeo_version = "";
+               version_check.ReadStdout.connect_same_thread (*this, boost::bind (&VideoTimeLine::xjadeo_readversion, this, _1 ,_2));
+               version_check.Terminated.connect_same_thread (*this, boost::bind (&VideoTimeLine::xjadeo_readversion, this, "\n" ,1));
+               if (version_check.start(2)) {
+                       warning << _(
+                                       "Video-monitor 'xjadeo' cannot be launched."
+                                       ) << endmsg;
+                       _xjadeo_bin = X_("");
+                       return;
+               }
+
+               version_check.wait ();
+               int timeout = 300;
+               while (xjadeo_version.empty() && --timeout) {
+                       Glib::usleep(10000);
+               }
+
+               bool v_ok = false;
+               size_t vo = xjadeo_version.find(" version ");
+               if (vo != string::npos) {
+                       int v_major, v_minor, v_micro;
+                       if(sscanf(xjadeo_version.substr(vo + 9, string::npos).c_str(),"%d.%d.%d",
+                                               &v_major, &v_minor, &v_micro) == 3)
+                       {
+                               if (v_major >= 1) v_ok = true;
+                               else if (v_major == 0 && v_minor >= 8) v_ok = true;
+                               else if (v_major == 0 && v_minor >= 7 && v_micro >= 7) v_ok = true;
+                       }
+               }
+               if (!v_ok) {
+                       _xjadeo_bin = X_("");
+                       warning << _(
+                                       "Video-monitor 'xjadeo' is too old. "
+                                       "Please install xjadeo version 0.7.7 or later. http://xjadeo.sf.net/"
+                                       ) << endmsg;
+               }
+       }
 }
 
 void
index dd61d2ef73c6646c4cfa7471c87f856d683ec532..5a1bceb25897b0c2b4595dd1ea2ef9a60e06e54d 100644 (file)
@@ -29,7 +29,7 @@
 #include "video_image_frame.h"
 #include "video_monitor.h"
 #include "pbd/signals.h"
-#include "canvas/group.h"
+#include "canvas/container.h"
 
 namespace ARDOUR {
        class Session;
@@ -55,7 +55,7 @@ class PublicEditor;
 class VideoTimeLine : public sigc::trackable, public ARDOUR::SessionHandlePtr, public PBD::ScopedConnectionList, public PBD::StatefulDestructible
 {
        public:
-       VideoTimeLine (PublicEditor*, ArdourCanvas::Group*, int);
+       VideoTimeLine (PublicEditor*, ArdourCanvas::Container*, int);
        virtual ~VideoTimeLine ();
 
        void set_session (ARDOUR::Session *s);
@@ -102,7 +102,7 @@ class VideoTimeLine : public sigc::trackable, public ARDOUR::SessionHandlePtr, p
        protected:
 
        PublicEditor *editor;
-       ArdourCanvas::Group *videotl_group;
+       ArdourCanvas::Container *videotl_group;
        int bar_height;
 
        std::string _xjadeo_bin;
@@ -123,6 +123,9 @@ class VideoTimeLine : public sigc::trackable, public ARDOUR::SessionHandlePtr, p
        std::string video_server_url;
        std::string server_docroot;
 
+       void xjadeo_readversion (std::string d, size_t s);
+       std::string xjadeo_version;
+
        typedef std::list<VideoImageFrame*> VideoFrames;
        VideoFrames video_frames;
        VideoImageFrame *get_video_frame (framepos_t vfn, int cut=0, int rightend = -1);
index 6db3bc7d7a2976fe326b195f594d87117ad1850a..007b623355ab497084cc7c138c25c86ef8d8bae7 100644 (file)
@@ -116,7 +116,7 @@ VolumeController::to_control_value (double display_value)
        if (_linear) {
                v = _controllable->lower() + ((_controllable->upper() - _controllable->lower()) * display_value);
        } else {
-               v = slider_position_to_gain_with_max (display_value, ARDOUR::Config->get_max_gain());
+               v = ARDOUR::slider_position_to_gain_with_max (display_value, ARDOUR::Config->get_max_gain());
        }
 
        return v;
@@ -130,7 +130,7 @@ VolumeController::to_display_value (double control_value)
        if (_linear) {
                v = (control_value - _controllable->lower ()) / (_controllable->upper() - _controllable->lower());
        } else {
-               v = gain_to_slider_position_with_max (control_value, _controllable->upper());
+               v = ARDOUR::gain_to_slider_position_with_max (control_value, _controllable->upper());
        }
 
        return v;
@@ -151,13 +151,13 @@ VolumeController::adjust (double control_delta)
                 */
 #if 0
                /* convert to linear/fractional slider position domain */
-               v = gain_to_slider_position_with_max (_controllable->get_value (), _controllable->upper());
+               v = ARDOUR::gain_to_slider_position_with_max (_controllable->get_value (), _controllable->upper());
                /* increment in this domain */
                v += control_delta;
                /* clamp to appropriate range for linear/fractional slider domain */
                v = std::max (0.0, std::min (1.0, v));
                /* convert back to gain coefficient domain */
-               v = slider_position_to_gain_with_max (v, _controllable->upper());
+               v = ARDOUR::slider_position_to_gain_with_max (v, _controllable->upper());
                /* clamp in controller domain */
                v = std::max (_controllable->lower(), std::min (_controllable->upper(), v));
                /* convert to dB domain */
index 920f71bd7db343efb917b1aa7fe376e92a34e5d8..41e67e023e895577187dc53fc1b9da986c81751d 100644 (file)
@@ -101,8 +101,6 @@ gtk2_ardour_sources = [
         'ghostregion.cc',
         'global_port_matrix.cc',
         'group_tabs.cc',
-        'gtk-custom-hruler.c',
-        'gtk-custom-ruler.c',
         'gtk_pianokeyboard.c',
         'gui_object.cc',
         'insert_time_dialog.cc',
@@ -195,6 +193,7 @@ gtk2_ardour_sources = [
         'route_processor_selection.cc',
         'route_time_axis.cc',
         'route_ui.cc',
+        'ruler_dialog.cc',
         'search_path_option.cc',
         'selection.cc',
         'send_ui.cc',
@@ -204,6 +203,7 @@ gtk2_ardour_sources = [
         'session_option_editor.cc',
         'sfdb_ui.cc',
         'shuttle_control.cc',
+        'soundcloud_export_selector.cc',
         'splash.cc',
         'speaker_dialog.cc',
         'startup.cc',
@@ -261,10 +261,6 @@ def configure(conf):
         '', '')
     autowaf.configure(conf)
 
-    if Options.options.dist_target == 'auto':
-        if re.search ("linux", sys.platform) != None:
-            autowaf.check_pkg(conf, 'alsa', uselib_store='ALSA')
-
     # TODO: Insert a sanity check for on OS X to ensure CoreAudio is present
 
     autowaf.check_pkg(conf, 'fftw3f', uselib_store='FFTW3F',
@@ -486,7 +482,7 @@ def build(bld):
             'INSTALL_PREFIX' : bld.env['PREFIX'],
             'LIBDIR'         : os.path.normpath(bld.env['DLLDIR']),
             'DATADIR'        : os.path.normpath(bld.env['DATADIR']),
-            'SYSCONFDIR'     : os.path.normpath(bld.env['CONFDIR']),
+            'CONFDIR'        : os.path.normpath(bld.env['CONFDIR']),
             'LIBS'           : 'build/libs',
             'VERSION'        : bld.env['VERSION'],
             'EXECUTABLE'     : 'build/gtk2_ardour/ardour-' + bld.env['VERSION']
@@ -524,6 +520,7 @@ def build(bld):
 
     dark_rc_subst_dict = {}
     light_rc_subst_dict = {}
+    ui_conf_dict = {}
     font_sizes      = {}
     base_font       = ""
 
@@ -584,18 +581,21 @@ def build(bld):
 
             dark_rc_subst_dict[key] = fontstyle
             light_rc_subst_dict[key] = fontstyle
-
+            ui_conf_dict[key] = points
+            
     # @FONT_SIZE_XXXX@
     for sizename,points in iter(font_sizes.items()):
             key = "_".join (['FONT_SIZE',sizename])
             dark_rc_subst_dict[key] = points
             light_rc_subst_dict[key] = points
+            ui_conf_dict[key] = points
 
     # various font names, eg @BOLD_MONOSPACE@
     for font_sym,text in iter(font_names.items()):
         key = font_sym
         dark_rc_subst_dict[key] = text
         light_rc_subst_dict[key] = text
+        ui_conf_dict[key] = text
 
     # RC files
     dark_rc_subst_dict['COLOR_SCHEME'] = build_color_scheme(
@@ -660,6 +660,12 @@ def build(bld):
     obj.target = 'ardour3_ui_light.rc'
     obj.install_path = bld.env['CONFDIR']
 
+    obj              = bld(features = 'subst')
+    obj.source       = [ 'ardour3_ui_default.conf.in' ]
+    obj.target       = 'ardour3_ui_default.conf'
+    obj.install_path = None
+    set_subst_dict(obj, ui_conf_dict)
+
     # Menus
     menus_argv = []
     if bld.is_defined('GTKOSX'):
index dc2644c4288567eb581a44bb022b04e414681f15..a9037214cc33f3fa6874e0c8f7f75ff024cbe7f4 100644 (file)
@@ -17,6 +17,7 @@ using namespace ARDOUR;
 using namespace PBD;
 
 #ifdef PLATFORM_WINDOWS
+#include <windows.h>
 #define sleep(X) Sleep((X) * 1000)
 #endif
 
index be80f9c73b7ea80de710dab43c7dea2bd23663ca..3548324fb1f5800317105eac0c492b808cd1d75e 100644 (file)
@@ -81,7 +81,7 @@ def build(bld):
             'INSTALL_PREFIX' : bld.env['PREFIX'],
             'LIBDIR'         : os.path.normpath(bld.env['LIBDIR']),
             'DATADIR'        : os.path.normpath(bld.env['DATADIR']),
-            'SYSCONFDIR'     : os.path.normpath(bld.env['SYSCONFDIR']),
+            'CONFDIR'        : os.path.normpath(bld.env['CONFDIR']),
             'LIBS'           : 'build/libs',
             'VERSION'        : bld.env['VERSION'],
             'EXECUTABLE'     : 'build/headless/hardour-' + bld.env['VERSION']
index 6a96c31b4c2ea1dd32f6aae8029fa07171eaed5f..9bc6600a7a9dddc4b4231381ca69817ee7fe3efb 100644 (file)
                                RelativePath="..\default_click.cc"
                                >
                        </File>
+                       <File
+                               RelativePath="..\delayline.cc"
+                               >
+                       </File>
                        <File
                                RelativePath="..\delivery.cc"
                                >
                                RelativePath="..\midi_ui.cc"
                                >
                        </File>
+                       <File
+                               RelativePath="..\mididm.cc"
+                               >
+                       </File>
                        <File
                                RelativePath="..\midiport_manager.cc"
                                >
                                RelativePath="..\sndfilesource.cc"
                                >
                        </File>
+                       <File
+                               RelativePath="..\soundcloud_upload.cc"
+                               >
+                       </File>
                        <File
                                RelativePath="..\source.cc"
                                >
                                RelativePath="..\ardour\debug.h"
                                >
                        </File>
+                       <File
+                               RelativePath="..\ardour\delayline.h"
+                               >
+                       </File>
                        <File
                                RelativePath="..\ardour\delivery.h"
                                >
                                RelativePath="..\ardour\midi_ui.h"
                                >
                        </File>
+                       <File
+                               RelativePath="..\ardour\mididm.h"
+                               >
+                       </File>
                        <File
                                RelativePath="..\ardour\midiport_manager.h"
                                >
                                RelativePath="..\ardour\route_group_specialized.h"
                                >
                        </File>
+                       <File
+                               RelativePath="..\ardour\route_sorters.h"
+                               >
+                       </File>
                        <File
                                RelativePath="..\ardour\runtime_functions.h"
                                >
                                RelativePath="..\ardour\sndfilesource.h"
                                >
                        </File>
+                       <File
+                               RelativePath="..\ardour\soundcloud_upload.h"
+                               >
+                       </File>
                        <File
                                RelativePath="..\ardour\soundseq.h"
                                >
index 132be2e1c6db4c9cf8efa2066730b77dd885a34d..29032525f244c57a7d7c94bfb65bfd6c866d62d0 100644 (file)
@@ -435,7 +435,10 @@ Amp::setup_gain_automation (framepos_t start_frame, framepos_t end_frame, framec
 {
        Glib::Threads::Mutex::Lock am (control_lock(), Glib::Threads::TRY_LOCK);
 
-       if (am.locked() && _session.transport_rolling() && _gain_control->automation_playback()) {
+       if (am.locked()
+           && (_session.transport_rolling() || _session.bounce_processing())
+           && _gain_control->automation_playback())
+       {
                assert (_gain_automation_buffer);
                _apply_gain_automation = _gain_control->list()->curve().rt_safe_get_vector (
                        start_frame, end_frame, _gain_automation_buffer, nframes);
index 78e390318259597c9a708378acd906aaeb8bffda..bf860e9aebbfb1bd79f405aefe1dfdb647bfc820 100644 (file)
@@ -263,6 +263,14 @@ class LIBARDOUR_API AudioBackend : public PortEngine {
      * external D-A/D-A converters. Units are samples.
      */
     virtual int set_systemic_output_latency (uint32_t) = 0;
+    /** Set the (additional) input latency for a specific midi device,
+     * or if the identifier is empty, apply to all midi devices.
+     */
+    virtual int set_systemic_midi_input_latency (std::string const, uint32_t) = 0;
+    /** Set the (additional) output latency for a specific midi device,
+     * or if the identifier is empty, apply to all midi devices.
+     */
+    virtual int set_systemic_midi_output_latency (std::string const, uint32_t) = 0;
 
     /* Retrieving parameters */
 
@@ -274,6 +282,8 @@ class LIBARDOUR_API AudioBackend : public PortEngine {
     virtual uint32_t     output_channels () const = 0;
     virtual uint32_t     systemic_input_latency () const = 0;
     virtual uint32_t     systemic_output_latency () const = 0;
+    virtual uint32_t     systemic_midi_input_latency (std::string const) const = 0;
+    virtual uint32_t     systemic_midi_output_latency (std::string const) const = 0;
 
     /** override this if this implementation returns true from
      * requires_driver_selection()
@@ -311,7 +321,19 @@ class LIBARDOUR_API AudioBackend : public PortEngine {
     virtual int set_midi_option (const std::string& option) = 0;
 
     virtual std::string midi_option () const = 0;
-    
+
+    /** Detailed MIDI device list - if available */
+    virtual std::vector<DeviceStatus> enumerate_midi_devices () const = 0;
+
+    /** mark a midi-devices as enabled */
+    virtual int set_midi_device_enabled (std::string const, bool) = 0;
+
+    /** query if a midi-device is enabled */
+    virtual bool midi_device_enabled (std::string const) const = 0;
+
+    /** if backend supports systemic_midi_[in|ou]tput_latency() */
+    virtual bool can_set_systemic_midi_latencies () const = 0;
+
     /* State Control */
  
     /** Start using the device named in the most recent call
index 2e838ff628c982965008c13cf5d1e9676959e8ac..2614b8061c18ccde552e217cb6557dc129ca6a0e 100644 (file)
@@ -108,6 +108,7 @@ class LIBARDOUR_API AudioDiskstream : public Diskstream
        int remove_channel (uint32_t how_many);
 
        bool set_name (std::string const &);
+       bool set_write_source_name (const std::string& str);
 
        /* stateful */
 
index 1ecec88f8986f3d8431f567978bbfb96ddff45a9..0bc8be81f45b066ac8f8d2b57a4cb954a247f353 100644 (file)
@@ -58,7 +58,7 @@ class LIBARDOUR_API AudioTrack : public Track
        boost::shared_ptr<Region> bounce_range (framepos_t start, framepos_t end, InterThreadInfo&, 
                                                boost::shared_ptr<Processor> endpoint, bool include_endpoint);
        int export_stuff (BufferSet& bufs, framepos_t start_frame, framecnt_t nframes,
-                         boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export);
+                         boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export, bool for_freeze);
 
        int set_state (const XMLNode&, int version);
 
index dd57f81e90154798ab1d3ca4be665809cdc02d13..7eeae8f205c03c5e66bee2c5125f9d55c91b5f96 100644 (file)
@@ -54,6 +54,7 @@ namespace ARDOUR {
 
 class InternalPort;
 class MidiPort;
+class MIDIDM;
 class Port;
 class Session;
 class ProcessThread;
@@ -191,14 +192,24 @@ public:
 
     /* latency measurement */
 
-    MTDM* mtdm();
+    MTDM* mtdm() { return _mtdm; }
+    MIDIDM* mididm() { return _mididm; }
+
     int  prepare_for_latency_measurement ();
-    int  start_latency_detection ();
+    int  start_latency_detection (bool);
     void stop_latency_detection ();
     void set_latency_input_port (const std::string&);
     void set_latency_output_port (const std::string&);
     uint32_t latency_signal_delay () const { return _latency_signal_latency; }
 
+               enum LatencyMeasurement {
+                       MeasureNone,
+                       MeasureAudio,
+                       MeasureMIDI
+               };
+
+               LatencyMeasurement measuring_latency () const { return _measuring_latency; }
+
   private:
     AudioEngine ();
 
@@ -221,7 +232,8 @@ public:
     Glib::Threads::Thread*     m_meter_thread;
     ProcessThread*            _main_thread;
     MTDM*                     _mtdm;
-    bool                      _measuring_latency;
+    MIDIDM*                   _mididm;
+               LatencyMeasurement        _measuring_latency;
     PortEngine::PortHandle    _latency_input_port;
     PortEngine::PortHandle    _latency_output_port;
     framecnt_t                _latency_flush_frames;
index e0a199fd72a46b920ff77a5fba68ea62196f5896..4831eb208166e812499b3ac8a12d4b93952fd1f0 100644 (file)
@@ -94,6 +94,12 @@ protected:
        /** Constructor to be called for existing in-session files */
        AudioFileSource (Session&, const XMLNode&, bool must_exist = true);
 
+       /** Constructor to be called for crash recovery. Final argument is not
+        * used but exists to differentiate from the external-to-session
+        * constructor above.
+        */
+       AudioFileSource (Session&, const std::string& path, Source::Flag flags, bool);
+
        int init (const std::string& idstr, bool must_exist);
 
        virtual void set_header_timeline_position () = 0;
index 6aeda90eb814acba6f02c3cbd29c3295bb79ad74..452f7eb2462ba6e856d152f85b3739863f3f5157 100644 (file)
@@ -97,7 +97,7 @@ class LIBARDOUR_API Auditioner : public Track
        boost::shared_ptr<Region> bounce_range (framepos_t, framepos_t, InterThreadInfo&, boost::shared_ptr<Processor>, bool)
                { return boost::shared_ptr<Region> (); }
 
-       int export_stuff (BufferSet&, framepos_t, framecnt_t, boost::shared_ptr<Processor>, bool, bool)
+       int export_stuff (BufferSet&, framepos_t, framecnt_t, boost::shared_ptr<Processor>, bool, bool, bool)
                { return -1; }
 
        boost::shared_ptr<Diskstream> diskstream_factory (XMLNode const &)
index f5a3935ead6dffe18392f35b54dd3f5a1af618c0..c73b59b763949911d638dbc783487f3b2303da38 100644 (file)
@@ -40,7 +40,7 @@ public:
        static ThreadBuffers* get_thread_buffers ();
        static void           put_thread_buffers (ThreadBuffers*);
 
-       static void ensure_buffers (ChanCount howmany = ChanCount::ZERO);
+       static void ensure_buffers (ChanCount howmany = ChanCount::ZERO, size_t custom = 0);
 
 private:
         static Glib::Threads::Mutex rb_mutex;
index d6bef8d48a9aecd15ad7d539c0e7d1a21da95ccc..9a7b0a495fd0e74c07b59b51b4b11479248476b8 100644 (file)
@@ -35,6 +35,7 @@ namespace PBD {
                LIBARDOUR_API extern uint64_t SnapBBT;
                LIBARDOUR_API extern uint64_t Configuration;
                LIBARDOUR_API extern uint64_t Latency;
+               LIBARDOUR_API extern uint64_t LatencyCompensation;
                LIBARDOUR_API extern uint64_t Peaks;
                LIBARDOUR_API extern uint64_t Processors;
                LIBARDOUR_API extern uint64_t ProcessThreads;
@@ -67,6 +68,7 @@ namespace PBD {
                LIBARDOUR_API extern uint64_t WiimoteControl;
                LIBARDOUR_API extern uint64_t Ports;
                LIBARDOUR_API extern uint64_t AudioEngine;
+               LIBARDOUR_API extern uint64_t Soundcloud;
        }
 }
 
diff --git a/libs/ardour/ardour/delayline.h b/libs/ardour/ardour/delayline.h
new file mode 100644 (file)
index 0000000..56a6de0
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+    Copyright (C) 2006, 2013 Paul Davis
+    Copyright (C) 2013, 2014 Robin Gareus <robin@gareus.org>
+
+    This program is free software; you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by the Free
+    Software Foundation; either version 2 of the License, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __ardour_delayline_h__
+#define __ardour_delayline_h__
+
+#include "ardour/types.h"
+#include "ardour/processor.h"
+
+namespace ARDOUR {
+
+class BufferSet;
+class ChanCount;
+class Session;
+
+/** Meters peaks on the input and stores them for access.
+ */
+class LIBARDOUR_API DelayLine : public Processor {
+public:
+
+  DelayLine (Session& s, const std::string& name);
+       ~DelayLine ();
+
+       bool display_to_user() const { return false; }
+
+       void run (BufferSet&, framepos_t, framepos_t, pframes_t, bool);
+       void set_delay(framecnt_t signal_delay);
+       framecnt_t get_delay() { return _pending_delay; }
+
+       bool configure_io (ChanCount in, ChanCount out);
+       bool can_support_io_configuration (const ChanCount& in, ChanCount& out);
+
+       void flush();
+       void realtime_handle_transport_stopped () { flush(); }
+       void realtime_locate () { flush(); }
+       void monitoring_changed() { flush(); }
+
+       XMLNode& state (bool full);
+
+private:
+       friend class IO;
+       framecnt_t _delay, _pending_delay;
+       framecnt_t _bsiz,  _pending_bsiz;
+       frameoffset_t _roff, _woff;
+       boost::shared_ptr<Sample> _buf;
+       boost::shared_ptr<Sample> _pending_buf;
+       boost::shared_ptr<MidiBuffer> _midi_buf;
+       bool _pending_flush;
+};
+
+} // namespace ARDOUR
+
+#endif // __ardour_meter_h__
index dd74d5cb521c837e69abadbb79ebeca3f48e41b1..055a298d84b71eeecd85e43cf351653b379d82a5 100644 (file)
@@ -70,6 +70,15 @@ class LIBARDOUR_API Diskstream : public SessionObject, public PublicDiskstream
        virtual ~Diskstream();
 
        virtual bool set_name (const std::string& str);
+       virtual bool set_write_source_name (const std::string& str);
+
+       std::string write_source_name () const {
+               if (_write_source_name.empty()) {
+                       return name();
+               } else {
+                       return _write_source_name;
+               }
+       }
 
        virtual std::string steal_write_source_name () { return std::string(); }
 
@@ -312,6 +321,8 @@ class LIBARDOUR_API Diskstream : public SessionObject, public PublicDiskstream
 
        bool          in_set_state;
 
+       std::string   _write_source_name;
+
        Glib::Threads::Mutex state_lock;
 
        PBD::ScopedConnectionList playlist_connections;
index 9a95111509b888b6f148bf3460dc58598ef7d41c..dad7d84b72f58b4406318fee6c98f7a8a475e48c 100644 (file)
@@ -100,6 +100,8 @@ class LIBARDOUR_API ExportFormatManager : public PBD::ScopedConnectionList
 
        void select_with_cue (bool);
        void select_with_toc (bool);
+       void select_upload (bool);
+       void set_command (std::string);
        void select_src_quality (ExportFormatBase::SRCQuality value);
        void select_trim_beginning (bool value);
        void select_silence_beginning (AnyTime const & time);
index 1593990d3592c43e9732b3b3fec5c21941a41397..2a62d792f00f44db0039b4468e45867c08e17a56 100644 (file)
@@ -96,6 +96,8 @@ class LIBARDOUR_API ExportFormatSpecification : public ExportFormatBase {
        void set_tag (bool tag_it) { _tag = tag_it; }
        void set_with_cue (bool yn) { _with_cue = yn; }
        void set_with_toc (bool yn) { _with_toc = yn; }
+       void set_soundcloud_upload (bool yn) { _soundcloud_upload = yn; }
+       void set_command (std::string command) { _command = command; }
 
        void set_silence_beginning (AnyTime const & value) { _silence_beginning = value; }
        void set_silence_end (AnyTime const & value) { _silence_end = value; }
@@ -125,6 +127,8 @@ class LIBARDOUR_API ExportFormatSpecification : public ExportFormatBase {
        float normalize_target () const { return _normalize_target; }
        bool with_toc() const { return _with_toc; }
        bool with_cue() const { return _with_cue; }
+       bool soundcloud_upload() const { return _soundcloud_upload; }
+       std::string command() const { return _command; }
 
        bool tag () const { return _tag && supports_tagging; }
 
@@ -174,6 +178,8 @@ class LIBARDOUR_API ExportFormatSpecification : public ExportFormatBase {
        float           _normalize_target;
        bool            _with_toc;
        bool            _with_cue;
+       bool            _soundcloud_upload;
+       std::string     _command;
 
        /* serialization helpers */
 
index 1bc80a80e976693c7e194ca7c2fcfaf23634680e..8336cea732aa896718445c15c4feedb7ecee4043 100644 (file)
@@ -31,6 +31,7 @@
 #include "ardour/session.h"
 #include "ardour/libardour_visibility.h"
 #include "ardour/types.h"
+#include "pbd/signals.h"
 
 namespace AudioGrapher {
        class BroadcastInfo;
@@ -68,7 +69,7 @@ class LIBARDOUR_API ExportElementFactory
        Session & session;
 };
 
-class LIBARDOUR_API ExportHandler : public ExportElementFactory
+class LIBARDOUR_API ExportHandler : public ExportElementFactory, public sigc::trackable
 {
   public:
        struct FileSpec {
@@ -95,6 +96,8 @@ class LIBARDOUR_API ExportHandler : public ExportElementFactory
        friend boost::shared_ptr<ExportHandler> Session::get_export_handler();
        ExportHandler (Session & session);
 
+       void command_output(std::string output, size_t size);
+
   public:
        ~ExportHandler ();
 
@@ -105,6 +108,18 @@ class LIBARDOUR_API ExportHandler : public ExportElementFactory
 
        std::string get_cd_marker_filename(std::string filename, CDMarkerFormat format);
 
+       /** signal emitted when soundcloud export reports progress updates during upload.
+        * The parameters are total and current bytes downloaded, and the current filename
+        */
+       PBD::Signal3<void, double, double, std::string> SoundcloudProgress;
+
+       /* upload credentials & preferences */
+       std::string soundcloud_username;
+       std::string soundcloud_password;
+       bool soundcloud_make_public;
+       bool soundcloud_open_page;
+       bool soundcloud_downloadable;
+
   private:
 
        void handle_duplicate_format_extensions();
index 37a7e67d2ef4c35a460ba151941c84f15ce41c14..8b8adfeb669813f7bb4a4d82f6d47f021e1e0101 100644 (file)
@@ -85,6 +85,10 @@ public:
        void existence_check ();
        virtual void prevent_deletion ();
 
+       /** Rename the file on disk referenced by this source to \param newname
+        */
+       int rename (const std::string& name);
+
 protected:
        FileSource (Session& session, DataType type,
                    const std::string& path,
index a06bf399065a8c646712813d78a6c702662795b6..6feeb35c6d006b2014493ad82c91908d84803231 100644 (file)
@@ -28,7 +28,7 @@ namespace ARDOUR {
 class LIBARDOUR_API InternalSend : public Send
 {
   public:
-       InternalSend (Session&, boost::shared_ptr<Pannable>, boost::shared_ptr<MuteMaster>, boost::shared_ptr<Route> send_to, Delivery::Role role = Delivery::Aux, bool ignore_bitslot = false);
+       InternalSend (Session&, boost::shared_ptr<Pannable>, boost::shared_ptr<MuteMaster>, boost::shared_ptr<Route> send_from, boost::shared_ptr<Route> send_to, Delivery::Role role = Delivery::Aux, bool ignore_bitslot = false);
        virtual ~InternalSend ();
 
        std::string display_name() const;
@@ -46,6 +46,7 @@ class LIBARDOUR_API InternalSend : public Send
        bool configure_io (ChanCount in, ChanCount out);
        int  set_block_size (pframes_t);
 
+       boost::shared_ptr<Route> source_route() const { return _send_from; }
        boost::shared_ptr<Route> target_route() const { return _send_to; }
        const PBD::ID& target_id() const { return _send_to_id; }
 
@@ -60,6 +61,7 @@ class LIBARDOUR_API InternalSend : public Send
 
   private:
        BufferSet mixbufs;
+       boost::shared_ptr<Route> _send_from;
        boost::shared_ptr<Route> _send_to;
        PBD::ID _send_to_id;
        PBD::ScopedConnection connect_c;
index 10105cfbb3c229f3339ac92799a8ece82a263b96..2e2cbf65042cc958bba8eae9ee5659be78e0ef79 100644 (file)
@@ -45,23 +45,27 @@ public:
 
        bool     push_back(const Evoral::MIDIEvent<TimeType>& event);
        bool     push_back(TimeType time, size_t size, const uint8_t* data);
+
        uint8_t* reserve(TimeType time, size_t size);
 
        void resize(size_t);
        size_t size() const { return _size; }
        bool empty() const { return _size == 0; }
 
+       bool insert_event(const Evoral::MIDIEvent<TimeType>& event);
        bool merge_in_place(const MidiBuffer &other);
 
        template<typename BufferType, typename EventType>
-       class iterator_base {
+               class iterator_base
+       {
        public:
-               iterator_base<BufferType, EventType>(BufferType& b, framecnt_t o) 
-                    : buffer(&b), offset(o) {}
-                iterator_base<BufferType, EventType>(const iterator_base<BufferType,EventType>& o) 
-                    : buffer (o.buffer), offset(o.offset) {}
-           
-               inline iterator_base<BufferType,EventType> operator= (const iterator_base<BufferType,EventType>& o) {
+               iterator_base<BufferType, EventType>(BufferType& b, framecnt_t o)
+                       : buffer(&b), offset(o) {}
+
+               iterator_base<BufferType, EventType>(const iterator_base<BufferType,EventType>& o)
+                       : buffer (o.buffer), offset(o.offset) {}
+
+               inline iterator_base<BufferType,EventType> operator= (const iterator_base<BufferType,EventType>& o) {
                        if (&o != this) {
                                buffer = o.buffer;
                                offset = o.offset;
@@ -77,6 +81,7 @@ public:
                                        *((TimeType*)(buffer->_data + offset)),
                                        event_size, ev_start);
                }
+
                inline EventType operator*() {
                        uint8_t* ev_start = buffer->_data + offset + sizeof(TimeType);
                        int event_size = Evoral::midi_event_size(ev_start);
@@ -86,6 +91,10 @@ public:
                                        event_size, ev_start);
                }
 
+               inline TimeType * timeptr() {
+                       return ((TimeType*)(buffer->_data + offset));
+               }
+
                inline iterator_base<BufferType, EventType>& operator++() {
                        uint8_t* ev_start = buffer->_data + offset + sizeof(TimeType);
                        int event_size = Evoral::midi_event_size(ev_start);
@@ -93,12 +102,15 @@ public:
                        offset += sizeof(TimeType) + event_size;
                        return *this;
                }
+
                inline bool operator!=(const iterator_base<BufferType, EventType>& other) const {
                        return (buffer != other.buffer) || (offset != other.offset);
                }
+
                inline bool operator==(const iterator_base<BufferType, EventType>& other) const {
                        return (buffer == other.buffer) && (offset == other.offset);
                }
+
                BufferType*     buffer;
                size_t          offset;
        };
@@ -112,7 +124,7 @@ public:
        const_iterator begin() const { return const_iterator(*this, 0); }
        const_iterator end()   const { return const_iterator(*this, _size); }
 
-        iterator erase(const iterator& i) {
+       iterator erase(const iterator& i) {
                assert (i.buffer == this);
                uint8_t* ev_start = _data + i.offset + sizeof (TimeType);
                int event_size = Evoral::midi_event_size (ev_start);
@@ -124,7 +136,7 @@ public:
 
                size_t total_data_deleted = sizeof(TimeType) + event_size;
 
-               if (i.offset + total_data_deleted >= _size) {
+               if (i.offset + total_data_deleted > _size) {
                        _size = 0;
                        return end();
                }
@@ -155,7 +167,7 @@ public:
         * its MIDI status byte.
         */
        static bool second_simultaneous_midi_byte_is_first (uint8_t, uint8_t);
-       
+
 private:
        friend class iterator_base< MidiBuffer, Evoral::MIDIEvent<TimeType> >;
        friend class iterator_base< const MidiBuffer, const Evoral::MIDIEvent<TimeType> >;
@@ -164,7 +176,6 @@ private:
        pframes_t _size;
 };
 
-
 } // namespace ARDOUR
 
 #endif // __ardour_midi_buffer_h__
index 5a100420960bc7eeb3d2526b7bb2468ec8a5be54..91fb891f17705c651101fbba62294c2154328138 100644 (file)
@@ -76,6 +76,7 @@ class LIBARDOUR_API MidiDiskstream : public Diskstream
        int use_copy_playlist ();
 
        bool set_name (std::string const &);
+       bool set_write_source_name (const std::string& str);
 
        /* stateful */
        XMLNode& get_state(void);
@@ -173,6 +174,7 @@ class LIBARDOUR_API MidiDiskstream : public Diskstream
        gint                         _frames_read_from_ringbuffer;
        volatile gint                _frames_pending_write;
        volatile gint                _num_captured_loops;
+       framepos_t                   _accumulated_capture_offset;
 
        /** A buffer that we use to put newly-arrived MIDI data in for
            the GUI to read (so that it can update itself).
index cf6d3f7b6476c8fbc1eb4007e4adaf50e6a766a5..1acec0346eb4ecf7cc75322da26bcf9df9be41fd 100644 (file)
@@ -72,7 +72,8 @@ public:
                          framecnt_t                   end_frame,
                          boost::shared_ptr<Processor> endpoint,
                          bool                         include_endpoint,
-                         bool                         for_export);
+                         bool                         for_export,
+                         bool                         for_freeze);
 
        int set_state (const XMLNode&, int version);
 
diff --git a/libs/ardour/ardour/mididm.h b/libs/ardour/ardour/mididm.h
new file mode 100644 (file)
index 0000000..65ed15a
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2013-2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __libardour_mididm_h__
+#define __libardour_mididm_h__
+
+#include "ardour/types.h"
+#include "ardour/libardour_visibility.h"
+
+namespace ARDOUR {
+
+class PortEngine;
+
+class LIBARDOUR_API MIDIDM
+{
+public:
+
+       MIDIDM (framecnt_t sample_rate);
+
+       int process (pframes_t nframes, PortEngine &pe, void *midi_in, void *midi_out);
+
+       framecnt_t latency (void) { return _cnt_total > 10 ? _avg_delay : 0; }
+       framecnt_t processed (void) { return _cnt_total; }
+       double     deviation (void) { return _cnt_total > 1 ? sqrt(_var_s / ((double)(_cnt_total - 1))) : 0; }
+       bool       ok (void) { return _cnt_total > 200; }
+       bool       have_signal (void) { return (_monotonic_cnt - _last_signal_tme) < (uint64_t) _sample_rate ; }
+
+private:
+       int64_t parse_mclk (uint8_t* buf, pframes_t timestamp) const;
+       int64_t parse_mtc  (uint8_t* buf, pframes_t timestamp) const;
+
+       framecnt_t _sample_rate;
+
+       uint64_t _monotonic_cnt;
+       uint64_t _last_signal_tme;
+
+       uint64_t _cnt_total;
+       uint64_t _dly_total;
+       uint32_t _min_delay;
+       uint32_t _max_delay;
+       double   _avg_delay;
+       double   _var_m;
+       double   _var_s;
+
+};
+
+}
+
+#endif /* __libardour_mididm_h__ */
index 627c8475139616f0e5a0788f3acd65a997905f34..f1c03a79d268b182efeed5ebe681b0fdc0778485 100644 (file)
@@ -177,7 +177,7 @@ class LIBARDOUR_API PluginInsert : public Processor
        /** details of the match currently being used */
        Match _match;
 
-       void automation_run (BufferSet& bufs, pframes_t nframes);
+       void automation_run (BufferSet& bufs, framepos_t start, pframes_t nframes);
        void connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t offset, bool with_auto, framepos_t now = 0);
 
        void create_automatable_parameters ();
index 9fd739a4da725c66588f82e64da0602f4cb239ab..3afd66bf3ca9613ef61a639d120e9c0e2fb411b4 100644 (file)
@@ -36,6 +36,19 @@ struct LIBARDOUR_API RegionSortByLayer {
        }
 };
 
+/* sort by RegionSortByLayerAndPosition()
+ * is equivalent to
+ * stable_sort by RegionSortByPosition();
+ * stable_sort by RegionSortByLayer();
+ */
+struct LIBARDOUR_API RegionSortByLayerAndPosition {
+       bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
+               return
+                          (a->layer() < b->layer()  && a->position() < b->position())
+                       || (a->layer() == b->layer() && a->position() < b->position());
+       }
+};
+
 } // namespace
 
 #endif /* __libardour_region_sorters_h__ */
index fb2b18f177d5e54fc7af1e3c635af413f9dc487f..6dbdda496cefbb7590d2406fc7312518ec78079a 100644 (file)
@@ -54,6 +54,7 @@
 namespace ARDOUR {
 
 class Amp;
+class DelayLine;
 class Delivery;
 class IOProcessor;
 class Panner;
@@ -190,6 +191,7 @@ class LIBARDOUR_API Route : public SessionObject, public Automatable, public Rou
        PeakMeter&       peak_meter()       { return *_meter.get(); }
        const PeakMeter& peak_meter() const { return *_meter.get(); }
        boost::shared_ptr<PeakMeter> shared_peak_meter() const { return _meter; }
+       boost::shared_ptr<DelayLine> delay_line() const  { return _delayline; }
 
        void flush_processors ();
 
@@ -285,6 +287,21 @@ class LIBARDOUR_API Route : public SessionObject, public Automatable, public Rou
        PBD::Signal1<void,void*> mute_changed;
        PBD::Signal0<void>       mute_points_changed;
 
+       /** track numbers - assigned by session
+        * nubers > 0 indicate tracks (audio+midi)
+        * nubers < 0 indicate busses
+        * zero is reserved for unnumbered special busses.
+        * */
+       PBD::Signal0<void> track_number_changed;
+       int64_t track_number() const { return _track_number; }
+
+       void set_track_number(int64_t tn) {
+               if (tn == _track_number) { return; }
+               _track_number = tn;
+               track_number_changed();
+               PropertyChanged (ARDOUR::Properties::name);
+       }
+
        /** the processors have changed; the parameter indicates what changed */
        PBD::Signal1<void,RouteProcessorChange> processors_changed;
        PBD::Signal1<void,void*> record_enable_changed;
@@ -470,11 +487,20 @@ class LIBARDOUR_API Route : public SessionObject, public Automatable, public Rou
                                             pframes_t nframes, int declick,
                                             bool gain_automation_ok);
 
+       virtual void bounce_process (BufferSet& bufs,
+                                    framepos_t start_frame, framecnt_t nframes,
+                                                                                                                        boost::shared_ptr<Processor> endpoint, bool include_endpoint,
+                                    bool for_export, bool for_freeze);
+
+       framecnt_t   bounce_get_latency (boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export, bool for_freeze) const;
+       ChanCount    bounce_get_output_streams (ChanCount &cc, boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export, bool for_freeze) const;
+
        boost::shared_ptr<IO> _input;
        boost::shared_ptr<IO> _output;
 
        bool           _active;
        framecnt_t     _signal_latency;
+       framecnt_t     _signal_latency_at_amp_position;
        framecnt_t     _initial_delay;
        framecnt_t     _roll_delay;
 
@@ -539,6 +565,7 @@ class LIBARDOUR_API Route : public SessionObject, public Automatable, public Rou
 
        boost::shared_ptr<Amp>       _amp;
        boost::shared_ptr<PeakMeter> _meter;
+       boost::shared_ptr<DelayLine> _delayline;
 
        boost::shared_ptr<Processor> the_instrument_unlocked() const;
 
@@ -550,6 +577,8 @@ class LIBARDOUR_API Route : public SessionObject, public Automatable, public Rou
        bool _has_order_key;
         uint32_t _remote_control_id;
 
+       int64_t _track_number;
+
        void input_change_handler (IOChange, void *src);
        void output_change_handler (IOChange, void *src);
 
diff --git a/libs/ardour/ardour/route_sorters.h b/libs/ardour/ardour/route_sorters.h
new file mode 100644 (file)
index 0000000..022d5a2
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+    Copyright (C) 2000-2014 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __libardour_route_sorters_h__
+#define __libardour_route_sorters_h__
+
+#include "ardour/route.h"
+
+namespace ARDOUR {
+
+struct SignalOrderRouteSorter {
+       bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
+               if (a->is_master() || a->is_monitor()) {
+                       /* "a" is a special route (master, monitor, etc), and comes
+                        * last in the mixer ordering
+                        */
+                       return false;
+               } else if (b->is_master() || b->is_monitor()) {
+                       /* everything comes before b */
+                       return true;
+               }
+               return a->order_key () < b->order_key ();
+       }
+};
+
+} // namespace
+
+#endif /* __libardour_route_sorters_h__ */
index a9d2c5dacd2574555ecffba56052303b5083f3ec..17343bff96d6916bb00acea91b0a495a5779b0a7 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "ardour/ardour.h"
 #include "ardour/delivery.h"
+#include "ardour/delayline.h"
 
 namespace ARDOUR {
 
@@ -59,6 +60,12 @@ class LIBARDOUR_API Send : public Delivery
        bool can_support_io_configuration (const ChanCount& in, ChanCount& out);
        bool configure_io (ChanCount in, ChanCount out);
 
+       /* latency compensation */
+       void set_delay_in (framecnt_t);
+       void set_delay_out (framecnt_t);
+       framecnt_t get_delay_in () const { return _delay_in; }
+       framecnt_t get_delay_out () const { return _delay_out; }
+
        void activate ();
        void deactivate ();
 
@@ -73,6 +80,7 @@ class LIBARDOUR_API Send : public Delivery
        bool _metering;
        boost::shared_ptr<Amp> _amp;
        boost::shared_ptr<PeakMeter> _meter;
+       boost::shared_ptr<DelayLine> _delayline;
 
   private:
        /* disallow copy construction */
@@ -82,6 +90,9 @@ class LIBARDOUR_API Send : public Delivery
        int set_state_2X (XMLNode const &, int);
 
        uint32_t  _bitslot;
+
+       framecnt_t _delay_in;
+       framecnt_t _delay_out;
 };
 
 } // namespace ARDOUR
index 40ada138a6dac0f8027d0536da423afbba66110a..03eccd40a32dd45ea29e3758fa94894d339090c6 100644 (file)
@@ -196,10 +196,10 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
        std::string peak_path (std::string) const;
 
        std::string peak_path_from_audio_path (std::string) const;
-       std::string new_audio_source_name (const std::string&, uint32_t nchans, uint32_t chan, bool destructive);
-       std::string new_midi_source_name (const std::string&);
-       std::string new_source_path_from_name (DataType type, const std::string&);
+       std::string new_audio_source_path (const std::string&, uint32_t nchans, uint32_t chan, bool destructive, bool take_required);
+       std::string new_midi_source_path (const std::string&);
         RouteList new_route_from_template (uint32_t how_many, const std::string& template_path, const std::string& name);
+       std::vector<std::string> get_paths_for_new_sources (bool allow_replacing, const std::string& import_file_path, uint32_t channels);
 
        void process (pframes_t nframes);
 
@@ -260,6 +260,10 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
        bool route_name_unique (std::string) const;
        bool route_name_internal (std::string) const;
 
+       uint32_t track_number_decimals () const {
+               return _track_number_decimals;
+       }
+
        bool get_record_enabled() const {
                return (record_status () >= Enabled);
        }
@@ -397,13 +401,14 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
        int rename (const std::string&);
        bool get_nsm_state () const { return _under_nsm_control; }
        void set_nsm_state (bool state) { _under_nsm_control = state; }
+       bool save_default_options ();
 
        PBD::Signal1<void,std::string> StateSaved;
        PBD::Signal0<void> StateReady;
        PBD::Signal0<void> SaveSession;
 
-       std::vector<std::string*>* possible_states() const;
-       static std::vector<std::string*>* possible_states (std::string path);
+       std::vector<std::string> possible_states() const;
+       static std::vector<std::string> possible_states (std::string path);
 
        XMLNode& get_state();
        int      set_state(const XMLNode& node, int version); // not idempotent
@@ -425,6 +430,23 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
 
        StateOfTheState state_of_the_state() const { return _state_of_the_state; }
 
+       class StateProtector {
+               public:
+                       StateProtector (Session* s) : _session (s) {
+                               g_atomic_int_inc (&s->_suspend_save);
+                       }
+                       ~StateProtector () {
+                               if (g_atomic_int_dec_and_test (&_session->_suspend_save)) {
+                                       while (_session->_save_queued) {
+                                               _session->_save_queued = false;
+                                               _session->save_state ("");
+                                       }
+                               }
+                       }
+               private:
+                       Session * _session;
+       };
+
        void add_route_group (RouteGroup *);
        void remove_route_group (RouteGroup&);
        void reorder_route_groups (std::list<RouteGroup*>);
@@ -527,8 +549,6 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
 
        boost::shared_ptr<Region> find_whole_file_parent (boost::shared_ptr<Region const>) const;
 
-       std::string path_from_region_name (DataType type, std::string name, std::string identifier);
-
        boost::shared_ptr<Region>      XMLRegionFactory (const XMLNode&, bool full);
        boost::shared_ptr<AudioRegion> XMLAudioRegionFactory (const XMLNode&, bool full);
        boost::shared_ptr<MidiRegion>  XMLMidiRegionFactory (const XMLNode&, bool full);
@@ -585,8 +605,8 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
        boost::shared_ptr<MidiSource> create_midi_source_by_stealing_name (boost::shared_ptr<Track>);
 
        boost::shared_ptr<Source> source_by_id (const PBD::ID&);
-       boost::shared_ptr<AudioFileSource> source_by_path_and_channel (const std::string&, uint16_t) const;
-       boost::shared_ptr<MidiSource> source_by_path (const std::string&) const;
+       boost::shared_ptr<AudioFileSource> audio_source_by_path_and_channel (const std::string&, uint16_t) const;
+       boost::shared_ptr<MidiSource> midi_source_by_path (const std::string&) const;
        uint32_t count_sources_by_origin (const std::string&);
 
        void add_playlist (boost::shared_ptr<Playlist>, bool unused = false);
@@ -608,7 +628,8 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
 
        boost::shared_ptr<Region> write_one_track (AudioTrack&, framepos_t start, framepos_t end,
                                                   bool overwrite, std::vector<boost::shared_ptr<Source> >&, InterThreadInfo& wot,
-                                                  boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export);
+                                                  boost::shared_ptr<Processor> endpoint,
+                                                        bool include_endpoint, bool for_export, bool for_freeze);
        int freeze_all (InterThreadInfo&);
 
        /* session-wide solo/mute/rec-enable */
@@ -791,6 +812,10 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
                return _exporting;
        }
 
+       bool bounce_processing() const {
+               return _bounce_processing_active;
+       }
+
        /* this is a private enum, but setup_enum_writer() needs it,
           and i can't find a way to give that function
           friend access. sigh.
@@ -928,6 +953,7 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
        mutable gint             processing_prohibited;
        process_function_type    process_function;
        process_function_type    last_process_function;
+       bool                    _bounce_processing_active;
        bool                     waiting_for_sync_offset;
        framecnt_t              _base_frame_rate;
        framecnt_t              _current_frame_rate;  //this includes video pullup offset
@@ -982,13 +1008,15 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
        void process_without_events (pframes_t);
        void process_with_events    (pframes_t);
        void process_audition       (pframes_t);
-        int  process_export         (pframes_t);
+       int  process_export         (pframes_t);
        int  process_export_fw      (pframes_t);
 
        void block_processing() { g_atomic_int_set (&processing_prohibited, 1); }
        void unblock_processing() { g_atomic_int_set (&processing_prohibited, 0); }
        bool processing_blocked() const { return g_atomic_int_get (&processing_prohibited); }
 
+       static const framecnt_t bounce_chunk_size;
+
        /* slave tracking */
 
        static const int delta_accumulator_size = 25;
@@ -1087,6 +1115,10 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
        bool             state_was_pending;
        StateOfTheState _state_of_the_state;
 
+       friend class    StateProtector;
+       gint            _suspend_save; /* atomic */
+       volatile bool   _save_queued;
+
        void     auto_save();
        int      load_options (const XMLNode&);
        int      load_state (std::string snapshot_name);
@@ -1334,6 +1366,11 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
                                 ChanCount input_start = ChanCount (), ChanCount output_start = ChanCount ());
        void midi_output_change_handler (IOChange change, void* /*src*/, boost::weak_ptr<Route> midi_track);
 
+       /* track numbering */
+
+       void reassign_track_numbers ();
+       uint32_t _track_number_decimals;
+
        /* mixer stuff */
 
        bool solo_update_disabled;
@@ -1446,7 +1483,7 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
 
        bool no_questions_about_missing_files;
 
-       std::string get_best_session_directory_for_new_source ();
+       std::string get_best_session_directory_for_new_audio ();
 
        mutable gint _playback_load;
        mutable gint _capture_load;
@@ -1631,6 +1668,7 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
        static int get_session_info_from_path (XMLTree& state_tree, const std::string& xmlpath);
 };
 
+
 } // namespace ARDOUR
 
 #endif /* __ardour_session_h__ */
index e72d19f32208708179e349ef31397f08543d8eb7..c0af2239439c04ffdb8afc2547a688e4f6d0880e 100644 (file)
@@ -35,6 +35,9 @@ public:
        XMLNode& get_variables ();
        void set_variables (XMLNode const &);
 
+       bool load_state ();
+       bool save_state ();
+
        /* define accessor methods */
 
 #undef  CONFIG_VARIABLE
index 6349692e774e737f87d41a4ab3eaba0284e64105..ec11fc545dc7e5d5c1a5c6ecfaf66d78bb8042eb 100644 (file)
@@ -25,7 +25,6 @@
     the value of the variable.
 *****************************************************/
 
-CONFIG_VARIABLE (CrossfadeChoice, xfade_choice, "xfade-choice", ConstantPowerMinus3dB)
 CONFIG_VARIABLE (uint32_t, destructive_xfade_msecs,  "destructive-xfade-msecs", 2)
 CONFIG_VARIABLE (bool, use_region_fades, "use-region-fades", true)
 CONFIG_VARIABLE (bool, show_region_fades, "show-region-fades", true)
@@ -41,6 +40,9 @@ CONFIG_VARIABLE (Timecode::TimecodeFormat, timecode_format, "timecode-format", T
 CONFIG_VARIABLE_SPECIAL(std::string, raid_path, "raid-path", "", PBD::path_expand)
 CONFIG_VARIABLE_SPECIAL(std::string, audio_search_path, "audio-search-path", "", PBD::search_path_expand)
 CONFIG_VARIABLE_SPECIAL(std::string, midi_search_path, "midi-search-path", "", PBD::search_path_expand)
+CONFIG_VARIABLE (bool, track_name_number, "track-name-number", false)
+CONFIG_VARIABLE (bool, track_name_take, "track-name-take", false)
+CONFIG_VARIABLE (std::string, take_name, "take-name", "Take1")
 CONFIG_VARIABLE (bool, jack_time_master, "jack-time-master", true)
 CONFIG_VARIABLE (bool, use_video_sync, "use-video-sync", false)
 CONFIG_VARIABLE (float, video_pullup, "video-pullup", 0.0f)
index 9d85f943529d3e4d531d1ca27fe542804f8afe87..84c45f9b3cf342be199c2e552c58daa74c715f12 100644 (file)
@@ -36,24 +36,17 @@ template<typename T> class MidiRingBuffer;
 /** Standard Midi File (Type 0) Source */
 class LIBARDOUR_API SMFSource : public MidiSource, public FileSource, public Evoral::SMF {
 public:
+       /** Constructor for new internal-to-session files */
+       SMFSource (Session& session, const std::string& path, Source::Flag flags);
+
        /** Constructor for existing external-to-session files */
-       SMFSource (Session& session, const std::string& path,
-                       Source::Flag flags = Source::Flag(0));
+       SMFSource (Session& session, const std::string& path);
 
        /** Constructor for existing in-session files */
        SMFSource (Session& session, const XMLNode&, bool must_exist = false);
 
        virtual ~SMFSource ();
 
-       /** Rename the file on disk referenced by this source to \param newname
-        *
-        * This method exists only for MIDI file sources, not for audio, which 
-        * can never be renamed. It exists for MIDI so that we can get
-        * consistent and sane region/source numbering when regions are added
-        * manually (which never happens with audio).
-        */
-       int rename (const std::string& name);
-
         bool safe_file_extension (const std::string& path) const {
                return safe_midi_file_extension(path);
        }
@@ -75,6 +68,7 @@ public:
        void ensure_disk_file ();
 
        static bool safe_midi_file_extension (const std::string& path);
+       static bool valid_midi_file (const std::string& path);
 
        void prevent_deletion ();
 
index 25b8f003b25b8b3a3c6163ed7d5ee4382ac38d53..831f8db5f727f685dbf8711cc838497022e6901e 100644 (file)
@@ -38,7 +38,16 @@ class LIBARDOUR_API SndFileSource : public AudioFileSource {
                       SampleFormat samp_format, HeaderFormat hdr_format, framecnt_t rate,
                       Flag flags = SndFileSource::default_writable_flags);
 
-       /** Constructor to be called for existing in-session files */
+       /* Constructor to be called for recovering files being used for
+        * capture. They are in-session, they already exist, they should not
+        * be writable. They are an odd hybrid (from a constructor point of
+        * view) of the previous two constructors.
+        */
+       SndFileSource (Session&, const std::string& path, int chn);
+
+       /** Constructor to be called for existing in-session files during
+        * session loading 
+        */
        SndFileSource (Session&, const XMLNode&);
 
        ~SndFileSource ();
diff --git a/libs/ardour/ardour/soundcloud_upload.h b/libs/ardour/ardour/soundcloud_upload.h
new file mode 100644 (file)
index 0000000..c1074f0
--- /dev/null
@@ -0,0 +1,55 @@
+/* soundcloud_upload.h ******************************************************
+
+       Adapted for Ardour by Ben Loftis, March 2012
+
+*****************************************************************************/
+
+#ifndef __ardour_soundcloud_upload_h__
+#define __ardour_soundcloud_upload_h__
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <stdio.h>
+#include <cstring>
+#include <string>
+#include <sstream>
+#include <vector>
+
+#include "curl/curl.h"
+#include "ardour/session_handle.h"
+#include "ardour/export_handler.h"
+#include "pbd/signals.h"
+
+//--- struct to store XML file
+struct MemoryStruct {
+       char *memory;
+       size_t size;
+};
+
+
+class SoundcloudUploader
+{
+public:
+       SoundcloudUploader();
+       ~SoundcloudUploader();
+
+       std::string     Get_Auth_Token(std::string username, std::string password);
+       std::string Upload (std::string file_path, std::string title, std::string token, bool ispublic, bool downloadable, ARDOUR::ExportHandler *caller);
+       static int progress_callback(void *caller, double dltotal, double dlnow, double ultotal, double ulnow);
+
+
+private:
+
+       void            setcUrlOptions();
+
+       CURL *curl_handle;
+       CURLM *multi_handle;
+       char errorBuffer[CURL_ERROR_SIZE];      // storage for cUrl error message
+
+       std::string title;
+       ARDOUR::ExportHandler *caller;
+
+};
+
+#endif /* __ardour_soundcloud_upload_h__ */
index 4ef2cd60ee6506534518d5fb0e0c3a76b542a951..614fdce7b19deeda12d905cb8122af47a307c2e6 100644 (file)
@@ -57,6 +57,9 @@ class LIBARDOUR_API SourceFactory {
                 bool destructive, framecnt_t rate, bool announce = true, bool async = false);
 
 
+       static boost::shared_ptr<Source> createForRecovery
+               (DataType type, Session&, const std::string& path, int chn);
+
        static boost::shared_ptr<Source> createFromPlaylist
                (DataType type, Session& s, boost::shared_ptr<Playlist> p, const PBD::ID& orig, const std::string& name,
                 uint32_t chn, frameoffset_t start, framecnt_t len, bool copy, bool defer_peaks);
index 40e429720ef9371448b0315a38baf1532ffaa4a6..ae865c7bff7ce040b87449688dfa8a54ae898c0e 100644 (file)
@@ -32,6 +32,7 @@ class LIBARDOUR_API SystemExec
 public:
        SystemExec (std::string c, std::string a = "");
        SystemExec (std::string c, char ** a);
+       SystemExec (std::string c, const std::map<char, std::string> subs);
        ~SystemExec ();
 
        int start (int stderr_mode = 1) {
index f30476a4747ea662959269014a45b6545d7a8360..bf686fd57ee1e0053f21eb29314138335a5c90a8 100644 (file)
@@ -35,7 +35,7 @@ public:
        ThreadBuffers ();
        ~ThreadBuffers ();
 
-       void ensure_buffers (ChanCount howmany = ChanCount::ZERO);
+       void ensure_buffers (ChanCount howmany = ChanCount::ZERO, size_t custom = 0);
 
        BufferSet* silent_buffers;
        BufferSet* scratch_buffers;
index ee74fee46e883711e3484e19d8c662b53bbada4f..d33e24e4e684597bb35d0d88ed4da7c3250ebbeb 100644 (file)
@@ -44,6 +44,7 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream
        int init ();
 
        bool set_name (const std::string& str);
+       void resync_track_name ();
 
        TrackMode mode () const { return _mode; }
        virtual int set_mode (TrackMode /*m*/) { return false; }
@@ -96,7 +97,7 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream
        virtual boost::shared_ptr<Region> bounce_range (framepos_t start, framepos_t end, InterThreadInfo&, 
                                                        boost::shared_ptr<Processor> endpoint, bool include_endpoint) = 0;
        virtual int export_stuff (BufferSet& bufs, framepos_t start_frame, framecnt_t nframes,
-                                 boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export) = 0;
+                                 boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export, bool for_freeze) = 0;
 
        XMLNode&    get_state();
        XMLNode&    get_template();
@@ -228,6 +229,9 @@ private:
        void diskstream_record_enable_changed ();
        void diskstream_speed_changed ();
        void diskstream_alignment_style_changed ();
+       void parameter_changed (std::string const & p);
+
+       std::string _diskstream_name;
 };
 
 }; /* namespace ARDOUR*/
index 92a8c0da5be97d4e2c35d060551b2ec92c95b052..216de8bb0c31c6d5487c3fa4e5b40dc40acf2dab 100644 (file)
@@ -420,17 +420,6 @@ namespace ARDOUR {
                MixerOrdered
        };
 
-       enum CrossfadeModel {
-               FullCrossfade,
-               ShortCrossfade
-       };
-
-       enum CrossfadeChoice {
-               RegionFades,
-               ConstantPowerMinus3dB,
-               ConstantPowerMinus6dB,
-       };
-
        enum ListenPosition {
                AfterFaderListen,
                PreFaderListen
@@ -623,8 +612,6 @@ std::istream& operator>>(std::istream& o, ARDOUR::AFLPosition& sf);
 std::istream& operator>>(std::istream& o, ARDOUR::RemoteModel& sf);
 std::istream& operator>>(std::istream& o, ARDOUR::ListenPosition& sf);
 std::istream& operator>>(std::istream& o, ARDOUR::InsertMergePolicy& sf);
-std::istream& operator>>(std::istream& o, ARDOUR::CrossfadeModel& sf);
-std::istream& operator>>(std::istream& o, ARDOUR::CrossfadeChoice& sf);
 std::istream& operator>>(std::istream& o, ARDOUR::SyncSource& sf);
 std::istream& operator>>(std::istream& o, ARDOUR::ShuttleBehaviour& sf);
 std::istream& operator>>(std::istream& o, ARDOUR::ShuttleUnits& sf);
@@ -647,8 +634,6 @@ std::ostream& operator<<(std::ostream& o, const ARDOUR::AFLPosition& sf);
 std::ostream& operator<<(std::ostream& o, const ARDOUR::RemoteModel& sf);
 std::ostream& operator<<(std::ostream& o, const ARDOUR::ListenPosition& sf);
 std::ostream& operator<<(std::ostream& o, const ARDOUR::InsertMergePolicy& sf);
-std::ostream& operator<<(std::ostream& o, const ARDOUR::CrossfadeModel& sf);
-std::ostream& operator<<(std::ostream& o, const ARDOUR::CrossfadeChoice& sf);
 std::ostream& operator<<(std::ostream& o, const ARDOUR::SyncSource& sf);
 std::ostream& operator<<(std::ostream& o, const ARDOUR::ShuttleBehaviour& sf);
 std::ostream& operator<<(std::ostream& o, const ARDOUR::ShuttleUnits& sf);
index 6dba01c79754b9782d41956b709a9f8806ae1e20..4b71c1586ed74ebfee6133c315bcefe65672fa04 100644 (file)
@@ -39,6 +39,8 @@
 
 class XMLNode;
 
+namespace ARDOUR {
+
 LIBARDOUR_API std::string legalize_for_path (const std::string& str);
 LIBARDOUR_API std::string legalize_for_universal_path (const std::string& str);
 LIBARDOUR_API std::string legalize_for_uri (const std::string& str);
@@ -56,6 +58,7 @@ static inline float f_max(float x, float a) {
 }
 
 LIBARDOUR_API std::string bump_name_once(const std::string& s, char delimiter);
+LIBARDOUR_API std::string bump_name_number(const std::string& s);
 
 LIBARDOUR_API int cmp_nocase (const std::string& s, const std::string& s2);
 LIBARDOUR_API int cmp_nocase_utf8 (const std::string& s1, const std::string& s2);
@@ -174,5 +177,7 @@ LIBARDOUR_API uint32_t how_many_dsp_threads ();
 LIBARDOUR_API std::string CFStringRefToStdString(CFStringRef stringRef);
 #endif // __APPLE__
 
+} //namespave
+
 #endif /* __ardour_utils_h__ */
 
index ffb8ef8a1b924a8249fd8446f26950207e1c2c20..0d2c664e4827290e02f527620dd54f12337f19f9 100644 (file)
@@ -1908,7 +1908,7 @@ AudioDiskstream::use_new_write_source (uint32_t n)
 
        try {
                if ((chan->write_source = _session.create_audio_source_for_session (
-                            n_channels().n_audio(), name(), n, destructive())) == 0) {
+                            n_channels().n_audio(), write_source_name(), n, destructive())) == 0) {
                        throw failed_constructor();
                }
        }
@@ -2179,11 +2179,16 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node)
                                continue;
                        }
 
+                       /* XXX as of June 2014, we always record to mono
+                          files. Since this Source is being created as part of
+                          crash recovery, we know that we need the first
+                          channel (the final argument to the SourceFactory
+                          call below). If we ever support non-mono files for
+                          capture, this will need rethinking.
+                       */
+
                        try {
-                               fs = boost::dynamic_pointer_cast<AudioFileSource> (
-                                       SourceFactory::createWritable (
-                                               DataType::AUDIO, _session,
-                                               prop->value(), false, _session.frame_rate()));
+                               fs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createForRecovery (DataType::AUDIO, _session, prop->value(), 0));
                        }
 
                        catch (failed_constructor& err) {
@@ -2214,21 +2219,31 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node)
                return -1;
        }
 
-       boost::shared_ptr<AudioRegion> region;
-
        try {
 
-               PropertyList plist;
+               boost::shared_ptr<AudioRegion> wf_region;
+               boost::shared_ptr<AudioRegion> region;
+               
+               /* First create the whole file region */
 
+               PropertyList plist;
+               
                plist.add (Properties::start, 0);
                plist.add (Properties::length, first_fs->length (first_fs->timeline_position()));
                plist.add (Properties::name, region_name_from_path (first_fs->name(), true));
 
-               region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, plist));
+               wf_region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, plist));
+
+               wf_region->set_automatic (true);
+               wf_region->set_whole_file (true);
+               wf_region->special_set_position (position);
 
-               region->set_automatic (true);
-               region->set_whole_file (true);
-               region->special_set_position (0);
+               /* Now create a region that isn't the whole file for adding to
+                * the playlist */
+
+               region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, plist));
+               
+               _playlist->add_region (region, position);
        }
 
        catch (failed_constructor& err) {
@@ -2239,7 +2254,6 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node)
                return -1;
        }
 
-       _playlist->add_region (region, position);
 
        return 0;
 }
@@ -2437,6 +2451,9 @@ AudioDiskstream::ChannelInfo::~ChannelInfo ()
 bool
 AudioDiskstream::set_name (string const & name)
 {
+       if (_name == name) {
+               return true;
+       }
        Diskstream::set_name (name);
 
        /* get a new write source so that its name reflects the new diskstream name */
@@ -2451,3 +2468,24 @@ AudioDiskstream::set_name (string const & name)
 
        return true;
 }
+
+bool
+AudioDiskstream::set_write_source_name (const std::string& str) {
+       if (_write_source_name == str) {
+               return true;
+       }
+
+       Diskstream::set_write_source_name (str);
+
+       if (_write_source_name == name()) {
+               return true;
+       }
+       boost::shared_ptr<ChannelList> c = channels.reader();
+       ChannelList::iterator i;
+       int n = 0;
+
+       for (n = 0, i = c->begin(); i != c->end(); ++i, ++n) {
+               use_new_write_source (n);
+       }
+       return true;
+}
index 8e14fff40225e36cc05d3ed562f7e07d756a16c1..13d5c43dda312d392e93199defdbd6a6b94f858c 100644 (file)
@@ -369,6 +369,13 @@ AudioTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_fram
 
        process_output_buffers (bufs, start_frame, end_frame, nframes, declick, (!diskstream->record_enabled() && _session.transport_rolling()));
 
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               boost::shared_ptr<Delivery> d = boost::dynamic_pointer_cast<Delivery> (*i);
+               if (d) {
+                       d->flush_buffers (nframes);
+               }
+       }
+
        need_butler = diskstream->commit (playback_distance);
 
        return 0;
@@ -376,7 +383,7 @@ AudioTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_fram
 
 int
 AudioTrack::export_stuff (BufferSet& buffers, framepos_t start, framecnt_t nframes,
-                         boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export)
+                         boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export, bool for_freeze)
 {
        boost::scoped_array<gain_t> gain_buffer (new gain_t[nframes]);
        boost::scoped_array<Sample> mix_buffer (new Sample[nframes]);
@@ -410,38 +417,7 @@ AudioTrack::export_stuff (BufferSet& buffers, framepos_t start, framecnt_t nfram
                }
        }
 
-       // If no processing is required, there's no need to go any further.
-
-       if (!endpoint && !include_endpoint) {
-               return 0;
-       }
-
-       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-
-               if (!include_endpoint && (*i) == endpoint) {
-                       break;
-               }
-               
-               /* if we're not exporting, stop processing if we come across a routing processor.
-                */
-               
-               if (!for_export && (*i)->does_routing()) {
-                       break;
-               }
-
-               /* even for export, don't run any processor that does routing. 
-
-                  oh, and don't bother with the peak meter either.
-                */
-
-               if (!(*i)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*i)) {
-                       (*i)->run (buffers, start, start+nframes, nframes, true);
-               }
-               
-               if ((*i) == endpoint) {
-                       break;
-               }
-       }
+       bounce_process (buffers, start, nframes, endpoint, include_endpoint, for_export, for_freeze);
 
        return 0;
 }
@@ -514,7 +490,7 @@ AudioTrack::bounce_range (framepos_t start, framepos_t end, InterThreadInfo& itt
                          boost::shared_ptr<Processor> endpoint, bool include_endpoint)
 {
        vector<boost::shared_ptr<Source> > srcs;
-       return _session.write_one_track (*this, start, end, false, srcs, itt, endpoint, include_endpoint, false);
+       return _session.write_one_track (*this, start, end, false, srcs, itt, endpoint, include_endpoint, false, false);
 }
 
 void
@@ -557,8 +533,8 @@ AudioTrack::freeze_me (InterThreadInfo& itt)
 
        boost::shared_ptr<Region> res;
 
-       if ((res = _session.write_one_track (*this, _session.current_start_frame(), _session.current_end_frame(), true, srcs, itt, 
-                                            main_outs(), false, false)) == 0) {
+       if ((res = _session.write_one_track (*this, _session.current_start_frame(), _session.current_end_frame(),
+                                       true, srcs, itt, main_outs(), false, false, true)) == 0) {
                return;
        }
 
@@ -569,7 +545,10 @@ AudioTrack::freeze_me (InterThreadInfo& itt)
 
                for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); ++r) {
 
-                       if (!(*r)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*r)) {
+                       if ((*r)->does_routing() && (*r)->active()) {
+                               break;
+                       }
+                       if (!boost::dynamic_pointer_cast<PeakMeter>(*r)) {
 
                                FreezeRecordProcessorInfo* frii  = new FreezeRecordProcessorInfo ((*r)->get_state(), (*r));
 
@@ -577,9 +556,10 @@ AudioTrack::freeze_me (InterThreadInfo& itt)
 
                                _freeze_record.processor_info.push_back (frii);
 
-                               /* now deactivate the processor */
-
-                               (*r)->deactivate ();
+                               /* now deactivate the processor, */
+                               if (!boost::dynamic_pointer_cast<Amp>(*r)) {
+                                       (*r)->deactivate ();
+                               }
                        }
                        
                        _session.set_dirty ();
index 9e461c63466515cfa63aff5d12384b7b1f8083e0..add2407f841cfc87f9602aed7f4911c934fc3e64 100644 (file)
@@ -28,7 +28,7 @@
 #include "pbd/xml++.h"
 #include "pbd/convert.h"
 #include "pbd/whitespace.h"
-#include "pbd/pathscanner.h"
+#include "pbd/file_utils.h"
 #include "pbd/locale_guard.h"
 
 #include <glibmm/threads.h>
@@ -2049,20 +2049,19 @@ AUPlugin::current_preset() const
 void
 AUPlugin::find_presets ()
 {
-       vector<string*>* preset_files;
-       PathScanner scanner;
+       vector<string> preset_files;
 
        user_preset_map.clear ();
 
-       preset_files = scanner (preset_search_path, au_preset_filter, this, true, true, -1, true);
+       find_files_matching_filter (preset_files, preset_search_path, au_preset_filter, this, true, true, true);
 
-       if (!preset_files) {
+       if (preset_files.empty()) {
                return;
        }
 
-       for (vector<string*>::iterator x = preset_files->begin(); x != preset_files->end(); ++x) {
+       for (vector<string>::iterator x = preset_files.begin(); x != preset_files.end(); ++x) {
 
-               string path = *(*x);
+               string path = *x;
                string preset_name;
 
                /* make an initial guess at the preset name using the path */
@@ -2079,11 +2078,8 @@ AUPlugin::find_presets ()
                        user_preset_map[preset_name] = path;
                }
 
-               delete *x;
        }
 
-       delete preset_files;
-
        /* now fill the vector<string> with the names we have */
 
        for (UserPresetMap::iterator i = user_preset_map.begin(); i != user_preset_map.end(); ++i) {
index 8d4b17ebd4c607f27065bd79d66b703e4518b46a..6c9fd442fbc4c45783769872b16cf4be274dc166 100644 (file)
@@ -48,6 +48,7 @@
 #include "ardour/meter.h"
 #include "ardour/midi_port.h"
 #include "ardour/midiport_manager.h"
+#include "ardour/mididm.h"
 #include "ardour/mtdm.h"
 #include "ardour/port.h"
 #include "ardour/process_thread.h"
@@ -73,7 +74,8 @@ AudioEngine::AudioEngine ()
        , m_meter_thread (0)
        , _main_thread (0)
        , _mtdm (0)
-       , _measuring_latency (false)
+       , _mididm (0)
+       , _measuring_latency (MeasureNone)
        , _latency_input_port (0)
        , _latency_output_port (0)
        , _latency_flush_frames (0)
@@ -195,7 +197,7 @@ AudioEngine::process_callback (pframes_t nframes)
 
        bool return_after_remove_check = false;
 
-       if (_measuring_latency && _mtdm) {
+       if (_measuring_latency == MeasureAudio && _mtdm) {
                /* run a normal cycle from the perspective of the PortManager
                   so that we get silence on all registered ports.
                   
@@ -218,6 +220,28 @@ AudioEngine::process_callback (pframes_t nframes)
                PortManager::cycle_end (nframes);
                return_after_remove_check = true;
 
+       } else if (_measuring_latency == MeasureMIDI && _mididm) {
+               /* run a normal cycle from the perspective of the PortManager
+                  so that we get silence on all registered ports.
+
+                  we overwrite the silence on the two ports used for latency
+                  measurement.
+               */
+
+               PortManager::cycle_start (nframes);
+               PortManager::silence (nframes);
+
+               if (_latency_input_port && _latency_output_port) {
+                       PortEngine& pe (port_engine());
+
+                       _mididm->process (nframes, pe,
+                                       pe.get_buffer (_latency_input_port, nframes),
+                                       pe.get_buffer (_latency_output_port, nframes));
+               }
+
+               PortManager::cycle_end (nframes);
+               return_after_remove_check = true;
+
        } else if (_latency_flush_frames) {
                
                /* wait for the appropriate duration for the MTDM signal to
@@ -482,15 +506,15 @@ AudioEngine::discover_backends ()
 #else
        Glib::PatternSpec dll_extension_pattern("*backend.dll");
 #endif
-       
-       find_matching_files_in_search_path (backend_search_path (),
-                                           so_extension_pattern, backend_modules);
 
-       find_matching_files_in_search_path (backend_search_path (),
-                                           dylib_extension_pattern, backend_modules);
+       find_files_matching_pattern (backend_modules, backend_search_path (),
+                                    so_extension_pattern);
 
-       find_matching_files_in_search_path (backend_search_path (),
-                                           dll_extension_pattern, backend_modules);
+       find_files_matching_pattern (backend_modules, backend_search_path (),
+                                    dylib_extension_pattern);
+
+       find_files_matching_pattern (backend_modules, backend_search_path (),
+                                    dll_extension_pattern);
 
        DEBUG_TRACE (DEBUG::AudioEngine, string_compose ("looking for backends in %1\n", backend_search_path().to_string()));
 
@@ -564,8 +588,9 @@ AudioEngine::drop_backend ()
 {
        if (_backend) {
                _backend->stop ();
-        _backend->drop_device();
+               _backend->drop_device();
                _backend.reset ();
+               _running = false;
        }
 }
 
@@ -659,7 +684,7 @@ AudioEngine::stop (bool for_latency)
        
        _running = false;
        _processed_frames = 0;
-       _measuring_latency = false;
+       _measuring_latency = MeasureNone;
        _latency_output_port = 0;
        _latency_input_port = 0;
        _started_for_latency = false;
@@ -1028,12 +1053,6 @@ AudioEngine::setup_required () const
        return true;
 }
 
-MTDM*
-AudioEngine::mtdm() 
-{
-       return _mtdm;
-}
-
 int
 AudioEngine::prepare_for_latency_measurement ()
 {
@@ -1051,7 +1070,7 @@ AudioEngine::prepare_for_latency_measurement ()
 }
 
 int
-AudioEngine::start_latency_detection ()
+AudioEngine::start_latency_detection (bool for_midi)
 {
        if (!running()) {
                if (prepare_for_latency_measurement ()) {
@@ -1064,6 +1083,9 @@ AudioEngine::start_latency_detection ()
        delete _mtdm;
        _mtdm = 0;
 
+       delete _mididm;
+       _mididm = 0;
+
        /* find the ports we will connect to */
 
        PortEngine::PortHandle out = pe.get_port_by_name (_latency_output_name);
@@ -1075,27 +1097,61 @@ AudioEngine::start_latency_detection ()
        }
 
        /* create the ports we will use to read/write data */
-       
-       if ((_latency_output_port = pe.register_port ("latency_out", DataType::AUDIO, IsOutput)) == 0) {
-               stop (true);
-               return -1;
-       }
-       if (pe.connect (_latency_output_port, _latency_output_name)) {
-               pe.unregister_port (_latency_output_port);
-               stop (true);
-               return -1;
-       }
+       if (for_midi) {
+               if ((_latency_output_port = pe.register_port ("latency_out", DataType::MIDI, IsOutput)) == 0) {
+                       stop (true);
+                       return -1;
+               }
+               if (pe.connect (_latency_output_port, _latency_output_name)) {
+                       pe.unregister_port (_latency_output_port);
+                       stop (true);
+                       return -1;
+               }
+
+               const string portname ("latency_in");
+               if ((_latency_input_port = pe.register_port (portname, DataType::MIDI, IsInput)) == 0) {
+                       pe.unregister_port (_latency_input_port);
+                       pe.unregister_port (_latency_output_port);
+                       stop (true);
+                       return -1;
+               }
+               if (pe.connect (_latency_input_name, make_port_name_non_relative (portname))) {
+                       pe.unregister_port (_latency_input_port);
+                       pe.unregister_port (_latency_output_port);
+                       stop (true);
+                       return -1;
+               }
+
+               _mididm = new MIDIDM (sample_rate());
+
+       } else {
+
+               if ((_latency_output_port = pe.register_port ("latency_out", DataType::AUDIO, IsOutput)) == 0) {
+                       stop (true);
+                       return -1;
+               }
+               if (pe.connect (_latency_output_port, _latency_output_name)) {
+                       pe.unregister_port (_latency_output_port);
+                       stop (true);
+                       return -1;
+               }
+
+               const string portname ("latency_in");
+               if ((_latency_input_port = pe.register_port (portname, DataType::AUDIO, IsInput)) == 0) {
+                       pe.unregister_port (_latency_input_port);
+                       pe.unregister_port (_latency_output_port);
+                       stop (true);
+                       return -1;
+               }
+               if (pe.connect (_latency_input_name, make_port_name_non_relative (portname))) {
+                       pe.unregister_port (_latency_input_port);
+                       pe.unregister_port (_latency_output_port);
+                       stop (true);
+                       return -1;
+               }
+
+               _mtdm = new MTDM (sample_rate());
 
-       const string portname ("latency_in");
-       if ((_latency_input_port = pe.register_port (portname, DataType::AUDIO, IsInput)) == 0) {
-               pe.unregister_port (_latency_output_port);
-               stop (true);
-               return -1;
-       }
-       if (pe.connect (_latency_input_name, make_port_name_non_relative (portname))) {
-               pe.unregister_port (_latency_output_port);
-               stop (true);
-               return -1;
        }
 
        LatencyRange lr;
@@ -1106,10 +1162,8 @@ AudioEngine::start_latency_detection ()
        _latency_signal_latency += lr.max;
 
        /* all created and connected, lets go */
-
-       _mtdm = new MTDM (sample_rate());
-       _measuring_latency = true;
-        _latency_flush_frames = samples_per_cycle();
+       _latency_flush_frames = samples_per_cycle();
+       _measuring_latency = for_midi ? MeasureMIDI : MeasureAudio;
 
        return 0;
 }
@@ -1117,7 +1171,7 @@ AudioEngine::start_latency_detection ()
 void
 AudioEngine::stop_latency_detection ()
 {
-       _measuring_latency = false;
+       _measuring_latency = MeasureNone;
 
        if (_latency_output_port) {
                port_engine().unregister_port (_latency_output_port);
index 014baa90317b13c420af7c9863378ef6ddd4f976..7d34b9d9a52c5aabb81fa4bbfa7d11c13acbb774 100644 (file)
@@ -36,6 +36,7 @@
 #include "pbd/stl_delete.h"
 #include "pbd/strsplit.h"
 #include "pbd/shortpath.h"
+#include "pbd/stacktrace.h"
 #include "pbd/enumwriter.h"
 
 #include <sndfile.h>
@@ -115,6 +116,22 @@ AudioFileSource::AudioFileSource (Session& s, const string& path, const string&
        }
 }
 
+/** Constructor used for existing internal-to-session files during crash
+ * recovery. File must exist
+ */
+AudioFileSource::AudioFileSource (Session& s, const string& path, Source::Flag flags, bool /* ignored-exists-for-prototype differentiation */)
+       : Source (s, DataType::AUDIO, path, flags)
+       , AudioSource (s, path)
+       , FileSource (s, DataType::AUDIO, path, string(), flags)
+{
+        /* note that origin remains empty */
+
+       if (init (_path, true)) {
+               throw failed_constructor ();
+       }
+}
+
+
 /** Constructor used for existing internal-to-session files via XML.  File must exist. */
 AudioFileSource::AudioFileSource (Session& s, const XMLNode& node, bool must_exist)
        : Source (s, node)
index 63530e2f01774b48835ff02d26e84652df270e4f..2eb09ae192c1aa4c933083500101e8e51f874b49 100644 (file)
@@ -540,21 +540,21 @@ AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer,
                /* see if some part of this read is within the fade out */
 
                /* .................        >|            REGION
-                                            _length
-
-                                 {           }            FADE
-                                            fade_out_length
-                                 ^
-                                 _length - fade_out_length
-                        |--------------|
-                        ^internal_offset
-                                       ^internal_offset + to_read
-
-                                      we need the intersection of [internal_offset,internal_offset+to_read] with
-                                      [_length - fade_out_length, _length]
-
-               */
-
+                *                           _length
+                *
+                *               {           }            FADE
+                *                           fade_out_length
+                *               ^
+                *               _length - fade_out_length
+                *
+                *      |--------------|
+                *      ^internal_offset
+                *                     ^internal_offset + to_read
+                *
+                *                     we need the intersection of [internal_offset,internal_offset+to_read] with
+                *                     [_length - fade_out_length, _length]
+                *
+                */
 
                fade_interval_start = max (internal_offset, _length - framecnt_t (_fade_out->back()->when));
                framecnt_t fade_interval_end = min(internal_offset + to_read, _length.val());
@@ -987,15 +987,15 @@ AudioRegion::set_fade_in (FadeShape shape, framecnt_t len)
        boost::shared_ptr<Evoral::ControlList> c2 (new Evoral::ControlList (FadeInAutomation));
        boost::shared_ptr<Evoral::ControlList> c3 (new Evoral::ControlList (FadeInAutomation));
 
-       const int num_steps = min ((framecnt_t) 16, len);
-
        _fade_in->freeze ();
        _fade_in->clear ();
        _inverse_fade_in->clear ();
 
+       const int num_steps = 32;
+
        switch (shape) {
        case FadeLinear:
-               _fade_in->fast_simple_add (0.0, 0.0);
+               _fade_in->fast_simple_add (0.0, VERY_SMALL_SIGNAL);
                _fade_in->fast_simple_add (len, 1.0);
                reverse_curve (_inverse_fade_in.val(), _fade_in.val());
                break;
@@ -1008,8 +1008,8 @@ AudioRegion::set_fade_in (FadeShape shape, framecnt_t len)
                break;
 
        case FadeSlow:
-               generate_db_fade (c1, len, num_steps/2, -1);  // start off with a slow fade
-               generate_db_fade (c2, len, num_steps/2, -80); // end with a fast fade
+               generate_db_fade (c1, len, num_steps, -1);  // start off with a slow fade
+               generate_db_fade (c2, len, num_steps, -80); // end with a fast fade
                merge_curves (_fade_in.val(), c1, c2);
                reverse_curve (c3, _fade_in.val());
                _fade_in->copy_events (*c3);
@@ -1017,9 +1017,10 @@ AudioRegion::set_fade_in (FadeShape shape, framecnt_t len)
                break;
 
        case FadeConstantPower:
-               for (int i = 0; i < num_steps; ++i) {
-                       float dist = (float) i / (num_steps+1.0);
-                       _fade_in->fast_simple_add (len*dist, sin (dist*M_PI/2));
+               _fade_in->fast_simple_add (0.0, VERY_SMALL_SIGNAL);
+               for (int i = 1; i < num_steps; ++i) {
+                       const float dist = i / (num_steps + 1.f);
+                       _fade_in->fast_simple_add (len * dist, sin (dist * M_PI / 2.0));
                }
                _fade_in->fast_simple_add (len, 1.0);
                reverse_curve (_inverse_fade_in.val(), _fade_in.val());
@@ -1028,15 +1029,12 @@ AudioRegion::set_fade_in (FadeShape shape, framecnt_t len)
        case FadeSymmetric:
                //start with a nearly linear cuve
                _fade_in->fast_simple_add (0, 1);
-               _fade_in->fast_simple_add (0.5*len, 0.6);
+               _fade_in->fast_simple_add (0.5 * len, 0.6);
                //now generate a fade-out curve by successively applying a gain drop
-               const float breakpoint = 0.7;  //linear for first 70%
-               for (int i = 2; i < num_steps; i++) {
-                       float coeff = (1.0-breakpoint);
-                       for (int j = 0; j < i; j++) {
-                               coeff *= 0.5;  //6dB drop per step
-                       }
-                       _fade_in->fast_simple_add (len* (breakpoint+((1.0-breakpoint)*(double)i/(double)num_steps)), coeff);
+               const double breakpoint = 0.7;  //linear for first 70%
+               for (int i = 2; i < 9; ++i) {
+                       const float coeff = (1.f - breakpoint) * powf (0.5, i);
+                       _fade_in->fast_simple_add (len * (breakpoint + ((1.0 - breakpoint) * (double)i / 9.0)), coeff);
                }
                _fade_in->fast_simple_add (len, VERY_SMALL_SIGNAL);
                reverse_curve (c3, _fade_in.val());
@@ -1045,6 +1043,9 @@ AudioRegion::set_fade_in (FadeShape shape, framecnt_t len)
                break;
        }
 
+       _fade_in->set_interpolation(Evoral::ControlList::Curved);
+       _inverse_fade_in->set_interpolation(Evoral::ControlList::Curved);
+
        _default_fade_in = false;
        _fade_in->thaw ();
        send_change (PropertyChange (Properties::fade_in));
@@ -1071,6 +1072,8 @@ AudioRegion::set_fade_out (FadeShape shape, framecnt_t len)
        _fade_out->clear ();
        _inverse_fade_out->clear ();
 
+       const int num_steps = 32;
+
        switch (shape) {
        case FadeLinear:
                _fade_out->fast_simple_add (0.0, 1.0);
@@ -1079,13 +1082,13 @@ AudioRegion::set_fade_out (FadeShape shape, framecnt_t len)
                break;
                
        case FadeFast: 
-               generate_db_fade (_fade_out.val(), len, 10, -60);
+               generate_db_fade (_fade_out.val(), len, num_steps, -60);
                generate_inverse_power_curve (_inverse_fade_out.val(), _fade_out.val());
                break;
                
        case FadeSlow: 
-               generate_db_fade (c1, len, 10, -1);  //start off with a slow fade
-               generate_db_fade (c2, len, 10, -80);  //end with a fast fade
+               generate_db_fade (c1, len, num_steps, -1);  //start off with a slow fade
+               generate_db_fade (c2, len, num_steps, -80);  //end with a fast fade
                merge_curves (_fade_out.val(), c1, c2);
                generate_inverse_power_curve (_inverse_fade_out.val(), _fade_out.val());
                break;
@@ -1094,9 +1097,9 @@ AudioRegion::set_fade_out (FadeShape shape, framecnt_t len)
                //constant-power fades use a sin/cos relationship
                //the cutoff is abrupt but it has the benefit of being symmetrical
                _fade_out->fast_simple_add (0.0, 1.0);
-               for (int i = 1; i < 9; i++ ) {
-                       float dist = (float)i/10.0;
-                       _fade_out->fast_simple_add ((len * dist), cos(dist*M_PI/2));
+               for (int i = 1; i < num_steps; ++i) {
+                       const float dist = i / (num_steps + 1.f);
+                       _fade_out->fast_simple_add (len * dist, cos (dist * M_PI / 2.0));
                }
                _fade_out->fast_simple_add (len, VERY_SMALL_SIGNAL);
                reverse_curve (_inverse_fade_out.val(), _fade_out.val());
@@ -1105,23 +1108,21 @@ AudioRegion::set_fade_out (FadeShape shape, framecnt_t len)
        case FadeSymmetric:
                //start with a nearly linear cuve
                _fade_out->fast_simple_add (0, 1);
-               _fade_out->fast_simple_add (0.5*len, 0.6);
-
+               _fade_out->fast_simple_add (0.5 * len, 0.6);
                //now generate a fade-out curve by successively applying a gain drop
-               const float breakpoint = 0.7;  //linear for first 70%
-               const int num_steps = 9;
-               for (int i = 2; i < num_steps; i++) {
-                       float coeff = (1.0-breakpoint);
-                       for (int j = 0; j < i; j++) {
-                               coeff *= 0.5;  //6dB drop per step
-                       }
-                       _fade_out->fast_simple_add (len* (breakpoint+((1.0-breakpoint)*(double)i/(double)num_steps)), coeff);
+               const double breakpoint = 0.7;  //linear for first 70%
+               for (int i = 2; i < 9; ++i) {
+                       const float coeff = (1.f - breakpoint) * powf (0.5, i);
+                       _fade_out->fast_simple_add (len * (breakpoint + ((1.0 - breakpoint) * (double)i / 9.0)), coeff);
                }
                _fade_out->fast_simple_add (len, VERY_SMALL_SIGNAL);
                reverse_curve (_inverse_fade_out.val(), _fade_out.val());
                break;
        }
 
+       _fade_out->set_interpolation(Evoral::ControlList::Curved);
+       _inverse_fade_out->set_interpolation(Evoral::ControlList::Curved);
+
        _default_fade_out = false;
        _fade_out->thaw ();
        send_change (PropertyChange (Properties::fade_out));
index 83df92488b02087b3adf6357992ffe57da0224de..32e69af7aeed9466ce606976004757415d091d90 100644 (file)
@@ -346,8 +346,6 @@ AutomationList::deserialize_events (const XMLNode& node)
                fast_simple_add (x, y);
        }
 
-       thin ();
-
        if (!ok) {
                clear ();
                error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
@@ -410,9 +408,7 @@ AutomationList::set_state (const XMLNode& node, int version)
                        fast_simple_add (x, y);
                }
 
-               thin ();
-
-                thaw ();
+               thaw ();
 
                return 0;
        }
index c8819e41cbe10e7e111a9fdfda7acd0bd814eeb6..c221837af88ddef013ca597ecf1ec8a272f348e0 100644 (file)
@@ -75,11 +75,11 @@ BufferManager::put_thread_buffers (ThreadBuffers* tbp)
 }
 
 void
-BufferManager::ensure_buffers (ChanCount howmany)
+BufferManager::ensure_buffers (ChanCount howmany, size_t custom)
 {
         /* this is protected by the audioengine's process lock: we do not  */
 
        for (ThreadBufferList::iterator i = thread_buffers_list->begin(); i != thread_buffers_list->end(); ++i) {
-               (*i)->ensure_buffers (howmany);
+               (*i)->ensure_buffers (howmany, custom);
        }
 }
index 49b3d0776161c6ecfe2b99473c194ad151905bd5..fdac48e731124aa8620be3913c68bb239c8c6d6e 100644 (file)
@@ -268,14 +268,14 @@ ControlProtocolManager::discover_control_protocols ()
        Glib::PatternSpec so_extension_pattern("*.so");
        Glib::PatternSpec dylib_extension_pattern("*.dylib");
 
-       find_matching_files_in_search_path (control_protocol_search_path (),
-                                           dll_extension_pattern, cp_modules);
+       find_files_matching_pattern (cp_modules, control_protocol_search_path (),
+                                    dll_extension_pattern);
 
-       find_matching_files_in_search_path (control_protocol_search_path (),
-                                           so_extension_pattern, cp_modules);
+       find_files_matching_pattern (cp_modules, control_protocol_search_path (),
+                                    so_extension_pattern);
 
-       find_matching_files_in_search_path (control_protocol_search_path (),
-                                           dylib_extension_pattern, cp_modules);
+       find_files_matching_pattern (cp_modules, control_protocol_search_path (),
+                                    dylib_extension_pattern);
 
        DEBUG_TRACE (DEBUG::ControlProtocols, 
                     string_compose (_("looking for control protocols in %1\n"), control_protocol_search_path().to_string()));
index 39ab5b82c76e75b1351030ece79bee8e99fdae13..69f2663aa83f2e377e6d07c94a3ad4d32350395b 100644 (file)
@@ -31,6 +31,7 @@ uint64_t PBD::DEBUG::MidiDiskstreamIO = PBD::new_debug_bit ("mididiskstreamio");
 uint64_t PBD::DEBUG::SnapBBT = PBD::new_debug_bit ("snapbbt");
 uint64_t PBD::DEBUG::Configuration = PBD::new_debug_bit ("configuration");
 uint64_t PBD::DEBUG::Latency = PBD::new_debug_bit ("latency");
+uint64_t PBD::DEBUG::LatencyCompensation = PBD::new_debug_bit ("latencycompensation");
 uint64_t PBD::DEBUG::Peaks = PBD::new_debug_bit ("peaks");
 uint64_t PBD::DEBUG::Processors = PBD::new_debug_bit ("processors");
 uint64_t PBD::DEBUG::ProcessThreads = PBD::new_debug_bit ("processthreads");
@@ -63,5 +64,6 @@ uint64_t PBD::DEBUG::Automation = PBD::new_debug_bit ("automation");
 uint64_t PBD::DEBUG::WiimoteControl = PBD::new_debug_bit ("wiimotecontrol");
 uint64_t PBD::DEBUG::Ports = PBD::new_debug_bit ("Ports");
 uint64_t PBD::DEBUG::AudioEngine = PBD::new_debug_bit ("AudioEngine");
+uint64_t PBD::DEBUG::Soundcloud = PBD::new_debug_bit ("Soundcloud");
 
 
diff --git a/libs/ardour/delayline.cc b/libs/ardour/delayline.cc
new file mode 100644 (file)
index 0000000..0bd7793
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+    Copyright (C) 2006, 2013 Paul Davis
+    Copyright (C) 2013, 2014 Robin Gareus <robin@gareus.org>
+
+    This program is free software; you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by the Free
+    Software Foundation; either version 2 of the License, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <assert.h>
+#include <cmath>
+
+#include "pbd/compose.h"
+
+#include "ardour/debug.h"
+#include "ardour/audio_buffer.h"
+#include "ardour/midi_buffer.h"
+#include "ardour/buffer_set.h"
+#include "ardour/delayline.h"
+
+using namespace std;
+using namespace PBD;
+using namespace ARDOUR;
+
+DelayLine::DelayLine (Session& s, const std::string& name)
+    : Processor (s, string_compose ("latency-compensation-%1", name))
+               , _delay(0)
+               , _pending_delay(0)
+               , _bsiz(0)
+               , _pending_bsiz(0)
+               , _roff(0)
+               , _woff(0)
+               , _pending_flush(false)
+{
+}
+
+DelayLine::~DelayLine ()
+{
+}
+
+#define FADE_LEN (16)
+void
+DelayLine::run (BufferSet& bufs, framepos_t /* start_frame */, framepos_t /* end_frame */, pframes_t nsamples, bool)
+{
+       const uint32_t chn = _configured_output.n_audio();
+       pframes_t p0 = 0;
+       uint32_t c;
+
+       const frameoffset_t pending_delay = _pending_delay;
+       const frameoffset_t delay_diff = _delay - pending_delay;
+       const bool pending_flush = _pending_flush;
+       _pending_flush = false;
+
+       /* run() and set_delay() may be called in parallel by
+        * different threads.
+        * if a larger buffer is needed, it is allocated in
+        * set_delay(), here it is just swap'ed in place
+        */
+       if (_pending_bsiz) {
+               assert(_pending_bsiz >= _bsiz);
+
+               const size_t boff = _pending_bsiz - _bsiz;
+               if (_bsiz > 0) {
+                       /* write offset is retained. copy existing data to new buffer */
+                       frameoffset_t wl = _bsiz - _woff;
+                       memcpy(_pending_buf.get(), _buf.get(), sizeof(Sample) * _woff * chn);
+                       memcpy(_pending_buf.get() + (_pending_bsiz - wl) * chn, _buf.get() + _woff * chn, sizeof(Sample) * wl * chn);
+
+                       /* new buffer is all zero by default, fade into the existing data copied above */
+                       frameoffset_t wo = _pending_bsiz - wl;
+                       for (pframes_t pos = 0; pos < FADE_LEN; ++pos) {
+                               const gain_t gain = (gain_t)pos / (gain_t)FADE_LEN;
+                               for (c = 0; c < _configured_input.n_audio(); ++c) {
+                                       _pending_buf.get()[ wo * chn + c ] *= gain;
+                                       wo = (wo + 1) % (_pending_bsiz + 1);
+                               }
+                       }
+
+                       /* read-pointer will be moved and may up anywhere..
+                        * copy current data for smooth fade-out below
+                        */
+                       frameoffset_t roold = _roff;
+                       frameoffset_t ro = _roff;
+                       if (ro > _woff) {
+                               ro += boff;
+                       }
+                       ro += delay_diff;
+                       if (ro < 0) {
+                               ro -= (_pending_bsiz +1) * floor(ro / (float)(_pending_bsiz +1));
+                       }
+                       ro = ro % (_pending_bsiz + 1);
+                       for (pframes_t pos = 0; pos < FADE_LEN; ++pos) {
+                               for (c = 0; c < _configured_input.n_audio(); ++c) {
+                                       _pending_buf.get()[ ro * chn + c ] = _buf.get()[ roold * chn + c ];
+                                       ro = (ro + 1) % (_pending_bsiz + 1);
+                                       roold = (roold + 1) % (_bsiz + 1);
+                               }
+                       }
+               }
+
+               if (_roff > _woff) {
+                       _roff += boff;
+               }
+
+               _buf = _pending_buf;
+               _bsiz = _pending_bsiz;
+               _pending_bsiz = 0;
+               _pending_buf.reset();
+       }
+
+       /* there may be no buffer when delay == 0.
+        * we also need to check audio-channels in case all audio-channels
+        * were removed in which case no new buffer was allocated. */
+       Sample *buf = _buf.get();
+       if (buf && _configured_output.n_audio() > 0) {
+
+               assert (_bsiz >= pending_delay);
+               const framecnt_t rbs = _bsiz + 1;
+
+               if (pending_delay != _delay || pending_flush) {
+                       const pframes_t fade_len = (nsamples >= FADE_LEN) ? FADE_LEN : nsamples / 2;
+
+                       DEBUG_TRACE (DEBUG::LatencyCompensation,
+                                       string_compose ("Old %1 delay: %2 bufsiz: %3 offset-diff: %4 write-offset: %5 read-offset: %6\n",
+                                               name(), _delay, _bsiz, ((_woff - _roff + rbs) % rbs), _woff, _roff));
+
+                       // fade out at old position
+                       c = 0;
+                       for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++c) {
+                               Sample * const data = i->data();
+                               for (pframes_t pos = 0; pos < fade_len; ++pos) {
+                                       const gain_t gain = (gain_t)(fade_len - pos) / (gain_t)fade_len;
+                                       buf[ _woff * chn + c ] = data[ pos ];
+                                       data[ pos ] = buf[ _roff * chn + c ] * gain;
+                                       _roff = (_roff + 1) % rbs;
+                                       _woff = (_woff + 1) % rbs;
+                               }
+                       }
+
+                       if (pending_flush) {
+                               DEBUG_TRACE (DEBUG::LatencyCompensation,
+                                               string_compose ("Flush buffer: %1\n", name()));
+                               memset(buf, 0, _configured_output.n_audio() * rbs * sizeof (Sample));
+                       }
+
+                       // adjust read pointer
+                       _roff += _delay - pending_delay;
+
+                       if (_roff < 0) {
+                               _roff -= rbs * floor(_roff / (float)rbs);
+                       }
+                       _roff = _roff % rbs;
+
+                       // fade in at new position
+                       c = 0;
+                       for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++c) {
+                               Sample * const data = i->data();
+                               for (pframes_t pos = fade_len; pos < 2 * fade_len; ++pos) {
+                                       const gain_t gain = (gain_t)(pos - fade_len) / (gain_t)fade_len;
+                                       buf[ _woff * chn + c ] = data[ pos ];
+                                       data[ pos ] = buf[ _roff * chn + c ] * gain;
+                                       _roff = (_roff + 1) % rbs;
+                                       _woff = (_woff + 1) % rbs;
+                               }
+                       }
+                       p0  = 2 * fade_len;
+
+                       _delay = pending_delay;
+
+                       DEBUG_TRACE (DEBUG::LatencyCompensation,
+                                       string_compose ("New %1 delay: %2 bufsiz: %3 offset-diff: %4 write-offset: %5 read-offset: %6\n",
+                                               name(), _delay, _bsiz, ((_woff - _roff + rbs) % rbs), _woff, _roff));
+               }
+
+               assert(_delay == ((_woff - _roff + rbs) % rbs));
+
+               c = 0;
+               for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++c) {
+                       Sample * const data = i->data();
+                       for (pframes_t pos = p0; pos < nsamples; ++pos) {
+                               buf[ _woff * chn + c ] = data[ pos ];
+                               data[ pos ] = buf[ _roff * chn + c ];
+                               _roff = (_roff + 1) % rbs;
+                               _woff = (_woff + 1) % rbs;
+                       }
+               }
+       }
+
+       if (_midi_buf.get()) {
+               _delay = pending_delay;
+
+               for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
+                       if (i != bufs.midi_begin()) { break; } // XXX only one buffer for now
+
+                       MidiBuffer* dly = _midi_buf.get();
+                       MidiBuffer& mb (*i);
+                       if (pending_flush) {
+                               dly->silence(nsamples);
+                       }
+
+                       // If the delay time changes, iterate over all events in the dly-buffer
+                       // and adjust the time in-place. <= 0 becomes 0.
+                       //
+                       // iterate over all events in dly-buffer and subtract one cycle
+                       // (nsamples) from the timestamp, bringing them closer to de-queue.
+                       for (MidiBuffer::iterator m = dly->begin(); m != dly->end(); ++m) {
+                               MidiBuffer::TimeType *t = m.timeptr();
+                               if (*t > nsamples + delay_diff) {
+                                       *t -= nsamples + delay_diff;
+                               } else {
+                                       *t = 0;
+                               }
+                       }
+
+                       if (_delay != 0) {
+                               // delay events in current-buffer, in place.
+                               for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
+                                       MidiBuffer::TimeType *t = m.timeptr();
+                                       *t += _delay;
+                               }
+                       }
+
+                       // move events from dly-buffer into current-buffer until nsamples
+                       // and remove them from the dly-buffer
+                       for (MidiBuffer::iterator m = dly->begin(); m != dly->end();) {
+                               const Evoral::MIDIEvent<MidiBuffer::TimeType> ev (*m, false);
+                               if (ev.time() >= nsamples) {
+                                       break;
+                               }
+                               mb.insert_event(ev);
+                               m = dly->erase(m);
+                       }
+
+                       /* For now, this is only relevant if there is there's a positive delay.
+                        * In the future this could also be used to delay 'too early' events
+                        * (ie '_global_port_buffer_offset + _port_buffer_offset' - midi_port.cc)
+                        */
+                       if (_delay != 0) {
+                               // move events after nsamples from current-buffer into dly-buffer
+                               // and trim current-buffer after nsamples
+                               for (MidiBuffer::iterator m = mb.begin(); m != mb.end();) {
+                                       const Evoral::MIDIEvent<MidiBuffer::TimeType> ev (*m, false);
+                                       if (ev.time() < nsamples) {
+                                               ++m;
+                                               continue;
+                                       }
+                                       dly->insert_event(ev);
+                                       m = mb.erase(m);
+                               }
+                       }
+               }
+       }
+
+       _delay = pending_delay;
+}
+
+void
+DelayLine::set_delay(framecnt_t signal_delay)
+{
+       if (signal_delay < 0) {
+               signal_delay = 0;
+               cerr << "WARNING: latency compensation is not possible.\n";
+       }
+
+       const framecnt_t rbs = signal_delay + 1;
+
+       DEBUG_TRACE (DEBUG::LatencyCompensation,
+                       string_compose ("%1 set_delay to %2 samples for %3 channels\n",
+                               name(), signal_delay, _configured_output.n_audio()));
+
+       if (signal_delay <= _bsiz) {
+               _pending_delay = signal_delay;
+               return;
+       }
+
+       if (_pending_bsiz) {
+               if (_pending_bsiz < signal_delay) {
+                       cerr << "LatComp: buffer resize in progress. "<< name() << "pending: "<< _pending_bsiz <<" want: " << signal_delay <<"\n"; // XXX
+               } else {
+                       _pending_delay = signal_delay;
+               }
+               return;
+       }
+
+       if (_configured_output.n_audio() > 0 ) {
+               _pending_buf.reset(new Sample[_configured_output.n_audio() * rbs]);
+               memset(_pending_buf.get(), 0, _configured_output.n_audio() * rbs * sizeof (Sample));
+               _pending_bsiz = signal_delay;
+       } else {
+               _pending_buf.reset();
+               _pending_bsiz = 0;
+       }
+
+       _pending_delay = signal_delay;
+
+       DEBUG_TRACE (DEBUG::LatencyCompensation,
+                       string_compose ("allocated buffer for %1 of size %2\n",
+                               name(), signal_delay));
+}
+
+bool
+DelayLine::can_support_io_configuration (const ChanCount& in, ChanCount& out)
+{
+       out = in;
+       return true;
+}
+
+bool
+DelayLine::configure_io (ChanCount in, ChanCount out)
+{
+       if (out != in) { // always 1:1
+               return false;
+       }
+
+       // TODO realloc buffers if channel count changes..
+       // TODO support multiple midi buffers
+
+       DEBUG_TRACE (DEBUG::LatencyCompensation,
+                       string_compose ("configure IO: %1 Ain: %2 Aout: %3 Min: %4 Mout: %5\n",
+                               name(), in.n_audio(), out.n_audio(), in.n_midi(), out.n_midi()));
+
+       if (in.n_midi() > 0 && !_midi_buf) {
+               _midi_buf.reset(new MidiBuffer(16384));
+       }
+
+       return Processor::configure_io (in, out);
+}
+
+void
+DelayLine::flush()
+{
+       _pending_flush = true;
+}
+
+XMLNode&
+DelayLine::state (bool full_state)
+{
+       XMLNode& node (Processor::state (full_state));
+       node.add_property("type", "delay");
+       return node;
+}
index 8c12d44e5107518ec495ffe30d9b00b1ffdb4102..30f7ef63fa2761245543d347d1f7a45ee57eedac 100644 (file)
@@ -249,6 +249,7 @@ Delivery::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pf
           processing pathway that wants to use this->output_buffers() for some reason.
        */
 
+       // TODO delayline -- latency-compensation
        output_buffers().get_backend_port_addresses (ports, nframes);
 
        // this Delivery processor is not a derived type, and thus we assume
@@ -556,7 +557,7 @@ Delivery::set_name (const std::string& name)
 {
        bool ret = IOProcessor::set_name (name);
 
-       if (ret) {
+       if (ret && _panshell) {
                ret = _panshell->set_name (name);
        }
 
index e046d5a830beda849f45ab7a77208f8de90a18ba..0e05ffabf488e588da9b123764002a21a4a55346 100644 (file)
@@ -280,17 +280,17 @@ Diskstream::set_align_choice (AlignChoice a, bool force)
        if ((a != _alignment_choice) || force) {
                _alignment_choice = a;
 
-                switch (_alignment_choice) {
-                case Automatic:
-                        set_align_style_from_io ();
-                        break;
-                case UseExistingMaterial:
-                        set_align_style (ExistingMaterial);
-                        break;
-                case UseCaptureTime:
-                        set_align_style (CaptureTime);
-                        break;
-                }
+               switch (_alignment_choice) {
+                       case Automatic:
+                               set_align_style_from_io ();
+                               break;
+                       case UseExistingMaterial:
+                               set_align_style (ExistingMaterial);
+                               break;
+                       case UseCaptureTime:
+                               set_align_style (CaptureTime);
+                               break;
+               }
        }
 }
 
@@ -437,7 +437,13 @@ Diskstream::set_name (const string& str)
                playlist()->set_name (str);
                SessionObject::set_name(str);
        }
-        return true;
+       return true;
+}
+
+bool
+Diskstream::set_write_source_name (const std::string& str) {
+       _write_source_name = str;
+       return true;
 }
 
 XMLNode&
@@ -593,11 +599,11 @@ Diskstream::check_record_status (framepos_t transport_frame, bool can_record)
        const int transport_rolling = 0x4;
        const int track_rec_enabled = 0x2;
        const int global_rec_enabled = 0x1;
-        const int fully_rec_enabled = (transport_rolling|track_rec_enabled|global_rec_enabled);
+       const int fully_rec_enabled = (transport_rolling|track_rec_enabled|global_rec_enabled);
 
        /* merge together the 3 factors that affect record status, and compute
-          what has changed.
-       */
+        * what has changed.
+        */
 
        rolling = _session.transport_speed() != 0.0f;
        possibly_recording = (rolling << 2) | ((int)record_enabled() << 1) | (int)can_record;
@@ -607,13 +613,13 @@ Diskstream::check_record_status (framepos_t transport_frame, bool can_record)
                return;
        }
 
-        framecnt_t existing_material_offset = _session.worst_playback_latency();
+       framecnt_t existing_material_offset = _session.worst_playback_latency();
 
-        if (possibly_recording == fully_rec_enabled) {
+       if (possibly_recording == fully_rec_enabled) {
 
-                if (last_possibly_recording == fully_rec_enabled) {
-                        return;
-                }
+               if (last_possibly_recording == fully_rec_enabled) {
+                       return;
+               }
 
                capture_start_frame = _session.transport_frame();
                first_recordable_frame = capture_start_frame + _capture_offset;
@@ -636,32 +642,32 @@ Diskstream::check_record_status (framepos_t transport_frame, bool can_record)
                                                                               first_recordable_frame));
                 }
 
-                prepare_record_status (capture_start_frame);
+               prepare_record_status (capture_start_frame);
 
-        } else {
+       } else {
 
-                if (last_possibly_recording == fully_rec_enabled) {
+               if (last_possibly_recording == fully_rec_enabled) {
 
-                        /* we were recording last time */
+                       /* we were recording last time */
 
-                        if (change & transport_rolling) {
+                       if (change & transport_rolling) {
 
-                                /* transport-change (stopped rolling): last_recordable_frame was set in ::prepare_to_stop(). We
-                                   had to set it there because we likely rolled past the stopping point to declick out,
-                                   and then backed up.
-                                 */
+                               /* transport-change (stopped rolling): last_recordable_frame was set in ::prepare_to_stop(). We
+                                * had to set it there because we likely rolled past the stopping point to declick out,
+                                * and then backed up.
+                                */
 
-                        } else {
-                                /* punch out */
+                       } else {
+                               /* punch out */
 
-                                last_recordable_frame = _session.transport_frame() + _capture_offset;
+                               last_recordable_frame = _session.transport_frame() + _capture_offset;
 
-                                if (_alignment_style == ExistingMaterial) {
-                                        last_recordable_frame += existing_material_offset;
-                                }
-                        }
-                }
-        }
+                               if (_alignment_style == ExistingMaterial) {
+                                       last_recordable_frame += existing_material_offset;
+                               }
+                       }
+               }
+       }
 
        last_possibly_recording = possibly_recording;
 }
@@ -738,4 +744,3 @@ Diskstream::disengage_record_enable ()
 {
        g_atomic_int_set (&_record_enabled, 0);
 }
-
index 3f5ce75eb3a5ea06f32ca2e0cc9deb0fa6bd1bd3..6f4158d5de6d42c0df87d9d7b2c2ed8828b3ef69 100644 (file)
@@ -74,8 +74,6 @@ setup_enum_writer ()
        AFLPosition _AFLPosition;
        RemoteModel _RemoteModel;
        DenormalModel _DenormalModel;
-       CrossfadeModel _CrossfadeModel;
-       CrossfadeChoice _CrossfadeChoice;
        InsertMergePolicy _InsertMergePolicy;
        ListenPosition _ListenPosition;
        SampleFormat _SampleFormat;
@@ -290,15 +288,6 @@ setup_enum_writer ()
        */
        enum_writer.add_to_hack_table ("EditorOrdered", "MixerOrdered");
 
-       REGISTER_ENUM (FullCrossfade);
-       REGISTER_ENUM (ShortCrossfade);
-       REGISTER (_CrossfadeModel);
-
-       REGISTER_ENUM (RegionFades);
-       REGISTER_ENUM (ConstantPowerMinus3dB);
-       REGISTER_ENUM (ConstantPowerMinus6dB);
-       REGISTER (_CrossfadeChoice);
-
         REGISTER_ENUM (InsertMergeReject);
         REGISTER_ENUM (InsertMergeRelax);
         REGISTER_ENUM (InsertMergeReplace);
@@ -795,34 +784,6 @@ std::ostream& operator<<(std::ostream& o, const InsertMergePolicy& var)
        return o << s;
 }
 
-std::istream& operator>>(std::istream& o, CrossfadeModel& var)
-{
-       std::string s;
-       o >> s;
-       var = (CrossfadeModel) string_2_enum (s, var);
-       return o;
-}
-
-std::ostream& operator<<(std::ostream& o, const CrossfadeModel& var)
-{
-       std::string s = enum_2_string (var);
-       return o << s;
-}
-
-std::istream& operator>>(std::istream& o, CrossfadeChoice& var)
-{
-       std::string s;
-       o >> s;
-       var = (CrossfadeChoice) string_2_enum (s, var);
-       return o;
-}
-
-std::ostream& operator<<(std::ostream& o, const CrossfadeChoice& var)
-{
-       std::string s = enum_2_string (var);
-       return o << s;
-}
-
 std::istream& operator>>(std::istream& o, SyncSource& var)
 {
        std::string s;
index fc71d8536f049cfe413642bb1efd8b9d4b810506..93621717381ca19c2c4ea4a7d95761aa47dd75d7 100644 (file)
@@ -187,11 +187,12 @@ RegionExportChannelFactory::update_buffers (framecnt_t frames)
                assert (mixdown_buffer && gain_buffer);
                for (size_t channel = 0; channel < n_channels; ++channel) {
                        memset (mixdown_buffer.get(), 0, sizeof (Sample) * frames);
+                       buffers.get_audio (channel).silence(frames);
                        region.read_at (buffers.get_audio (channel).data(), mixdown_buffer.get(), gain_buffer.get(), position, frames, channel);
                }
                break;
        case Processed:
-               track.export_stuff (buffers, position, frames, track.main_outs(), true, true);
+               track.export_stuff (buffers, position, frames, track.main_outs(), true, true, false);
                break;
        default:
                throw ExportFailed ("Unhandled type in ExportChannelFactory::update_buffers");
index 890623c114b0f5a562aa28705f3e225b58dd94ea..04cfa76677720d8028891000f188aa226565f1a0 100644 (file)
@@ -293,6 +293,14 @@ ExportFormatManager::select_with_toc (bool value)
        check_for_description_change ();
 }
 
+
+void
+ExportFormatManager::set_command (std::string command)
+{
+       current_selection->set_command (command);
+       check_for_description_change ();
+}
+
 void
 ExportFormatManager::select_trim_beginning (bool value)
 {
index b139faeee2997d8401a9e4de77dfe2547254ff74..d8a45ba533e47afe81cb8a2b2332347a4d2ce7d2 100644 (file)
@@ -170,6 +170,8 @@ ExportFormatSpecification::ExportFormatSpecification (Session & s)
        , _normalize_target (1.0)
        , _with_toc (false)
        , _with_cue (false)
+       , _soundcloud_upload (false)
+       , _command ("")
 {
        format_ids.insert (F_None);
        endiannesses.insert (E_FileDefault);
@@ -244,6 +246,7 @@ ExportFormatSpecification::get_state ()
        root->add_property ("id", _id.to_s());
        root->add_property ("with-cue", _with_cue ? "true" : "false");
        root->add_property ("with-toc", _with_toc ? "true" : "false");
+       root->add_property ("command", _command);
 
        node = root->add_child ("Encoding");
        node->add_property ("id", enum_2_string (format_id()));
@@ -321,6 +324,13 @@ ExportFormatSpecification::set_state (const XMLNode & root)
                _with_toc = false;
        }
        
+       
+       if ((prop = root.property ("command"))) {
+               _command = prop->value();
+       } else {
+               _command = "";
+       }
+
        /* Encoding and SRC */
 
        if ((child = root.child ("Encoding"))) {
@@ -590,6 +600,10 @@ ExportFormatSpecification::description (bool include_name)
                components.push_back ("CUE");
        }
 
+       if (!_command.empty()) {
+               components.push_back ("+");
+       }
+
        string desc;
        if (include_name) {
                desc = _name + ": ";
index 20abc80de1995866ff8befb89228d04bd1e83845..e706522aa9b6678e5038e354f81fa3301a081a65 100644 (file)
 #include "pbd/convert.h"
 
 #include "ardour/audiofile_tagger.h"
+#include "ardour/debug.h"
 #include "ardour/export_graph_builder.h"
 #include "ardour/export_timespan.h"
 #include "ardour/export_channel_configuration.h"
 #include "ardour/export_status.h"
 #include "ardour/export_format_specification.h"
 #include "ardour/export_filename.h"
+#include "ardour/soundcloud_upload.h"
+#include "ardour/system_exec.h"
+#include "pbd/openuri.h"
+#include "pbd/basename.h"
 #include "ardour/session_metadata.h"
 
 #include "i18n.h"
@@ -277,6 +282,13 @@ ExportHandler::process_normalize ()
        return 0;
 }
 
+void
+ExportHandler::command_output(std::string output, size_t size)
+{
+       std::cerr << "command: " << size << ", " << output << std::endl;
+       info << output << endmsg;
+}
+
 void
 ExportHandler::finish_timespan ()
 {
@@ -297,13 +309,69 @@ ExportHandler::finish_timespan ()
                        AudiofileTagger::tag_file(filename, *SessionMetadata::Metadata());
                }
 
+               if (!fmt->command().empty()) {
+
+#if 0                  // would be nicer with C++11 initialiser...
+                       std::map<char, std::string> subs {
+                               { 'f', filename },
+                               { 'd', Glib::path_get_dirname(filename)  + G_DIR_SEPARATOR },
+                               { 'b', PBD::basename_nosuffix(filename) },
+                               ...
+                       };
+#endif
+
+                       PBD::ScopedConnection command_connection;
+                       std::map<char, std::string> subs;
+                       subs.insert (std::pair<char, std::string> ('f', filename));
+                       subs.insert (std::pair<char, std::string> ('d', Glib::path_get_dirname (filename) + G_DIR_SEPARATOR));
+                       subs.insert (std::pair<char, std::string> ('b', PBD::basename_nosuffix (filename)));
+                       subs.insert (std::pair<char, std::string> ('s', session.path ()));
+                       subs.insert (std::pair<char, std::string> ('n', session.name ()));
+
+                       ARDOUR::SystemExec *se = new ARDOUR::SystemExec(fmt->command(), subs);
+                       se->ReadStdout.connect_same_thread(command_connection, boost::bind(&ExportHandler::command_output, this, _1, _2));
+                       if (se->start (2) == 0) {
+                               // successfully started
+                               while (se->is_running ()) {
+                                       // wait for system exec to terminate
+                                       Glib::usleep (1000);
+                               }
+                       }
+                       delete (se);
+               }
+
+               if (fmt->soundcloud_upload()) {
+                       SoundcloudUploader *soundcloud_uploader = new SoundcloudUploader;
+                       std::string token = soundcloud_uploader->Get_Auth_Token(soundcloud_username, soundcloud_password);
+                       DEBUG_TRACE (DEBUG::Soundcloud, string_compose(
+                                               "uploading %1 - username=%2, password=%3, token=%4",
+                                               filename, soundcloud_username, soundcloud_password, token) );
+                       std::string path = soundcloud_uploader->Upload (
+                                       filename,
+                                       PBD::basename_nosuffix(filename), // title
+                                       token,
+                                       soundcloud_make_public,
+                                       soundcloud_downloadable,
+                                       this);
+
+                       if (path.length() != 0) {
+                               info << string_compose ( _("File %1 uploaded to %2"), filename, path) << endmsg;
+                               if (soundcloud_open_page) {
+                                       DEBUG_TRACE (DEBUG::Soundcloud, string_compose ("opening %1", path) );
+                                       open_uri(path.c_str());  // open the soundcloud website to the new file
+                               }
+                       } else {
+                               error << _("upload to Soundcloud failed.  Perhaps your email or password are incorrect?\n") << endmsg;
+                       }
+                       delete soundcloud_uploader;
+               }
                config_map.erase (config_map.begin());
        }
 
        start_timespan ();
 }
 
-/*** CD Marker sutff ***/
+/*** CD Marker stuff ***/
 
 struct LocationSortByStart {
     bool operator() (Location *a, Location *b) {
index 0792232b5fc0edb60e8d45773e51762c5f7760aa..4a10d3dfa832b035e220d171f164f4ff1ae0b0f6 100644 (file)
@@ -332,8 +332,7 @@ ExportProfileManager::find_file (std::string const & pattern)
 {
        vector<std::string> found;
 
-       Glib::PatternSpec pattern_spec (pattern);
-       find_matching_files_in_search_path (search_path, pattern_spec, found);
+       find_files_matching_pattern (found, search_path, pattern);
 
        return found;
 }
index 109539ce2d7539fe242861a0d189b6e55e5f701b..8c41f981b9b480ac5da359096659f294ec313298 100644 (file)
@@ -56,7 +56,7 @@ PBD::Signal2<int,std::string,std::vector<std::string> > FileSource::AmbiguousFil
 FileSource::FileSource (Session& session, DataType type, const string& path, const string& origin, Source::Flag flag)
        : Source(session, type, path, flag)
        , _path (path)
-       , _file_is_new (!origin.empty()) // origin empty => new file VS. origin !empty => new file
+       , _file_is_new (!origin.empty()) // if origin is left unspecified (empty string) then file must exist
        , _channel (0)
         , _origin (origin)
         , _open (false)
@@ -214,7 +214,7 @@ FileSource::move_to_trash (const string& trash_dir_name)
 
        if (move_dependents_to_trash() != 0) {
                /* try to back out */
-               rename (newpath.c_str(), _path.c_str());
+               ::rename (newpath.c_str(), _path.c_str());
                return -1;
        }
 
@@ -572,3 +572,28 @@ FileSource::is_stub () const
        return true;
 }
                
+int
+FileSource::rename (const string& newpath)
+{
+       Glib::Threads::Mutex::Lock lm (_lock);
+       string oldpath = _path;
+
+       // Test whether newpath exists, if yes notify the user but continue.
+       if (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)) {
+               error << string_compose (_("Programming error! %1 tried to rename a file over another file! It's safe to continue working, but please report this to the developers."), PROGRAM_NAME) << endmsg;
+               return -1;
+       }
+
+       if (Glib::file_test (oldpath.c_str(), Glib::FILE_TEST_EXISTS)) { 
+               /* rename only needed if file exists on disk */
+               if (::rename (oldpath.c_str(), newpath.c_str()) != 0) {
+                       error << string_compose (_("cannot rename file %1 to %2 (%3)"), oldpath, newpath, strerror(errno)) << endmsg;
+                       return -1;
+               }
+       }
+
+       _name = Glib::path_get_basename (newpath);
+       _path = newpath;
+
+       return 0;
+}
index b085ec946b3d09305d52b3b8871e8a05a29f1e30..b723de1e56508af42ada0c7c7932e477bf0d4d32 100644 (file)
@@ -59,10 +59,9 @@ Filter::make_new_sources (boost::shared_ptr<Region> region, SourceList& nsrcs, s
                        }
                }
 
-               string path = session.path_from_region_name (region->data_type(),
-                               PBD::basename_nosuffix (names[i]), string (""));
+               string path = session.new_audio_source_path (name, region->n_channels(), i, false, false);
 
-               if (path.length() == 0) {
+               if (path.empty()) {
                        error << string_compose (_("filter: error creating name for new file based on %1"), region->name())
                              << endmsg;
                        return -1;
index 8bee4ec9023cc0f8c4d4c098283ab8cc8a26ca6c..ed62420ff07e7055cb69d7cec3aeb742daee2060 100644 (file)
@@ -241,6 +241,7 @@ ARDOUR::init (bool use_windows_vst, bool try_optimization, const char* localedir
 
 #ifdef ENABLE_NLS
        (void) bindtextdomain(PACKAGE, localedir);
+       (void) bind_textdomain_codeset (PACKAGE, "UTF-8");
 #endif
 
        SessionEvent::init_event_pool ();
@@ -389,6 +390,8 @@ ARDOUR::cleanup ()
 #ifdef LXVST_SUPPORT
        vstfx_exit();
 #endif
+       delete &PluginManager::instance();
+       delete Config;
        PBD::cleanup ();
 
        return;
@@ -401,11 +404,9 @@ ARDOUR::find_bindings_files (map<string,string>& files)
        Searchpath spath = ardour_config_search_path();
 
        if (getenv ("ARDOUR_SAE")) {
-               Glib::PatternSpec pattern("*SAE-*.bindings");
-               find_matching_files_in_search_path (spath, pattern, found);
+               find_files_matching_pattern (found, spath, "*SAE-*.bindings");
        } else {
-               Glib::PatternSpec pattern("*.bindings");
-               find_matching_files_in_search_path (spath, pattern, found);
+               find_files_matching_pattern (found, spath, "*.bindings");
        }
 
        if (found.empty()) {
index b66f3542249bcb042e771c8e405bd97ff57fbbf4..e86e500ed93ff6d12308e5c8db4b289a749c5aee 100644 (file)
@@ -117,78 +117,31 @@ open_importable_source (const string& path, framecnt_t samplerate, ARDOUR::SrcQu
        }
 }
 
-static std::string
-get_non_existent_filename (HeaderFormat hf, DataType type, const bool allow_replacing, const std::string& destdir, const std::string& basename, uint32_t channel, uint32_t channels)
-{
-       char buf[PATH_MAX+1];
-       bool goodfile = false;
-       string base = basename;
-       string ext = native_header_format_extension (hf, type);
-       uint32_t cnt = 1;
-
-       do {
-
-               if (type == DataType::AUDIO && channels == 2) {
-                       if (channel == 0) {
-                               if (cnt == 1) {
-                                       snprintf (buf, sizeof(buf), "%s-L%s", base.c_str(), ext.c_str());
-                               } else {
-                                       snprintf (buf, sizeof(buf), "%s-%d-L%s", base.c_str(), cnt, ext.c_str());
-                               }
-                       } else {
-                               if (cnt == 1) {
-                                       snprintf (buf, sizeof(buf), "%s-R%s", base.c_str(), ext.c_str());
-                               } else {
-                                       snprintf (buf, sizeof(buf), "%s-%d-R%s", base.c_str(), cnt, ext.c_str());
-                               }
-                       }
-               } else if (channels > 1) {
-                       if (cnt == 1) {
-                               snprintf (buf, sizeof(buf), "%s-c%d%s", base.c_str(), channel, ext.c_str());
-                       } else {
-                               snprintf (buf, sizeof(buf), "%s-%d-c%d%s", base.c_str(), cnt, channel, ext.c_str());
-                       }
-               } else {
-                       if (cnt == 1) {
-                               snprintf (buf, sizeof(buf), "%s%s", base.c_str(), ext.c_str());
-                       } else {
-                               snprintf (buf, sizeof(buf), "%s-%d%s", base.c_str(), cnt, ext.c_str());
-                       }
-               }
-
-               string tempname = destdir + "/" + buf;
-
-               if (!allow_replacing && Glib::file_test (tempname, Glib::FILE_TEST_EXISTS)) {
-
-                       cnt++;
-
-               } else {
-
-                       goodfile = true;
-               }
-
-       } while (!goodfile);
-
-       return buf;
-}
-
-static vector<string>
-get_paths_for_new_sources (HeaderFormat hf, const bool allow_replacing, const string& import_file_path, const string& session_dir, uint32_t channels)
+vector<string>
+Session::get_paths_for_new_sources (bool /*allow_replacing*/, const string& import_file_path, uint32_t channels)
 {
        vector<string> new_paths;
        const string basename = basename_nosuffix (import_file_path);
 
-       SessionDirectory sdir(session_dir);
-
        for (uint32_t n = 0; n < channels; ++n) {
 
                const DataType type = SMFSource::safe_midi_file_extension (import_file_path) ? DataType::MIDI : DataType::AUDIO;
+               string filepath;
 
-               std::string filepath = (type == DataType::MIDI)
-                       ? sdir.midi_path() : sdir.sound_path();
+               switch (type) {
+                 case DataType::MIDI:
+                       filepath = new_midi_source_path (basename);
+                       break;
+               case DataType::AUDIO:
+                       filepath = new_audio_source_path (basename, channels, n, false, false);
+                       break;
+               }
+
+               if (filepath.empty()) {
+                       error << string_compose (_("Cannot find new filename for imported file %1"), import_file_path) << endmsg;
+                       return vector<string>();
+               }
 
-               filepath = Glib::build_filename (filepath,
-                                                get_non_existent_filename (hf, type, allow_replacing, filepath, basename, n, channels));
                new_paths.push_back (filepath);
        }
 
@@ -202,7 +155,7 @@ map_existing_mono_sources (const vector<string>& new_paths, Session& /*sess*/,
        for (vector<string>::const_iterator i = new_paths.begin();
             i != new_paths.end(); ++i)
        {
-               boost::shared_ptr<Source> source = session->source_by_path_and_channel(*i, 0);
+               boost::shared_ptr<Source> source = session->audio_source_by_path_and_channel(*i, 0);
 
                if (source == 0) {
                        error << string_compose(_("Could not find a source for %1 even though we are updating this file!"), (*i)) << endl;
@@ -325,7 +278,7 @@ write_audio_data_to_new_files (ImportableSource* source, ImportStatus& status,
                progress_base = 0.5;
        }
 
-       uint32_t read_count = 0;
+       framecnt_t read_count = 0;
 
        while (!status.cancel) {
 
@@ -521,11 +474,13 @@ Session::import_files (ImportStatus& status)
                                return;
                        }
                }
+               
+               if (channels == 0) {
+                       error << _("Import: file contains no channels.") << endmsg;
+                       continue;
+               }
 
-               vector<string> new_paths = get_paths_for_new_sources (config.get_native_file_header_format(),
-                                                                     status.replace_existing_source, *p,
-                                                                     get_best_session_directory_for_new_source (),
-                                                                     channels);
+               vector<string> new_paths = get_paths_for_new_sources (status.replace_existing_source, *p, channels);
                Sources newfiles;
                framepos_t natural_position = source ? source->natural_position() : 0;
 
index fc5963603b5ac572d7d9313b3211b0b2c07bacc4..1eae5d31719e4fde4a97f8a19e1594729e4264f1 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "ardour/internal_return.h"
 #include "ardour/internal_send.h"
+#include "ardour/route.h"
 
 using namespace std;
 using namespace ARDOUR;
@@ -41,7 +42,7 @@ InternalReturn::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*e
        
        if (lm.locked ()) {
                for (list<InternalSend*>::iterator i = _sends.begin(); i != _sends.end(); ++i) {
-                       if ((*i)->active ()) {
+                       if ((*i)->active () && (!(*i)->source_route() || (*i)->source_route()->active())) {
                                bufs.merge_from ((*i)->get_buffers(), nframes);
                        }
                }
index 17a3ca1f421b47dc0acbdd2d866f5286e5739048..e5c7232f480f7d69db09b5ecec96637d31bbb031 100644 (file)
@@ -40,14 +40,21 @@ using namespace std;
 
 PBD::Signal1<void, pframes_t> InternalSend::CycleStart;
 
-InternalSend::InternalSend (Session& s, boost::shared_ptr<Pannable> p, boost::shared_ptr<MuteMaster> mm, boost::shared_ptr<Route> sendto, Delivery::Role role, bool ignore_bitslot)
+InternalSend::InternalSend (Session& s,
+               boost::shared_ptr<Pannable> p,
+               boost::shared_ptr<MuteMaster> mm,
+               boost::shared_ptr<Route> sendfrom,
+               boost::shared_ptr<Route> sendto,
+               Delivery::Role role,
+               bool ignore_bitslot)
        : Send (s, p, mm, role, ignore_bitslot)
+       , _send_from (sendfrom)
 {
-        if (sendto) {
-                if (use_target (sendto)) {
-                        throw failed_constructor();
-                }
-        }
+       if (sendto) {
+               if (use_target (sendto)) {
+                       throw failed_constructor();
+               }
+       }
 
        init_gain ();
 
@@ -202,6 +209,8 @@ InternalSend::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame
        _amp->setup_gain_automation (start_frame, end_frame, nframes);
        _amp->run (mixbufs, start_frame, end_frame, nframes, true);
 
+       _delayline->run (mixbufs, start_frame, end_frame, nframes, true);
+
        /* consider metering */
 
        if (_metering) {
index 0d976d653b520d0bdc20b134873e2860402c2afd..bd1ed0d27e01946e4da747d6ef10f48f136450a2 100644 (file)
@@ -152,8 +152,8 @@ void* vstfx_load_vst_library(const char* path)
        len2 = strlen(path);
 
        /*Try all the possibilities in the path - deliminated by : */
-
-       lxvst_path = strtok (envdup, ":");
+       char *saveptr;
+       lxvst_path = strtok_r (envdup, ":", &saveptr);
        
        while (lxvst_path != 0)
        {
@@ -177,7 +177,7 @@ void* vstfx_load_vst_library(const char* path)
        
                /*Try again*/
 
-               lxvst_path = strtok (0, ":");
+               lxvst_path = strtok_r (0, ":", &saveptr);
        }
 
        /*Free the path*/
index ced0226d00ea3c001267a66ac0be6280bd07b447..8d570aeeb8476047738a8c46b9968061eaac3d0a 100644 (file)
@@ -590,7 +590,7 @@ LTC_Slave::approximate_current_delta() const
        } else if ((monotonic_cnt - last_timestamp) > 2 * frames_per_ltc_frame) {
                snprintf(delta, sizeof(delta), "%s", _("flywheel"));
        } else {
-               snprintf(delta, sizeof(delta), "\u0394<span foreground=\"green\" face=\"monospace\" >%s%s%" PRIi64 "</span>sm",
+               snprintf(delta, sizeof(delta), "\u0394<span foreground=\"green\" face=\"monospace\" >%s%s%lld</span>sm",
                                LEADINGZERO(llabs(current_delta)), PLUSMINUS(-current_delta), llabs(current_delta));
        }
        return std::string(delta);
index 33b02a76e3b2174958448b4e16505c2b7c293ebe..1bdb2f20d6e76921a51833b6fa539813c328f223 100644 (file)
@@ -31,8 +31,8 @@
 
 #include <boost/utility.hpp>
 
-#include "pbd/clear_dir.h"
-#include "pbd/pathscanner.h"
+#include "pbd/file_utils.h"
+#include "pbd/stl_delete.h"
 #include "pbd/compose.h"
 #include "pbd/error.h"
 #include "pbd/xml++.h"
@@ -2013,21 +2013,19 @@ LV2World::load_bundled_plugins()
 {
        if (!_bundle_checked) {
                cout << "Scanning folders for bundled LV2s: " << ARDOUR::lv2_bundled_search_path().to_string() << endl;
-               PathScanner scanner;
-               vector<string *> *plugin_objects = scanner (ARDOUR::lv2_bundled_search_path().to_string(), lv2_filter, 0, true, true);
-               if (plugin_objects) {
-                       for ( vector<string *>::iterator x = plugin_objects->begin(); x != plugin_objects->end (); ++x) {
+
+               vector<string> plugin_objects;
+               find_paths_matching_filter (plugin_objects, ARDOUR::lv2_bundled_search_path(), lv2_filter, 0, true, true, true);
+               for ( vector<string>::iterator x = plugin_objects.begin(); x != plugin_objects.end (); ++x) {
 #ifdef PLATFORM_WINDOWS
-                               string uri = "file:///" + **x + "/";
+                       string uri = "file:///" + *x + "/";
 #else
-                               string uri = "file://" + **x + "/";
+                       string uri = "file://" + *x + "/";
 #endif
-                               LilvNode *node = lilv_new_uri(world, uri.c_str());
-                               lilv_world_load_bundle(world, node);
-                               lilv_node_free(node);
-                       }
+                       LilvNode *node = lilv_new_uri(world, uri.c_str());
+                       lilv_world_load_bundle(world, node);
+                       lilv_node_free(node);
                }
-               delete (plugin_objects);
 
                _bundle_checked = true;
        }
index 1a6cb7fa2612d4fc461d8a3b28c88bce2b10728c..4715be928c18d39c88e68225da959fbbe8527bca 100644 (file)
@@ -199,6 +199,57 @@ MidiBuffer::push_back(TimeType time, size_t size, const uint8_t* data)
        return true;
 }
 
+bool
+MidiBuffer::insert_event(const Evoral::MIDIEvent<TimeType>& ev)
+{
+       if (size() == 0) {
+               return push_back(ev);
+       }
+
+       const size_t stamp_size = sizeof(TimeType);
+       const size_t bytes_to_merge = stamp_size + ev.size();
+
+       if (_size + bytes_to_merge >= _capacity) {
+               cerr << "MidiBuffer::push_back failed (buffer is full)" << endl;
+               PBD::stacktrace (cerr, 20);
+               return false;
+       }
+
+       TimeType t = ev.time();
+
+       ssize_t insert_offset = -1;
+       for (MidiBuffer::iterator m = begin(); m != end(); ++m) {
+               if ((*m).time() < t) {
+                       continue;
+               }
+               if ((*m).time() == t) {
+                       const uint8_t our_midi_status_byte = *(_data + m.offset + sizeof (TimeType));
+                       if (second_simultaneous_midi_byte_is_first (ev.type(), our_midi_status_byte)) {
+                               continue;
+                       }
+               }
+               insert_offset = m.offset;
+               break;
+       }
+       if (insert_offset == -1) {
+               return push_back(ev);
+       }
+
+       // don't use memmove - it may use malloc(!)
+       // memmove (_data + insert_offset + bytes_to_merge, _data + insert_offset, _size - insert_offset);
+       for (ssize_t a = _size + bytes_to_merge - 1, b = _size - 1; b >= insert_offset; --b, --a) {
+               _data[a] = _data[b];
+       }
+
+       uint8_t* const write_loc = _data + insert_offset;
+       *((TimeType*)write_loc) = t;
+       memcpy(write_loc + stamp_size, ev.buffer(), ev.size());
+
+       _size += bytes_to_merge;
+
+       return true;
+}
+
 /** Reserve space for a new event in the buffer.
  *
  * This call is for copying MIDI directly into the buffer, the data location
index 4a878c389861b1ae638f47d1f17353a494242284..69eca996aaeda92cf56c47643f34f4d966d82265 100644 (file)
@@ -75,6 +75,7 @@ MidiDiskstream::MidiDiskstream (Session &sess, const string &name, Diskstream::F
        , _frames_read_from_ringbuffer(0)
        , _frames_pending_write(0)
        , _num_captured_loops(0)
+       , _accumulated_capture_offset(0)
        , _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI))
 {
        in_set_state = true;
@@ -99,6 +100,7 @@ MidiDiskstream::MidiDiskstream (Session& sess, const XMLNode& node)
        , _frames_read_from_ringbuffer(0)
        , _frames_pending_write(0)
        , _num_captured_loops(0)
+       , _accumulated_capture_offset(0)
        , _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI))
 {
        in_set_state = true;
@@ -136,6 +138,8 @@ MidiDiskstream::init ()
 MidiDiskstream::~MidiDiskstream ()
 {
        Glib::Threads::Mutex::Lock lm (state_lock);
+       delete _playback_buf;
+       delete _capture_buf;
 }
 
 
@@ -362,6 +366,15 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
                Evoral::OverlapType ot = Evoral::coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
 
                calculate_record_range(ot, transport_frame, nframes, rec_nframes, rec_offset);
+               /* For audio: not writing frames to the capture ringbuffer offsets
+                * the recording. For midi: we need to keep track of the record range
+                * and subtract the accumulated difference from the event time.
+                */
+               if (rec_nframes) {
+                       _accumulated_capture_offset += rec_offset;
+               } else {
+                       _accumulated_capture_offset += nframes;
+               }
 
                if (rec_nframes && !was_recording) {
                        if (loop_loc) {
@@ -394,6 +407,9 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
 
                for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) {
                        Evoral::MIDIEvent<MidiBuffer::TimeType> ev(*i, false);
+                       if (ev.time() + rec_offset > rec_nframes) {
+                               break;
+                       }
 #ifndef NDEBUG
                        if (DEBUG::MidiIO & PBD::debug_bits) {
                                const uint8_t* __data = ev.buffer();
@@ -416,23 +432,26 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
                           any desirable behaviour.  We don't want to send event with
                           transport time here since that way the source can not
                           reconstruct their actual time; future clever MIDI looping should
-                          probabl be implemented in the source instead of here.
+                          probably be implemented in the source instead of here.
                        */
                        const framecnt_t loop_offset = _num_captured_loops * loop_length;
-                       
+                       const framepos_t event_time = transport_frame + loop_offset - _accumulated_capture_offset + ev.time();
+                       if (event_time < 0 || event_time < first_recordable_frame) {
+                               continue;
+                       }
                        switch (mode) {
                        case AllChannels:
-                               _capture_buf->write(transport_frame + loop_offset + ev.time(),
+                               _capture_buf->write(event_time,
                                                    ev.type(), ev.size(), ev.buffer());
                                break;
                        case FilterChannels:
                                if (ev.is_channel_event()) {
                                        if ((1<<ev.channel()) & mask) {
-                                               _capture_buf->write(transport_frame + loop_offset + ev.time(),
+                                               _capture_buf->write(event_time,
                                                                    ev.type(), ev.size(), ev.buffer());
                                        }
                                } else {
-                                       _capture_buf->write(transport_frame + loop_offset + ev.time(),
+                                       _capture_buf->write(event_time,
                                                            ev.type(), ev.size(), ev.buffer());
                                }
                                break;
@@ -440,7 +459,7 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
                                if (ev.is_channel_event()) {
                                        ev.set_channel (PBD::ffs(mask) - 1);
                                }
-                               _capture_buf->write(transport_frame + loop_offset + ev.time(),
+                               _capture_buf->write(event_time,
                                                    ev.type(), ev.size(), ev.buffer());
                                break;
                        }
@@ -472,6 +491,7 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
                if (was_recording) {
                        finish_capture ();
                }
+               _accumulated_capture_offset = 0;
 
        }
 
@@ -972,6 +992,10 @@ MidiDiskstream::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen
 
                                RegionFactory::region_name (region_name, _write_source->name(), false);
 
+                               DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 capture start @ %2 length %3 add new region %4\n",
+                                                       _name, (*ci)->start, (*ci)->frames, region_name));
+
+
                                // cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add a region\n";
 
                                try {
@@ -1194,11 +1218,12 @@ MidiDiskstream::use_new_write_source (uint32_t n)
                return 1;
        }
 
+       _accumulated_capture_offset = 0;
        _write_source.reset();
 
        try {
                _write_source = boost::dynamic_pointer_cast<SMFSource>(
-                       _session.create_midi_source_for_session (name ()));
+                       _session.create_midi_source_for_session (write_source_name ()));
 
                if (!_write_source) {
                        throw failed_constructor();
@@ -1233,9 +1258,9 @@ MidiDiskstream::steal_write_source_name ()
         */
 
        try {
-               string new_name = _session.new_midi_source_name (name());
+               string new_path = _session.new_midi_source_path (name());
                
-               if (_write_source->rename (new_name)) {
+               if (_write_source->rename (new_path)) {
                        return string();
                }
        } catch (...) {
@@ -1416,6 +1441,9 @@ MidiDiskstream::get_playback (MidiBuffer& dst, framecnt_t nframes)
 bool
 MidiDiskstream::set_name (string const & name)
 {
+       if (_name == name) {
+               return true;
+       }
        Diskstream::set_name (name);
 
        /* get a new write source so that its name reflects the new diskstream name */
@@ -1424,6 +1452,19 @@ MidiDiskstream::set_name (string const & name)
        return true;
 }
 
+bool
+MidiDiskstream::set_write_source_name (const std::string& str) {
+       if (_write_source_name == str) {
+               return true;
+       }
+       Diskstream::set_write_source_name (str);
+       if (_write_source_name == name()) {
+               return true;
+       }
+       use_new_write_source (0);
+       return true;
+}
+
 boost::shared_ptr<MidiBuffer>
 MidiDiskstream::get_gui_feed_buffer () const
 {
index 4587b1aad9d483243f963ffa14b467eb04db553c..591c0cae40909743725d10976dc00bc0508db9c3 100644 (file)
@@ -68,10 +68,9 @@ MidiPatchManager::add_session_patches ()
 
        assert (Glib::file_test (path_to_patches, Glib::FILE_TEST_IS_DIR));
 
-       Glib::PatternSpec pattern(string("*.midnam"));
        vector<std::string> result;
 
-       find_matching_files_in_directory (path_to_patches, pattern, result);
+       find_files_matching_pattern (result, path_to_patches, "*.midnam");
 
        info << "Loading " << result.size() << " MIDI patches from " << path_to_patches << endmsg;
 
@@ -104,10 +103,9 @@ MidiPatchManager::refresh()
        _all_models.clear();
 
        Searchpath search_path = midi_patch_search_path ();
-       Glib::PatternSpec pattern (string("*.midnam"));
        vector<std::string> result;
 
-       find_matching_files_in_search_path (search_path, pattern, result);
+       find_files_matching_pattern (result, search_path, "*.midnam");
 
        info << "Loading " << result.size() << " MIDI patches from " << search_path.to_string() << endmsg;
 
index b820c001e1c1a846cd7d4aaaec35df0bb26c5c84..638ed057063a56b08b0e2f034fa4d6cc1d336260 100644 (file)
@@ -547,7 +547,7 @@ MidiTrack::write_out_of_band_data (BufferSet& bufs, framepos_t /*start*/, framep
 
 int
 MidiTrack::export_stuff (BufferSet& /*bufs*/, framepos_t /*start_frame*/, framecnt_t /*nframes*/, 
-                        boost::shared_ptr<Processor> /*endpoint*/, bool /*include_endpoint*/, bool /*forexport*/)
+                        boost::shared_ptr<Processor> /*endpoint*/, bool /*include_endpoint*/, bool /*for_export*/, bool /*for_freeze*/)
 {
        return -1;
 }
diff --git a/libs/ardour/mididm.cc b/libs/ardour/mididm.cc
new file mode 100644 (file)
index 0000000..0888d15
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2013-2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "ardour/mididm.h"
+#include "ardour/port_engine.h"
+
+using namespace ARDOUR;
+
+MIDIDM::MIDIDM (framecnt_t sample_rate)
+  : _sample_rate (sample_rate)
+  , _monotonic_cnt (sample_rate)
+  , _last_signal_tme (0)
+  , _cnt_total (0)
+  , _dly_total (0)
+  , _min_delay (INT32_MAX)
+  , _max_delay (0)
+  , _avg_delay (0)
+  , _var_m (0)
+  , _var_s (0)
+{
+
+}
+
+int64_t
+MIDIDM::parse_mclk (uint8_t* buf, pframes_t timestamp) const
+{
+       /* calculate time difference */
+#define MODCLK (16384)  // 1<<(2*7)
+       const int64_t tc = (_monotonic_cnt + timestamp) & 0x3fff; // MODCLK - 1;
+       const int64_t ti = ((buf[2] & 0x7f) << 7) | (buf[1] & 0x7f);
+       const int64_t tdiff = (MODCLK + tc - ti) % MODCLK;
+#ifdef DEBUG_MIDIDM
+               printf("MCLK DELAY: #%5"PRId64" dt:%6"PRId64" [spl] (%6"PRId64" - %8"PRId64") @(%8"PRId64" + %d)\n",
+                               _cnt_total, tdiff, tc, ti, _monotonic_cnt, timestamp);
+#endif
+       return tdiff;
+}
+
+int64_t
+MIDIDM::parse_mtc (uint8_t* buf, pframes_t timestamp) const
+{
+#define MODTC (2097152)  // 1<<(3*7)
+       const int64_t tc = (_monotonic_cnt + timestamp) & 0x001FFFFF;
+       const int64_t ti = (buf[5] & 0x7f)
+               | ((buf[6] & 0x7f) << 7)
+               | ((buf[7] & 0x7f) << 14)
+               | ((buf[8] & 0x7f) << 21);
+       const int64_t tdiff = (MODTC + tc - ti) % MODTC;
+#ifdef DEBUG_MIDIDM
+               printf("MTC DELAY: #%5"PRId64" dt:%6"PRId64" [spl] (%6"PRId64" - %8"PRId64") @(%8"PRId64" + %d)\n",
+                               _cnt_total, tdiff, tc, ti, _monotonic_cnt, timestamp);
+#endif
+       return tdiff;
+}
+
+int MIDIDM::process (pframes_t nframes, PortEngine &pe, void *midi_in, void *midi_out)
+{
+       /* send midi event */
+       pe.midi_clear(midi_out);
+#ifndef USE_MTC // use 3-byte song position
+       uint8_t obuf[3];
+       obuf[0] = 0xf2;
+       obuf[1] = (_monotonic_cnt)      & 0x7f;
+       obuf[2] = (_monotonic_cnt >> 7) & 0x7f;
+       pe.midi_event_put (midi_out, 0, obuf, 3);
+#else // sysex MTC frame
+       uint8_t obuf[10];
+       obuf[0] = 0xf0;
+       obuf[1] = 0x7f;
+       obuf[2] = 0x7f;
+       obuf[3] = 0x01;
+       obuf[4] = 0x01;
+       obuf[9] = 0xf7;
+       obuf[5] = (_monotonic_cnt      ) & 0x7f;
+       obuf[6] = (_monotonic_cnt >>  7) & 0x7f;
+       obuf[7] = (_monotonic_cnt >> 14) & 0x7f;
+       obuf[8] = (_monotonic_cnt >> 21) & 0x7f;
+       pe.midi_event_put (midi_out, 0, obuf, 10);
+#endif
+
+       /* process incoming */
+       const pframes_t nevents = pe.get_midi_event_count (midi_in);
+#ifdef DEBUG_MIDIDM
+               printf("MIDI SEND: @%8"PRId64", recv: %d systime:%"PRId64"\n", _monotonic_cnt, nevents, g_get_monotonic_time());
+#endif
+       for (pframes_t n = 0; n < nevents; ++n) {
+               pframes_t timestamp;
+               size_t size;
+               uint8_t* buf;
+               int64_t tdiff;
+               pe.midi_event_get (timestamp, size, &buf, midi_in, n);
+
+               if (size == 3 && buf[0] == 0xf2 )
+               {
+                       tdiff = parse_mclk(buf, timestamp);
+               } else if (size == 10 && buf[0] == 0xf0)
+               {
+                       tdiff = parse_mtc(buf, timestamp);
+               }
+               else
+               {
+                       continue;
+               }
+
+               _last_signal_tme = _monotonic_cnt;
+
+               /* running variance */
+               if (_cnt_total == 0) {
+                       _var_m = tdiff;
+               } else {
+                       const double var_m1 = _var_m;
+                       _var_m = _var_m + ((double)tdiff - _var_m) / (double)(_cnt_total + 1);
+                       _var_s = _var_s + ((double)tdiff - _var_m) * ((double)tdiff - var_m1);
+               }
+               /* average and mix/max */
+               ++_cnt_total;
+               _dly_total += tdiff;
+               _avg_delay = _dly_total / _cnt_total;
+               if (tdiff < _min_delay) _min_delay = tdiff;
+               if (tdiff > _max_delay) _max_delay = tdiff;
+       }
+
+  _monotonic_cnt += nframes;
+       return 0;
+}
index b14c7c79cf532efcf4a969e9495c327f4ef0ac47..906e979862a45e1b3a30b5edf0c4f6a11d4c4dc1 100644 (file)
@@ -24,7 +24,7 @@
 
 #include "pbd/error.h"
 #include "pbd/compose.h"
-#include "pbd/pathscanner.h"
+#include "pbd/file_utils.h"
 #include "pbd/stl_delete.h"
 
 #include "ardour/debug.h"
@@ -90,19 +90,15 @@ static bool panner_filter (const string& str, void */*arg*/)
 void
 PannerManager::discover_panners ()
 {
-       PathScanner scanner;
-       std::vector<std::string *> *panner_modules;
-       std::string search_path = panner_search_path().to_string();
+       std::vector<std::string> panner_modules;
 
-       DEBUG_TRACE (DEBUG::Panning, string_compose (_("looking for panners in %1\n"), search_path));
+       DEBUG_TRACE (DEBUG::Panning, string_compose (_("looking for panners in %1\n"), panner_search_path().to_string()));
 
-       panner_modules = scanner (search_path, panner_filter, 0, false, true, 1, true);
+       find_files_matching_filter (panner_modules, panner_search_path(), panner_filter, 0, false, true, true);
 
-       for (vector<std::string *>::iterator i = panner_modules->begin(); i != panner_modules->end(); ++i) {
-               panner_discover (**i);
+       for (vector<std::string>::iterator i = panner_modules.begin(); i != panner_modules.end(); ++i) {
+               panner_discover (*i);
        }
-
-       vector_delete (panner_modules);
 }
 
 int
@@ -123,6 +119,8 @@ PannerManager::panner_discover (string path)
                if (i == panner_info.end()) {
                        panner_info.push_back (pinfo);
                        DEBUG_TRACE (DEBUG::Panning, string_compose(_("Panner discovered: \"%1\" in %2\n"), pinfo->descriptor.name, path));
+               } else {
+                       delete pinfo;
                }
        }
 
index 064554eac65dab1bbf3e8b9f376b3e8afc7345a0..a4a9342816375ac7ea8b8e34d9ef8c7c366fc8bc 100644 (file)
@@ -233,6 +233,16 @@ PannerShell::set_state (const XMLNode& node, int version)
                                                _current_panner_uri = (*p)->descriptor.panner_uri;
                                                _panner_gui_uri = (*p)->descriptor.gui_uri;
 
+                                               if (_is_send) {
+                                                       if (!_panlinked) {
+                                                               _pannable_internal->set_panner(_panner);
+                                                       } else {
+                                                               _force_reselect = true;
+                                                       }
+                                               } else {
+                                                       _pannable_route->set_panner(_panner);
+                                               }
+
                                                if (_panner->set_state (**niter, version) == 0) {
                                                        return -1;
                                                }
index 11ca20972e7fe7c2ef889efb2f2fd72ac8f8a83d..266535da20ff15535007980bcc49a423068b2f5c 100644 (file)
@@ -2868,7 +2868,14 @@ Playlist::combine (const RegionList& r)
 
        pl->in_partition = true;
 
-       for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
+       /* sort by position then layer.
+        * route_time_axis passes 'selected_regions' - which is not sorted.
+        * here we need the top-most first, then every layer's region sorted by position.
+        */
+       RegionList sorted(r);
+       sorted.sort(RegionSortByLayerAndPosition());
+
+       for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
 
                /* copy the region */
 
@@ -3109,6 +3116,7 @@ Playlist::uncombine (boost::shared_ptr<Region> target)
 
        for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
                add_region ((*i), (*i)->position());
+               set_layer((*i), (*i)->layer());
        }
 
        in_partition = false;
index 6fbb5cb67908de7f318ecc46d087b034bdff5bab..5279a3696216e3ac216ed11e57d8adb5a6b80168 100644 (file)
@@ -455,13 +455,13 @@ PluginInsert::silence (framecnt_t nframes)
 }
 
 void
-PluginInsert::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/, pframes_t nframes, bool)
+PluginInsert::run (BufferSet& bufs, framepos_t start_frame, framepos_t /*end_frame*/, pframes_t nframes, bool)
 {
        if (_pending_active) {
                /* run as normal if we are active or moving from inactive to active */
 
-               if (_session.transport_rolling()) {
-                       automation_run (bufs, nframes);
+               if (_session.transport_rolling() || _session.bounce_processing()) {
+                       automation_run (bufs, start_frame, nframes);
                } else {
                        connect_and_run (bufs, nframes, 0, false);
                }
@@ -538,10 +538,10 @@ PluginInsert::get_parameter (Evoral::Parameter param)
 }
 
 void
-PluginInsert::automation_run (BufferSet& bufs, pframes_t nframes)
+PluginInsert::automation_run (BufferSet& bufs, framepos_t start, pframes_t nframes)
 {
        Evoral::ControlEvent next_event (0, 0.0f);
-       framepos_t now = _session.transport_frame ();
+       framepos_t now = start;
        framepos_t end = now + nframes;
        framecnt_t offset = 0;
 
@@ -1203,6 +1203,10 @@ PluginInsert::PluginControl::PluginControl (PluginInsert* p, const Evoral::Param
        _logarithmic = desc.logarithmic;
        _sr_dependent = desc.sr_dependent;
        _toggled = desc.toggled;
+
+       if (desc.toggled) {
+               set_flags(Controllable::Toggle);
+       }
 }
 
 /** @param val `user' value */
index 7c3eae538f9531fa9e17b20a2a796c2ebb4b3c6b..e596c676343abc1ff5fe78b34fac3b7f778de0d6 100644 (file)
@@ -50,7 +50,6 @@
 #include <glibmm/miscutils.h>
 #include <glibmm/pattern.h>
 
-#include "pbd/pathscanner.h"
 #include "pbd/whitespace.h"
 #include "pbd/file_utils.h"
 
@@ -117,7 +116,7 @@ PluginManager::PluginManager ()
        string lrdf_path;
 
        string  scan_p = Glib::build_filename(ARDOUR::ardour_dll_directory(), "fst");
-       if (!PBD::find_file_in_search_path ( PBD::Searchpath(scan_p), "ardour-vst-scanner", scanner_bin_path)) {
+       if (!PBD::find_file ( PBD::Searchpath(scan_p), "ardour-vst-scanner", scanner_bin_path)) {
                PBD::warning << "VST scanner app (ardour-vst-scanner) not found in path " << scan_p <<  endmsg;
        }
 
@@ -242,45 +241,32 @@ PluginManager::clear_vst_cache ()
        // see also libs/ardour/vst_info_file.cc - vstfx_infofile_path()
 #ifdef WINDOWS_VST_SUPPORT
        {
-               PathScanner scanner;
-               vector<string *> *fsi_files;
-
-               fsi_files = scanner (Config->get_plugin_path_vst(), "\\.fsi$", true, true, -1, false);
-               if (fsi_files) {
-                       for (vector<string *>::iterator i = fsi_files->begin(); i != fsi_files->end (); ++i) {
-                               ::g_unlink((*i)->c_str());
-                       }
+               vector<string> fsi_files;
+               find_files_matching_regex (fsi_files, Config->get_plugin_path_vst(), "\\.fsi$");
+               for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+                       ::g_unlink(i->c_str());
                }
-               vector_delete(fsi_files);
        }
 #endif
 
 #ifdef LXVST_SUPPORT
        {
-               PathScanner scanner;
-               vector<string *> *fsi_files;
-               fsi_files = scanner (Config->get_plugin_path_lxvst(), "\\.fsi$", true, true, -1, false);
-               if (fsi_files) {
-                       for (vector<string *>::iterator i = fsi_files->begin(); i != fsi_files->end (); ++i) {
-                               ::g_unlink((*i)->c_str());
-                       }
+               vector<string> fsi_files;
+               find_files_matching_regex (fsi_files, Config->get_plugin_path_lxvst(), "\\.fsi$");
+               for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+                       ::g_unlink(i->c_str());
                }
-               vector_delete(fsi_files);
        }
 #endif
 
 #if (defined WINDOWS_VST_SUPPORT || defined LXVST_SUPPORT)
        {
                string personal = get_personal_vst_info_cache_dir();
-               PathScanner scanner;
-               vector<string *> *fsi_files;
-               fsi_files = scanner (personal, "\\.fsi$", true, true, -1, false);
-               if (fsi_files) {
-                       for (vector<string *>::iterator i = fsi_files->begin(); i != fsi_files->end (); ++i) {
-                               ::g_unlink((*i)->c_str());
-                       }
+               vector<string> fsi_files;
+               find_files_matching_regex (fsi_files, personal, "\\.fsi$");
+               for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+                       ::g_unlink(i->c_str());
                }
-               vector_delete(fsi_files);
        }
 #endif
 }
@@ -290,30 +276,21 @@ PluginManager::clear_vst_blacklist ()
 {
 #ifdef WINDOWS_VST_SUPPORT
        {
-               PathScanner scanner;
-               vector<string *> *fsi_files;
-
-               fsi_files = scanner (Config->get_plugin_path_vst(), "\\.fsb$", true, true, -1, false);
-               if (fsi_files) {
-                       for (vector<string *>::iterator i = fsi_files->begin(); i != fsi_files->end (); ++i) {
-                               ::g_unlink((*i)->c_str());
-                       }
+               vector<string> fsi_files;
+               find_files_matching_regex (fsi_files, Config->get_plugin_path_vst(), "\\.fsb$");
+               for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+                       ::g_unlink(i->c_str());
                }
-               vector_delete(fsi_files);
        }
 #endif
 
 #ifdef LXVST_SUPPORT
        {
-               PathScanner scanner;
-               vector<string *> *fsi_files;
-               fsi_files = scanner (Config->get_plugin_path_lxvst(), "\\.fsb$", true, true, -1, false);
-               if (fsi_files) {
-                       for (vector<string *>::iterator i = fsi_files->begin(); i != fsi_files->end (); ++i) {
-                               ::g_unlink((*i)->c_str());
-                       }
+               vector<string> fsi_files;
+               find_files_matching_regex (fsi_files, Config->get_plugin_path_lxvst(), "\\.fsb$");
+               for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+                       ::g_unlink(i->c_str());
                }
-               vector_delete(fsi_files);
        }
 #endif
 
@@ -321,15 +298,11 @@ PluginManager::clear_vst_blacklist ()
        {
                string personal = get_personal_vst_blacklist_dir();
 
-               PathScanner scanner;
-               vector<string *> *fsi_files;
-               fsi_files = scanner (personal, "\\.fsb$", true, true, -1, false);
-               if (fsi_files) {
-                       for (vector<string *>::iterator i = fsi_files->begin(); i != fsi_files->end (); ++i) {
-                               ::g_unlink((*i)->c_str());
-                       }
+               vector<string> fsi_files;
+               find_files_matching_regex (fsi_files, personal, "\\.fsb$");
+               for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+                       ::g_unlink(i->c_str());
                }
-               vector_delete(fsi_files);
        }
 #endif
 }
@@ -353,18 +326,9 @@ PluginManager::ladspa_refresh ()
 
        DEBUG_TRACE (DEBUG::PluginManager, string_compose ("LADSPA: search along: [%1]\n", ladspa_search_path().to_string()));
 
-       Glib::PatternSpec so_extension_pattern("*.so");
-       Glib::PatternSpec dylib_extension_pattern("*.dylib");
-       Glib::PatternSpec dll_extension_pattern("*.dll");
-
-       find_matching_files_in_search_path (ladspa_search_path (),
-                                           so_extension_pattern, ladspa_modules);
-
-       find_matching_files_in_search_path (ladspa_search_path (),
-                                           dylib_extension_pattern, ladspa_modules);
-
-       find_matching_files_in_search_path (ladspa_search_path (),
-                                           dll_extension_pattern, ladspa_modules);
+       find_files_matching_pattern (ladspa_modules, ladspa_search_path (), "*.so");
+       find_files_matching_pattern (ladspa_modules, ladspa_search_path (), "*.dylib");
+       find_files_matching_pattern (ladspa_modules, ladspa_search_path (), "*.dll");
 
        for (vector<std::string>::iterator i = ladspa_modules.begin(); i != ladspa_modules.end(); ++i) {
                ARDOUR::PluginScanMessage(_("LADSPA"), *i, false);
@@ -403,9 +367,8 @@ void
 PluginManager::add_presets(string domain)
 {
 #ifdef HAVE_LRDF
-       PathScanner scanner;
-       vector<string *> *presets;
-       vector<string *>::iterator x;
+       vector<string> presets;
+       vector<string>::iterator x;
 
        char* envvar;
        if ((envvar = getenv ("HOME")) == 0) {
@@ -413,18 +376,15 @@ PluginManager::add_presets(string domain)
        }
 
        string path = string_compose("%1/.%2/rdf", envvar, domain);
-       presets = scanner (path, rdf_filter, 0, false, true);
+       find_files_matching_filter (presets, path, rdf_filter, 0, false, true);
 
-       if (presets) {
-               for (x = presets->begin(); x != presets->end (); ++x) {
-                       string file = "file:" + **x;
-                       if (lrdf_read_file(file.c_str())) {
-                               warning << string_compose(_("Could not parse rdf file: %1"), *x) << endmsg;
-                       }
+       for (x = presets.begin(); x != presets.end (); ++x) {
+               string file = "file:" + *x;
+               if (lrdf_read_file(file.c_str())) {
+                       warning << string_compose(_("Could not parse rdf file: %1"), *x) << endmsg;
                }
-
-               vector_delete (presets);
        }
+
 #endif
 }
 
@@ -432,22 +392,17 @@ void
 PluginManager::add_lrdf_data (const string &path)
 {
 #ifdef HAVE_LRDF
-       PathScanner scanner;
-       vector<string *>* rdf_files;
-       vector<string *>::iterator x;
+       vector<string> rdf_files;
+       vector<string>::iterator x;
 
-       rdf_files = scanner (path, rdf_filter, 0, false, true);
+       find_files_matching_filter (rdf_files, path, rdf_filter, 0, false, true);
 
-       if (rdf_files) {
-               for (x = rdf_files->begin(); x != rdf_files->end (); ++x) {
-                       const string uri(string("file://") + **x);
+       for (x = rdf_files.begin(); x != rdf_files.end (); ++x) {
+               const string uri(string("file://") + *x);
 
-                       if (lrdf_read_file(uri.c_str())) {
-                               warning << "Could not parse rdf file: " << uri << endmsg;
-                       }
+               if (lrdf_read_file(uri.c_str())) {
+                       warning << "Could not parse rdf file: " << uri << endmsg;
                }
-
-               vector_delete (rdf_files);
        }
 #endif
 }
@@ -653,22 +608,17 @@ static bool windows_vst_filter (const string& str, void * /*arg*/)
 int
 PluginManager::windows_vst_discover_from_path (string path, bool cache_only)
 {
-       PathScanner scanner;
-       vector<string *> *plugin_objects;
-       vector<string *>::iterator x;
+       vector<string> plugin_objects;
+       vector<string>::iterator x;
        int ret = 0;
 
        DEBUG_TRACE (DEBUG::PluginManager, string_compose ("detecting Windows VST plugins along %1\n", path));
 
-       plugin_objects = scanner (Config->get_plugin_path_vst(), windows_vst_filter, 0, false, true);
-
-       if (plugin_objects) {
-               for (x = plugin_objects->begin(); x != plugin_objects->end (); ++x) {
-                       ARDOUR::PluginScanMessage(_("VST"), **x, !cache_only && !cancelled());
-                       windows_vst_discover (**x, cache_only || cancelled());
-               }
+       find_files_matching_filter (plugin_objects, Config->get_plugin_path_vst(), windows_vst_filter, 0, false, true);
 
-               vector_delete (plugin_objects);
+       for (x = plugin_objects.begin(); x != plugin_objects.end (); ++x) {
+               ARDOUR::PluginScanMessage(_("VST"), *x, !cache_only && !cancelled());
+               windows_vst_discover (*x, cache_only || cancelled());
        }
 
        return ret;
@@ -773,9 +723,8 @@ static bool lxvst_filter (const string& str, void *)
 int
 PluginManager::lxvst_discover_from_path (string path, bool cache_only)
 {
-       PathScanner scanner;
-       vector<string *> *plugin_objects;
-       vector<string *>::iterator x;
+       vector<string> plugin_objects;
+       vector<string>::iterator x;
        int ret = 0;
 
 #ifndef NDEBUG
@@ -784,15 +733,11 @@ PluginManager::lxvst_discover_from_path (string path, bool cache_only)
 
        DEBUG_TRACE (DEBUG::PluginManager, string_compose ("Discovering linuxVST plugins along %1\n", path));
 
-       plugin_objects = scanner (Config->get_plugin_path_lxvst(), lxvst_filter, 0, false, true);
-
-       if (plugin_objects) {
-               for (x = plugin_objects->begin(); x != plugin_objects->end (); ++x) {
-                       ARDOUR::PluginScanMessage(_("LXVST"), **x, !cache_only && !cancelled());
-                       lxvst_discover (**x, cache_only || cancelled());
-               }
+       find_files_matching_filter (plugin_objects, Config->get_plugin_path_lxvst(), lxvst_filter, 0, false, true);
 
-               vector_delete (plugin_objects);
+       for (x = plugin_objects.begin(); x != plugin_objects.end (); ++x) {
+               ARDOUR::PluginScanMessage(_("LXVST"), *x, !cache_only && !cancelled());
+               lxvst_discover (*x, cache_only || cancelled());
        }
 
        return ret;
index 7fe030fb7e0102e3d39acc2a44586f45f253900f..b0be237563b4bb97c6184838cde61750da3c6bd0 100644 (file)
@@ -76,7 +76,7 @@ RCConfiguration::load_state ()
 
        /* load system configuration first */
 
-       if (find_file_in_search_path (ardour_config_search_path(), "ardour_system.rc", rcfile)) {
+       if (find_file (ardour_config_search_path(), "ardour_system.rc", rcfile)) {
 
                /* stupid XML Parser hates empty files */
 
@@ -104,7 +104,7 @@ RCConfiguration::load_state ()
 
        /* now load configuration file for user */
 
-       if (find_file_in_search_path (ardour_config_search_path(), "ardour.rc", rcfile)) {
+       if (find_file (ardour_config_search_path(), "ardour.rc", rcfile)) {
 
                /* stupid XML parser hates empty files */
 
index 4d7bb5802dec805b6b1c40802282abd2104bf088..5df58ea846b63669d26722f4e07449d5138dd346 100644 (file)
@@ -47,6 +47,7 @@
 #include "ardour/internal_return.h"
 #include "ardour/internal_send.h"
 #include "ardour/meter.h"
+#include "ardour/delayline.h"
 #include "ardour/midi_buffer.h"
 #include "ardour/midi_port.h"
 #include "ardour/monitor_processor.h"
@@ -79,6 +80,7 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        , GraphNode (sess._process_graph)
        , _active (true)
        , _signal_latency (0)
+       , _signal_latency_at_amp_position (0)
        , _initial_delay (0)
        , _roll_delay (0)
        , _flags (flg)
@@ -100,6 +102,7 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        , _order_key (0)
        , _has_order_key (false)
        , _remote_control_id (0)
+       , _track_number (0)
        , _in_configure_processors (false)
        , _initial_io_setup (false)
        , _custom_meter_position_noted (false)
@@ -142,6 +145,11 @@ Route::init ()
        _output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2));
        _output->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::output_port_count_changing, this, _1));
 
+       if (!is_master() && !is_monitor() && !is_auditioner()) {
+               _delayline.reset (new DelayLine (_session, _name));
+               add_processor (_delayline, PreFader);
+       }
+
        /* add amp processor  */
 
        _amp.reset (new Amp (_session));
@@ -428,7 +436,10 @@ Route::process_output_buffers (BufferSet& bufs,
        /* figure out if we're going to use gain automation */
        if (gain_automation_ok) {
                _amp->set_gain_automation_buffer (_session.gain_automation_buffer ());
-               _amp->setup_gain_automation (start_frame, end_frame, nframes);
+               _amp->setup_gain_automation (
+                               start_frame + _signal_latency_at_amp_position,
+                               end_frame + _signal_latency_at_amp_position,
+                               nframes);
        } else {
                _amp->apply_gain_automation (false);
        }
@@ -506,6 +517,8 @@ Route::process_output_buffers (BufferSet& bufs,
        /* set this to be true if the meter will already have been ::run() earlier */
        bool const meter_already_run = metering_state() == MeteringInput;
 
+       framecnt_t latency = 0;
+
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
 
                if (meter_already_run && boost::dynamic_pointer_cast<PeakMeter> (*i)) {
@@ -531,11 +544,120 @@ Route::process_output_buffers (BufferSet& bufs,
                   do we catch route != active somewhere higher?
                */
 
-               (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back());
+               if (boost::dynamic_pointer_cast<Send>(*i) != 0) {
+                       boost::dynamic_pointer_cast<Send>(*i)->set_delay_in(_signal_latency - latency);
+               }
+
+               (*i)->run (bufs, start_frame - latency, end_frame - latency, nframes, *i != _processors.back());
                bufs.set_count ((*i)->output_streams());
+
+               if ((*i)->active ()) {
+                       latency += (*i)->signal_latency ();
+               }
        }
 }
 
+void
+Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes,
+               boost::shared_ptr<Processor> endpoint,
+               bool include_endpoint, bool for_export, bool for_freeze)
+{
+       /* If no processing is required, there's no need to go any further. */
+       if (!endpoint && !include_endpoint) {
+               return;
+       }
+
+       framecnt_t latency = bounce_get_latency(_amp, false, for_export, for_freeze);
+       _amp->set_gain_automation_buffer (_session.gain_automation_buffer ());
+       _amp->setup_gain_automation (start - latency, start - latency + nframes, nframes);
+
+       latency = 0;
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+               if (!include_endpoint && (*i) == endpoint) {
+                       break;
+               }
+
+               /* if we're not exporting, stop processing if we come across a routing processor. */
+               if (!for_export && boost::dynamic_pointer_cast<PortInsert>(*i)) {
+                       break;
+               }
+               if (!for_export && for_freeze && (*i)->does_routing() && (*i)->active()) {
+                       break;
+               }
+
+               /* don't run any processors that does routing.
+                * oh, and don't bother with the peak meter either.
+                */
+               if (!(*i)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*i)) {
+                       (*i)->run (buffers, start - latency, start - latency + nframes, nframes, true);
+                       buffers.set_count ((*i)->output_streams());
+                       latency += (*i)->signal_latency ();
+               }
+
+               if ((*i) == endpoint) {
+                       break;
+               }
+       }
+}
+
+framecnt_t
+Route::bounce_get_latency (boost::shared_ptr<Processor> endpoint,
+               bool include_endpoint, bool for_export, bool for_freeze) const
+{
+       framecnt_t latency = 0;
+       if (!endpoint && !include_endpoint) {
+               return latency;
+       }
+
+       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if (!include_endpoint && (*i) == endpoint) {
+                       break;
+               }
+               if (!for_export && boost::dynamic_pointer_cast<PortInsert>(*i)) {
+                       break;
+               }
+               if (!for_export && for_freeze && (*i)->does_routing() && (*i)->active()) {
+                       break;
+               }
+               if (!(*i)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*i)) {
+                       latency += (*i)->signal_latency ();
+               }
+               if ((*i) == endpoint) {
+                       break;
+               }
+       }
+       return latency;
+}
+
+ChanCount
+Route::bounce_get_output_streams (ChanCount &cc, boost::shared_ptr<Processor> endpoint,
+               bool include_endpoint, bool for_export, bool for_freeze) const
+{
+       if (!endpoint && !include_endpoint) {
+               return cc;
+       }
+
+       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if (!include_endpoint && (*i) == endpoint) {
+                       break;
+               }
+               if (!for_export && boost::dynamic_pointer_cast<PortInsert>(*i)) {
+                       break;
+               }
+               if (!for_export && for_freeze && (*i)->does_routing() && (*i)->active()) {
+                       break;
+               }
+               if (!(*i)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*i)) {
+                       cc = (*i)->output_streams();
+               }
+               if ((*i) == endpoint) {
+                       break;
+               }
+       }
+       return cc;
+}
+
 ChanCount
 Route::n_process_buffers ()
 {
@@ -1333,7 +1455,7 @@ Route::clear_processors (Placement p)
                                seen_amp = true;
                        }
 
-                       if ((*i) == _amp || (*i) == _meter || (*i) == _main_outs) {
+                       if ((*i) == _amp || (*i) == _meter || (*i) == _main_outs || (*i) == _delayline) {
 
                                /* you can't remove these */
 
@@ -1400,7 +1522,7 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
 
        /* these can never be removed */
 
-       if (processor == _amp || processor == _meter || processor == _main_outs) {
+       if (processor == _amp || processor == _meter || processor == _main_outs || processor == _delayline) {
                return 0;
        }
 
@@ -1517,7 +1639,7 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
 
                        /* these can never be removed */
 
-                       if (processor == _amp || processor == _meter || processor == _main_outs) {
+                       if (processor == _amp || processor == _meter || processor == _main_outs || processor == _delayline) {
                                ++i;
                                continue;
                        }
@@ -2482,6 +2604,9 @@ Route::set_processor_state (const XMLNode& node)
                } else if (prop->value() == "meter") {
                        _meter->set_state (**niter, Stateful::current_state_version);
                        new_order.push_back (_meter);
+               } else if (prop->value() == "delay") {
+                       _delayline->set_state (**niter, Stateful::current_state_version);
+                       new_order.push_back (_delayline);
                } else if (prop->value() == "main-outs") {
                        _main_outs->set_state (**niter, Stateful::current_state_version);
                } else if (prop->value() == "intreturn") {
@@ -2519,7 +2644,7 @@ Route::set_processor_state (const XMLNode& node)
 
                                if (prop->value() == "intsend") {
 
-                                       processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::shared_ptr<Route>(), Delivery::Aux, true));
+                                       processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::dynamic_pointer_cast<ARDOUR::Route>(shared_from_this()), boost::shared_ptr<Route>(), Delivery::Aux, true));
 
                                } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
                                           prop->value() == "lv2" ||
@@ -2688,7 +2813,7 @@ Route::enable_monitor_send ()
 
        /* make sure we have one */
        if (!_monitor_send) {
-               _monitor_send.reset (new InternalSend (_session, _pannable, _mute_master, _session.monitor_out(), Delivery::Listen));
+               _monitor_send.reset (new InternalSend (_session, _pannable, _mute_master, boost::dynamic_pointer_cast<ARDOUR::Route>(shared_from_this()), _session.monitor_out(), Delivery::Listen));
                _monitor_send->set_display_to_user (false);
        }
 
@@ -2726,7 +2851,7 @@ Route::add_aux_send (boost::shared_ptr<Route> route, boost::shared_ptr<Processor
                {
                        Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
                        boost::shared_ptr<Pannable> sendpan (new Pannable (_session));
-                       listener.reset (new InternalSend (_session, sendpan, _mute_master, route, Delivery::Aux));
+                       listener.reset (new InternalSend (_session, sendpan, _mute_master, boost::dynamic_pointer_cast<ARDOUR::Route>(shared_from_this()), route, Delivery::Aux));
                }
 
                add_processor (listener, before);
@@ -3192,15 +3317,24 @@ framecnt_t
 Route::update_signal_latency ()
 {
        framecnt_t l = _output->user_latency();
+       framecnt_t lamp = 0;
+       bool before_amp = true;
 
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if ((*i)->active ()) {
                        l += (*i)->signal_latency ();
                }
+               if ((*i) == _amp) {
+                       before_amp = false;
+               }
+               if (before_amp) {
+                       lamp = l;
+               }
        }
 
        DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal signal latency = %2\n", _name, l));
 
+       _signal_latency_at_amp_position = lamp;
        if (_signal_latency != l) {
                _signal_latency = l;
                signal_latency_changed (); /* EMIT SIGNAL */
@@ -3429,14 +3563,14 @@ Route::save_as_template (const string& path, const string& name)
 bool
 Route::set_name (const string& str)
 {
-       bool ret;
-       string ioproc_name;
-       string name;
+       if (str == name()) {
+               return true;
+       }
 
-       name = Route::ensure_track_or_route_name (str, _session);
+       string name = Route::ensure_track_or_route_name (str, _session);
        SessionObject::set_name (name);
 
-       ret = (_input->set_name(name) && _output->set_name(name));
+       bool ret = (_input->set_name(name) && _output->set_name(name));
 
        if (ret) {
                /* rename the main outs. Leave other IO processors
@@ -3561,6 +3695,10 @@ Route::denormal_protection () const
 void
 Route::set_active (bool yn, void* src)
 {
+       if (_session.transport_rolling()) {
+               return;
+       }
+
        if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_route_active()) {
                _route_group->foreach_route (boost::bind (&Route::set_active, _1, yn, _route_group));
                return;
@@ -4003,6 +4141,10 @@ Route::setup_invisible_processors ()
                }
        }
 
+       if (!is_master() && !is_monitor() && !is_auditioner()) {
+               new_processors.push_front (_delayline);
+       }
+
        /* MONITOR CONTROL */
 
        if (_monitor_control && is_monitor ()) {
@@ -4159,6 +4301,10 @@ Route::non_realtime_locate (framepos_t pos)
                _pannable->transport_located (pos);
        }
 
+       if (_delayline.get()) {
+               _delayline.get()->flush();
+       }
+
        {
                //Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
index 71cab468794bd26e576cc8f2605022d21bcb3176..87aadf4193ce339b8f5749829111b8f29785b59f 100644 (file)
 #include "pbd/boost_debug.h"
 
 #include "ardour/amp.h"
-#include "ardour/send.h"
-#include "ardour/session.h"
 #include "ardour/buffer_set.h"
-#include "ardour/meter.h"
+#include "ardour/debug.h"
 #include "ardour/io.h"
+#include "ardour/meter.h"
 #include "ardour/panner_shell.h"
+#include "ardour/send.h"
+#include "ardour/session.h"
 
 #include "i18n.h"
 
@@ -73,6 +74,8 @@ Send::name_and_id_new_send (Session& s, Role r, uint32_t& bitslot, bool ignore_b
 Send::Send (Session& s, boost::shared_ptr<Pannable> p, boost::shared_ptr<MuteMaster> mm, Role r, bool ignore_bitslot)
        : Delivery (s, p, mm, name_and_id_new_send (s, r, _bitslot, ignore_bitslot), r)
        , _metering (false)
+       , _delay_in (0)
+       , _delay_out (0)
 {
        if (_role == Listen) {
                /* we don't need to do this but it keeps things looking clean
@@ -86,6 +89,8 @@ Send::Send (Session& s, boost::shared_ptr<Pannable> p, boost::shared_ptr<MuteMas
        _amp.reset (new Amp (_session));
        _meter.reset (new PeakMeter (_session, name()));
 
+       _delayline.reset (new DelayLine (_session, name()));
+
        add_control (_amp->gain_control ());
 
        if (panner_shell()) {
@@ -117,6 +122,35 @@ Send::deactivate ()
        Processor::deactivate ();
 }
 
+void
+Send::set_delay_in(framecnt_t delay)
+{
+       if (!_delayline) return;
+       if (_delay_in == delay) {
+               return;
+       }
+       _delay_in = delay;
+
+       DEBUG_TRACE (DEBUG::LatencyCompensation,
+                       string_compose ("Send::set_delay_in(%1) + %2 = %3\n",
+                               delay, _delay_out, _delay_out + _delay_in));
+       _delayline.get()->set_delay(_delay_out + _delay_in);
+}
+
+void
+Send::set_delay_out(framecnt_t delay)
+{
+       if (!_delayline) return;
+       if (_delay_out == delay) {
+               return;
+       }
+       _delay_out = delay;
+       DEBUG_TRACE (DEBUG::LatencyCompensation,
+                       string_compose ("Send::set_delay_out(%1) + %2 = %3\n",
+                               delay, _delay_in, _delay_out + _delay_in));
+       _delayline.get()->set_delay(_delay_out + _delay_in);
+}
+
 void
 Send::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes, bool)
 {
@@ -146,6 +180,8 @@ Send::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pframe
        _amp->setup_gain_automation (start_frame, end_frame, nframes);
        _amp->run (sendbufs, start_frame, end_frame, nframes, true);
 
+       _delayline->run (sendbufs, start_frame, end_frame, nframes, true);
+
        /* deliver to outputs */
 
        Delivery::run (sendbufs, start_frame, end_frame, nframes, true);
@@ -301,6 +337,11 @@ Send::configure_io (ChanCount in, ChanCount out)
                return false;
        }
 
+       if (_delayline && !_delayline->configure_io(in, out)) {
+               cerr << "send delayline config failed\n";
+               return false;
+       }
+
        reset_panner ();
 
        return true;
index 05fa883a9d3b02343d8cec6553ad1f1893021e8d..ae39d7c46841959ee6fcc7243590c09d9d419f0e 100644 (file)
@@ -36,9 +36,9 @@
 
 #include <boost/algorithm/string/erase.hpp>
 
+#include "pbd/convert.h"
 #include "pbd/error.h"
 #include "pbd/boost_debug.h"
-#include "pbd/pathscanner.h"
 #include "pbd/stl_delete.h"
 #include "pbd/basename.h"
 #include "pbd/stacktrace.h"
@@ -82,6 +82,7 @@
 #include "ardour/region_factory.h"
 #include "ardour/route_graph.h"
 #include "ardour/route_group.h"
+#include "ardour/route_sorters.h"
 #include "ardour/send.h"
 #include "ardour/session.h"
 #include "ardour/session_directory.h"
@@ -125,6 +126,7 @@ PBD::Signal0<void> Session::FeedbackDetected;
 PBD::Signal0<void> Session::SuccessfulGraphSort;
 PBD::Signal2<void,std::string,std::string> Session::VersionMismatch;
 
+const framecnt_t Session::bounce_chunk_size = 65536;
 static void clean_up_session_event (SessionEvent* ev) { delete ev; }
 const SessionEvent::RTeventCallback Session::rt_cleanup (clean_up_session_event);
 
@@ -137,6 +139,7 @@ Session::Session (AudioEngine &eng,
        : playlists (new SessionPlaylists)
        , _engine (eng)
        , process_function (&Session::process_with_events)
+       , _bounce_processing_active (false)
        , waiting_for_sync_offset (false)
        , _base_frame_rate (0)
        , _current_frame_rate (0)
@@ -192,6 +195,8 @@ Session::Session (AudioEngine &eng,
        , state_tree (0)
        , state_was_pending (false)
        , _state_of_the_state (StateOfTheState(CannotSave|InitialConnecting|Loading))
+       , _suspend_save (0)
+       , _save_queued (false)
        , _last_roll_location (0)
        , _last_roll_or_reversal_location (0)
        , _last_record_location (0)
@@ -233,6 +238,7 @@ Session::Session (AudioEngine &eng,
        , routes (new RouteList)
        , _adding_routes_in_progress (false)
        , destructive_index (0)
+       , _track_number_decimals(1)
        , solo_update_disabled (false)
        , default_fade_steepness (0)
        , default_fade_msecs (0)
@@ -295,6 +301,9 @@ Session::Session (AudioEngine &eng,
                        throw failed_constructor ();
                }
 
+               /* load default session properties - if any */
+               config.load_state();
+
        } else {
 
                if (load_state (_current_snapshot_name)) {
@@ -492,6 +501,14 @@ Session::destroy ()
 
        clear_clicks ();
 
+       /* need to remove auditioner before monitoring section
+        * otherwise it is re-connected */
+       auditioner.reset ();
+
+       /* drop references to routes held by the monitoring section
+        * specifically _monitor_out aux/listen references */
+       remove_monitor_section();
+
        /* clear out any pending dead wood from RCU managed objects */
 
        routes.flush ();
@@ -511,7 +528,6 @@ Session::destroy ()
 
        /* reset these three references to special routes before we do the usual route delete thing */
 
-       auditioner.reset ();
        _master_out.reset ();
        _monitor_out.reset ();
 
@@ -1877,6 +1893,7 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost:
 
   failed:
        if (!new_routes.empty()) {
+               StateProtector sp (this);
                add_routes (new_routes, true, true, true);
 
                if (instrument) {
@@ -2118,6 +2135,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
 
   failed:
        if (!new_routes.empty()) {
+               StateProtector sp (this);
                add_routes (new_routes, true, true, true);
        }
 
@@ -2203,6 +2221,7 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r
 
   failure:
        if (!ret.empty()) {
+               StateProtector sp (this);
                add_routes (ret, false, true, true); // autoconnect outputs only
        }
 
@@ -2319,6 +2338,7 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template
 
   out:
        if (!ret.empty()) {
+               StateProtector sp (this);
                add_routes (ret, true, true, true);
                IO::enable_connecting ();
        }
@@ -2348,6 +2368,8 @@ Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output
                save_state (_current_snapshot_name);
        }
        
+       reassign_track_numbers();
+
        RouteAdded (new_routes); /* EMIT SIGNAL */
 }
 
@@ -2589,6 +2611,13 @@ Session::remove_route (boost::shared_ptr<Route> route)
                }
        }
 
+       /* if the monitoring section had a pointer to this route, remove it */
+       if (_monitor_out && !route->is_master() && !route->is_monitor()) {
+               Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               PBD::Unwinder<bool> uw (ignore_route_processor_changes, true);
+               route->remove_aux_or_listen (_monitor_out);
+       }
+
        boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
        if (mt && mt->step_editing()) {
                if (_step_editors > 0) {
@@ -2625,6 +2654,7 @@ Session::remove_route (boost::shared_ptr<Route> route)
        if (save_state (_current_snapshot_name)) {
                save_history (_current_snapshot_name);
        }
+       reassign_track_numbers();
 }
 
 void
@@ -3041,6 +3071,42 @@ Session::route_by_remote_id (uint32_t id)
        return boost::shared_ptr<Route> ((Route*) 0);
 }
 
+
+void
+Session::reassign_track_numbers ()
+{
+       int64_t tn = 0;
+       int64_t bn = 0;
+       RouteList r (*(routes.reader ()));
+       SignalOrderRouteSorter sorter;
+       r.sort (sorter);
+
+       StateProtector sp (this);
+
+       for (RouteList::iterator i = r.begin(); i != r.end(); ++i) {
+               if (boost::dynamic_pointer_cast<Track> (*i)) {
+                       (*i)->set_track_number(++tn);
+               }
+               else if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_auditioner()) {
+                       (*i)->set_track_number(--bn);
+               }
+       }
+       const uint32_t decimals = ceilf (log10f (tn + 1));
+       const bool decimals_changed = _track_number_decimals != decimals;
+       _track_number_decimals = decimals;
+
+       if (decimals_changed && config.get_track_name_number ()) {
+               for (RouteList::iterator i = r.begin(); i != r.end(); ++i) {
+                       boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (*i);
+                       if (t) {
+                               t->resync_track_name();
+                       }
+               }
+               // trigger GUI re-layout
+               config.ParameterChanged("track-name-number");
+       }
+}
+
 void
 Session::playlist_region_added (boost::weak_ptr<Region> w)
 {
@@ -3309,7 +3375,7 @@ Session::source_by_id (const PBD::ID& id)
 }
 
 boost::shared_ptr<AudioFileSource>
-Session::source_by_path_and_channel (const string& path, uint16_t chn) const
+Session::audio_source_by_path_and_channel (const string& path, uint16_t chn) const
 {
        /* Restricted to audio files because only audio sources have channel
           as a property.
@@ -3330,7 +3396,7 @@ Session::source_by_path_and_channel (const string& path, uint16_t chn) const
 }
 
 boost::shared_ptr<MidiSource>
-Session::source_by_path (const std::string& path) const
+Session::midi_source_by_path (const std::string& path) const
 {
        /* Restricted to MIDI files because audio sources require a channel
           for unique identification, in addition to a path.
@@ -3370,30 +3436,6 @@ Session::count_sources_by_origin (const string& path)
        return cnt;
 }
 
-/** Return the full path (in some session directory) for a new within-session source.
- * \a name must be a session-unique name that does not contain slashes
- *         (e.g. as returned by new_*_source_name)
- */
-string
-Session::new_source_path_from_name (DataType type, const string& name)
-{
-       assert(name.find("/") == string::npos);
-
-       SessionDirectory sdir(get_best_session_directory_for_new_source());
-
-       std::string p;
-       if (type == DataType::AUDIO) {
-               p = sdir.sound_path();
-       } else if (type == DataType::MIDI) {
-               p = sdir.midi_path();
-       } else {
-               error << "Unknown source type, unable to create file path" << endmsg;
-               return "";
-       }
-
-       return Glib::build_filename (p, name);
-}
-
 string
 Session::peak_path (string base) const
 {
@@ -3402,18 +3444,20 @@ Session::peak_path (string base) const
 
 /** Return a unique name based on \a base for a new internal audio source */
 string
-Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t chan, bool destructive)
+Session::new_audio_source_path (const string& base, uint32_t nchan, uint32_t chan, bool destructive, bool take_required)
 {
        uint32_t cnt;
-       char buf[PATH_MAX+1];
-       const uint32_t limit = 10000;
+       string possible_name;
+       const uint32_t limit = 9999; // arbitrary limit on number of files with the same basic name
        string legalized;
        string ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO);
+       bool some_related_source_name_exists = false;
 
-       buf[0] = '\0';
+       possible_name[0] = '\0';
        legalized = legalize_for_path (base);
 
        // Find a "version" of the base name that doesn't exist in any of the possible directories.
+
        for (cnt = (destructive ? ++destructive_index : 1); cnt <= limit; ++cnt) {
 
                vector<space_and_path>::iterator i;
@@ -3421,47 +3465,37 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha
 
                for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
 
-                       if (destructive) {
-
-                               if (nchan < 2) {
-                                       snprintf (buf, sizeof(buf), "T%04d-%s%s",
-                                                 cnt, legalized.c_str(), ext.c_str());
-                               } else if (nchan == 2) {
-                                       if (chan == 0) {
-                                               snprintf (buf, sizeof(buf), "T%04d-%s%%L%s",
-                                                         cnt, legalized.c_str(), ext.c_str());
-                                       } else {
-                                               snprintf (buf, sizeof(buf), "T%04d-%s%%R%s",
-                                                         cnt, legalized.c_str(), ext.c_str());
-                                       }
-                               } else if (nchan < 26) {
-                                       snprintf (buf, sizeof(buf), "T%04d-%s%%%c%s",
-                                                 cnt, legalized.c_str(), 'a' + chan, ext.c_str());
-                               } else {
-                                       snprintf (buf, sizeof(buf), "T%04d-%s%s",
-                                                 cnt, legalized.c_str(), ext.c_str());
-                               }
+                       ostringstream sstr;
 
+                       if (destructive) {
+                               sstr << 'T';
+                               sstr << setfill ('0') << setw (4) << cnt;
+                               sstr << legalized;
                        } else {
-
-                               if (nchan < 2) {
-                                       snprintf (buf, sizeof(buf), "%s-%u%s", legalized.c_str(), cnt, ext.c_str());
-                               } else if (nchan == 2) {
-                                       if (chan == 0) {
-                                               snprintf (buf, sizeof(buf), "%s-%u%%L%s", legalized.c_str(), cnt, ext.c_str());
-                                       } else {
-                                               snprintf (buf, sizeof(buf), "%s-%u%%R%s", legalized.c_str(), cnt, ext.c_str());
-                                       }
-                               } else if (nchan < 26) {
-                                       snprintf (buf, sizeof(buf), "%s-%u%%%c%s", legalized.c_str(), cnt, 'a' + chan, ext.c_str());
-                               } else {
-                                       snprintf (buf, sizeof(buf), "%s-%u%s", legalized.c_str(), cnt, ext.c_str());
+                               sstr << legalized;
+                               
+                               if (take_required || some_related_source_name_exists) {
+                                       sstr << '-';
+                                       sstr << cnt;
                                }
                        }
+                       
+                       if (nchan == 2) {
+                               if (chan == 0) {
+                                       sstr << "%L";
+                               } else {
+                                       sstr << "%R";
+                               }
+                       } else if (nchan > 2 && nchan < 26) {
+                               sstr << '%';
+                               sstr << 'a' + chan;
+                       } 
 
-                       SessionDirectory sdir((*i).path);
+                       sstr << ext;
 
-                       string spath = sdir.sound_path();
+                       possible_name = sstr.str();
+                       SessionDirectory sdir((*i).path);
+                       const string spath = sdir.sound_path();
 
                        /* note that we search *without* the extension so that
                           we don't end up both "Audio 1-1.wav" and "Audio 1-1.caf"
@@ -3469,7 +3503,7 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha
                           a file format change.
                        */
 
-                       if (matching_unsuffixed_filename_exists_in (spath, buf)) {
+                       if (matching_unsuffixed_filename_exists_in (spath, possible_name)) {
                                existing++;
                                break;
                        }
@@ -3483,9 +3517,9 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha
                         * notions of their removability.
                         */
 
-                       string possible_path = Glib::build_filename (spath, buf);
+                       string possible_path = Glib::build_filename (spath, possible_name);
 
-                       if (source_by_path (possible_path)) {
+                       if (audio_source_by_path_and_channel (possible_path, chan)) {
                                existing++;
                                break;
                        }
@@ -3495,6 +3529,8 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha
                        break;
                }
 
+               some_related_source_name_exists = true;
+
                if (cnt > limit) {
                        error << string_compose(
                                        _("There are already %1 recordings for %2, which I consider too many."),
@@ -3504,32 +3540,31 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha
                }
        }
 
-       return Glib::path_get_basename (buf);
-}
+       /* We've established that the new name does not exist in any session
+        * directory, so now find out which one we should use for this new
+        * audio source.
+        */
 
-/** Create a new within-session audio source */
-boost::shared_ptr<AudioFileSource>
-Session::create_audio_source_for_session (size_t n_chans, string const & n, uint32_t chan, bool destructive)
-{
-       const string name    = new_audio_source_name (n, n_chans, chan, destructive);
-       const string path    = new_source_path_from_name(DataType::AUDIO, name);
+       SessionDirectory sdir (get_best_session_directory_for_new_audio());
 
-       return boost::dynamic_pointer_cast<AudioFileSource> (
-               SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate()));
+       std::string s = Glib::build_filename (sdir.sound_path(), possible_name);
+
+       return s;
 }
 
 /** Return a unique name based on \a owner_name for a new internal MIDI source */
 string
-Session::new_midi_source_name (const string& owner_name)
+Session::new_midi_source_path (const string& base)
 {
        uint32_t cnt;
        char buf[PATH_MAX+1];
        const uint32_t limit = 10000;
        string legalized;
+       string possible_path;
        string possible_name;
 
        buf[0] = '\0';
-       legalized = legalize_for_path (owner_name);
+       legalized = legalize_for_path (base);
 
        // Find a "version" of the file name that doesn't exist in any of the possible directories.
 
@@ -3537,7 +3572,7 @@ Session::new_midi_source_name (const string& owner_name)
 
                vector<space_and_path>::iterator i;
                uint32_t existing = 0;
-
+               
                for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
 
                        SessionDirectory sdir((*i).path);
@@ -3545,13 +3580,13 @@ Session::new_midi_source_name (const string& owner_name)
                        snprintf (buf, sizeof(buf), "%s-%u.mid", legalized.c_str(), cnt);
                        possible_name = buf;
 
-                       std::string possible_path = Glib::build_filename (sdir.midi_path(), possible_name);
+                       possible_path = Glib::build_filename (sdir.midi_path(), possible_name);
                        
                        if (Glib::file_test (possible_path, Glib::FILE_TEST_EXISTS)) {
                                existing++;
                        }
 
-                       if (source_by_path (possible_path)) {
+                       if (midi_source_by_path (possible_path)) {
                                existing++;
                        }
                }
@@ -3563,31 +3598,47 @@ Session::new_midi_source_name (const string& owner_name)
                if (cnt > limit) {
                        error << string_compose(
                                        _("There are already %1 recordings for %2, which I consider too many."),
-                                       limit, owner_name) << endmsg;
+                                       limit, base) << endmsg;
                        destroy ();
-                       throw failed_constructor();
+                       return 0;
                }
        }
 
-       return possible_name;
+       /* No need to "find best location" for software/app-based RAID, because
+          MIDI is so small that we always put it in the same place.
+       */
+
+       return possible_path;
 }
 
 
+/** Create a new within-session audio source */
+boost::shared_ptr<AudioFileSource>
+Session::create_audio_source_for_session (size_t n_chans, string const & base, uint32_t chan, bool destructive)
+{
+       const string path = new_audio_source_path (base, n_chans, chan, destructive, true);
+
+       if (!path.empty()) {
+               return boost::dynamic_pointer_cast<AudioFileSource> (
+                       SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate()));
+       } else {
+               throw failed_constructor ();
+       }
+}
+
 /** Create a new within-session MIDI source */
 boost::shared_ptr<MidiSource>
 Session::create_midi_source_for_session (string const & basic_name)
 {
-       std::string name;
-
-       if (name.empty()) {
-               name = new_midi_source_name (basic_name);
+       const string path = new_midi_source_path (basic_name);
+       
+       if (!path.empty()) {
+               return boost::dynamic_pointer_cast<SMFSource> (
+                       SourceFactory::createWritable (
+                               DataType::MIDI, *this, path, false, frame_rate()));
+       } else {
+               throw failed_constructor ();
        }
-
-       const string path = new_source_path_from_name (DataType::MIDI, name);
-
-       return boost::dynamic_pointer_cast<SMFSource> (
-               SourceFactory::createWritable (
-                       DataType::MIDI, *this, path, false, frame_rate()));
 }
 
 /** Create a new within-session MIDI source */
@@ -3621,7 +3672,7 @@ Session::create_midi_source_by_stealing_name (boost::shared_ptr<Track> track)
                return boost::shared_ptr<MidiSource>();
        }
 
-       const string path = new_source_path_from_name (DataType::MIDI, name);
+       const string path = new_midi_source_path (name);
 
        return boost::dynamic_pointer_cast<SMFSource> (
                SourceFactory::createWritable (
@@ -3699,6 +3750,9 @@ Session::audition_region (boost::shared_ptr<Region> r)
 void
 Session::cancel_audition ()
 {
+       if (!auditioner) {
+               return;
+       }
        if (auditioner->auditioning()) {
                auditioner->cancel_audition ();
                AuditionActive (false); /* EMIT SIGNAL */
@@ -3881,7 +3935,7 @@ Session::update_locations_after_tempo_map_change (Locations::LocationList& loc)
 void
 Session::ensure_buffers (ChanCount howmany)
 {
-       BufferManager::ensure_buffers (howmany);
+       BufferManager::ensure_buffers (howmany, bounce_processing() ? bounce_chunk_size : 0);
 }
 
 void
@@ -4122,24 +4176,22 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end,
                          bool /*overwrite*/, vector<boost::shared_ptr<Source> >& srcs,
                          InterThreadInfo& itt, 
                          boost::shared_ptr<Processor> endpoint, bool include_endpoint,
-                         bool for_export)
+                         bool for_export, bool for_freeze)
 {
        boost::shared_ptr<Region> result;
        boost::shared_ptr<Playlist> playlist;
        boost::shared_ptr<AudioFileSource> fsource;
-       uint32_t x;
-       char buf[PATH_MAX+1];
        ChanCount diskstream_channels (track.n_channels());
        framepos_t position;
        framecnt_t this_chunk;
        framepos_t to_do;
+       framepos_t latency_skip;
        BufferSet buffers;
-       SessionDirectory sdir(get_best_session_directory_for_new_source ());
-       const string sound_dir = sdir.sound_path();
        framepos_t len = end - start;
        bool need_block_size_reset = false;
-       string ext;
        ChanCount const max_proc = track.max_processor_streams ();
+       string legal_playlist_name;
+       string possible_path;
 
        if (end <= start) {
                error << string_compose (_("Cannot write a range where end <= start (e.g. %1 <= %2)"),
@@ -4147,41 +4199,52 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end,
                return result;
        }
 
-       const framecnt_t chunk_size = (256 * 1024)/4;
+       diskstream_channels = track.bounce_get_output_streams (diskstream_channels, endpoint,
+                       include_endpoint, for_export, for_freeze);
+
+       if (diskstream_channels.n_audio() < 1) {
+               error << _("Cannot write a range with no audio.") << endmsg;
+               return result;
+       }
 
        // block all process callback handling
 
        block_processing ();
 
+       {
+               // synchronize with AudioEngine::process_callback()
+               // make sure processing is not currently running
+               // and processing_blocked() is honored before
+               // acquiring thread buffers
+               Glib::Threads::Mutex::Lock lm (_engine.process_lock());
+       }
+
+       _bounce_processing_active = true;
+
        /* call tree *MUST* hold route_lock */
 
        if ((playlist = track.playlist()) == 0) {
                goto out;
        }
 
-       ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO);
+       legal_playlist_name = legalize_for_path (playlist->name());
 
        for (uint32_t chan_n = 0; chan_n < diskstream_channels.n_audio(); ++chan_n) {
 
-               for (x = 0; x < 99999; ++x) {
-                       snprintf (buf, sizeof(buf), "%s/%s-%d-bounce-%" PRIu32 "%s", sound_dir.c_str(), playlist->name().c_str(), chan_n, x+1, ext.c_str());
-                       if (!Glib::file_test (buf, Glib::FILE_TEST_EXISTS)) {
-                               break;
-                       }
-               }
-
-               if (x == 99999) {
-                       error << string_compose (_("too many bounced versions of playlist \"%1\""), playlist->name()) << endmsg;
+               string base_name = string_compose ("%1-%2-bounce", playlist->name(), chan_n);
+               string path = new_audio_source_path (legal_playlist_name, diskstream_channels.n_audio(), chan_n, false, true);
+               
+               if (path.empty()) {
                        goto out;
                }
 
                try {
                        fsource = boost::dynamic_pointer_cast<AudioFileSource> (
-                               SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate()));
+                               SourceFactory::createWritable (DataType::AUDIO, *this, path, false, frame_rate()));
                }
 
                catch (failed_constructor& err) {
-                       error << string_compose (_("cannot create new audio file \"%1\" for %2"), buf, track.name()) << endmsg;
+                       error << string_compose (_("cannot create new audio file \"%1\" for %2"), path, track.name()) << endmsg;
                        goto out;
                }
 
@@ -4194,13 +4257,17 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end,
         */
 
        need_block_size_reset = true;
-       track.set_block_size (chunk_size);
+       track.set_block_size (bounce_chunk_size);
+       _engine.main_thread()->get_buffers ();
 
        position = start;
        to_do = len;
+       latency_skip = track.bounce_get_latency (endpoint, include_endpoint, for_export, for_freeze);
 
        /* create a set of reasonably-sized buffers */
-       buffers.ensure_buffers (DataType::AUDIO, max_proc.n_audio(), chunk_size);
+       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+               buffers.ensure_buffers(*t, max_proc.get(*t), bounce_chunk_size);
+       }
        buffers.set_count (max_proc);
 
        for (vector<boost::shared_ptr<Source> >::iterator src = srcs.begin(); src != srcs.end(); ++src) {
@@ -4211,28 +4278,56 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end,
 
        while (to_do && !itt.cancel) {
 
-               this_chunk = min (to_do, chunk_size);
+               this_chunk = min (to_do, bounce_chunk_size);
 
-               if (track.export_stuff (buffers, start, this_chunk, endpoint, include_endpoint, for_export)) {
+               if (track.export_stuff (buffers, start, this_chunk, endpoint, include_endpoint, for_export, for_freeze)) {
                        goto out;
                }
 
+               start += this_chunk;
+               to_do -= this_chunk;
+               itt.progress = (float) (1.0 - ((double) to_do / len));
+
+               if (latency_skip >= bounce_chunk_size) {
+                       latency_skip -= bounce_chunk_size;
+                       continue;
+               }
+
+               const framecnt_t current_chunk = this_chunk - latency_skip;
+
                uint32_t n = 0;
                for (vector<boost::shared_ptr<Source> >::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) {
                        boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
 
                        if (afs) {
-                               if (afs->write (buffers.get_audio(n).data(), this_chunk) != this_chunk) {
+                               if (afs->write (buffers.get_audio(n).data(latency_skip), current_chunk) != current_chunk) {
                                        goto out;
                                }
                        }
                }
+               latency_skip = 0;
+       }
 
-               start += this_chunk;
-               to_do -= this_chunk;
+       /* post-roll, pick up delayed processor output */
+       latency_skip = track.bounce_get_latency (endpoint, include_endpoint, for_export, for_freeze);
 
-               itt.progress = (float) (1.0 - ((double) to_do / len));
+       while (latency_skip && !itt.cancel) {
+               this_chunk = min (latency_skip, bounce_chunk_size);
+               latency_skip -= this_chunk;
+
+               buffers.silence (this_chunk, 0);
+               track.bounce_process (buffers, start, this_chunk, endpoint, include_endpoint, for_export, for_freeze);
+
+               uint32_t n = 0;
+               for (vector<boost::shared_ptr<Source> >::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) {
+                       boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
 
+                       if (afs) {
+                               if (afs->write (buffers.get_audio(n).data(), this_chunk) != this_chunk) {
+                                       goto out;
+                               }
+                       }
+               }
        }
 
        if (!itt.cancel) {
@@ -4284,8 +4379,10 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end,
                }
        }
 
+       _bounce_processing_active = false;
 
        if (need_block_size_reset) {
+               _engine.main_thread()->drop_buffers ();
                track.set_block_size (get_block_size());
        }
 
@@ -4949,6 +5046,8 @@ Session::sync_order_keys ()
 
        DEBUG_TRACE (DEBUG::OrderKeys, "Sync Order Keys.\n");
 
+       reassign_track_numbers();
+
        Route::SyncOrderKeys (); /* EMIT SIGNAL */
 
        DEBUG_TRACE (DEBUG::OrderKeys, "\tsync done\n");
index 0cfdb52872ed37946d975fd3682d1120e8972c2a..f9f43ba4b2b0b5fadaaf0af3cd2c9f95e1dbcb5c 100644 (file)
 
 */
 
+#include <glib.h>
+#include <glib/gstdio.h> /* for g_stat() */
+#include <glibmm/miscutils.h> /* for build_filename() */
+
+#include "pbd/file_utils.h"
 #include "pbd/pathexpand.h"
 
 #include "ardour/types.h"
+#include "ardour/filesystem_paths.h"
 #include "ardour/session_configuration.h"
 #include "i18n.h"
 
@@ -122,3 +128,67 @@ SessionConfiguration::map_parameters (boost::function<void (std::string)>& funct
 #undef  CONFIG_VARIABLE
 #undef  CONFIG_VARIABLE_SPECIAL
 }
+
+
+bool
+SessionConfiguration::load_state ()
+{
+       std::string rcfile;
+       GStatBuf statbuf;
+       if (find_file (ardour_config_search_path(), "session.rc", rcfile)) {
+               if (g_stat (rcfile.c_str(), &statbuf)) {
+                       return false;
+               }
+               if (statbuf.st_size == 0) {
+                       return false;
+               }
+               XMLTree tree;
+               if (!tree.read (rcfile.c_str())) {
+                       error << string_compose(_("%1: cannot part default session options \"%2\""), PROGRAM_NAME, rcfile) << endmsg;
+                       return false;
+               }
+
+               XMLNode& root (*tree.root());
+               if (root.name() != X_("SessionDefaults")) {
+                       warning << _("Invalid session default XML Root.") << endmsg;
+                       return false;
+               }
+
+               XMLNode* node;
+               if (((node = find_named_node (root, X_("Config"))) != 0)) {
+                       LocaleGuard lg (X_("POSIX"));
+                       set_variables(*node);
+                       info << _("Loaded custom session defaults.") << endmsg;
+               } else {
+                       warning << _("Found no session defaults in XML file.") << endmsg;
+                       return false;
+               }
+
+               /* CUSTOM OVERRIDES */
+               set_audio_search_path("");
+               set_midi_search_path("");
+               set_raid_path("");
+       }
+       return true;
+}
+
+bool
+SessionConfiguration::save_state ()
+{
+       const std::string rcfile = Glib::build_filename (user_config_directory(), "session.rc");
+       if (rcfile.empty()) {
+               return false;
+       }
+
+       XMLTree tree;
+       XMLNode* root = new XMLNode(X_("SessionDefaults"));
+       root->add_child_nocopy (get_variables ());
+       tree.set_root (root);
+
+       if (!tree.write (rcfile.c_str())) {
+               error << _("Could not save session options") << endmsg;
+               return false;
+       }
+
+       return true;
+}
index 93df1fc9466e16ed98c348c4d5f1da4855fee560..639ea399d317a9983c5b456b75f2de393ce1d19a 100644 (file)
@@ -417,7 +417,7 @@ Session::send_full_time_code (framepos_t const t, MIDI::pframes_t nframes)
        msg[4] = 0x1;
        msg[9] = 0xf7;
 
-       msg[5] = mtc_timecode_bits | timecode.hours;
+       msg[5] = mtc_timecode_bits | (timecode.hours % 24);
        msg[6] = timecode.minutes;
        msg[7] = timecode.seconds;
        msg[8] = timecode.frames;
index 6a06863e9e16b1ca347cd261df2fe3e374d861ff..eaf9f08b257693702e57771d618fcc9720b17cfb 100644 (file)
 #include "pbd/error.h"
 #include "pbd/file_utils.h"
 #include "pbd/pathexpand.h"
-#include "pbd/pathscanner.h"
 #include "pbd/pthread_utils.h"
 #include "pbd/stacktrace.h"
 #include "pbd/convert.h"
-#include "pbd/clear_dir.h"
 #include "pbd/localtime_r.h"
 
 #include "ardour/amp.h"
@@ -671,6 +669,12 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot
                return 1;
        }
 
+       if (g_atomic_int_get(&_suspend_save)) {
+               _save_queued = true;
+               return 1;
+       }
+       _save_queued = false;
+
        if (!_engine.connected ()) {
                error << string_compose (_("the %1 audio engine is not connected and state saving would lose all I/O connections. Session not saved"),
                                          PROGRAM_NAME)
@@ -875,6 +879,12 @@ Session::load_options (const XMLNode& node)
        return 0;
 }
 
+bool
+Session::save_default_options ()
+{
+       return config.save_state();
+}
+
 XMLNode&
 Session::get_state()
 {
@@ -1421,7 +1431,13 @@ Session::XMLRouteFactory (const XMLNode& node, int version)
                 ret = track;
 
        } else {
-               boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML")));
+               enum Route::Flag flags = Route::Flag(0);
+               const XMLProperty* prop = node.property("flags");
+               if (prop) {
+                       flags = Route::Flag (string_2_enum (prop->value(), flags));
+               }
+
+               boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML"), flags));
 
                 if (r->init () == 0 && r->set_state (node, version) == 0) {
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
@@ -1493,7 +1509,13 @@ Session::XMLRouteFactory_2X (const XMLNode& node, int version)
                 ret = track;
 
        } else {
-               boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML")));
+               enum Route::Flag flags = Route::Flag(0);
+               const XMLProperty* prop = node.property("flags");
+               if (prop) {
+                       flags = Route::Flag (string_2_enum (prop->value(), flags));
+               }
+
+               boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML"), flags));
 
                 if (r->init () == 0 && r->set_state (node, version) == 0) {
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
@@ -1828,41 +1850,6 @@ Session::get_sources_as_xml ()
        return *node;
 }
 
-string
-Session::path_from_region_name (DataType type, string name, string identifier)
-{
-       char buf[PATH_MAX+1];
-       uint32_t n;
-       SessionDirectory sdir(get_best_session_directory_for_new_source());
-       std::string source_dir = ((type == DataType::AUDIO)
-               ? sdir.sound_path() : sdir.midi_path());
-
-        string ext = native_header_format_extension (config.get_native_file_header_format(), type);
-
-       for (n = 0; n < 999999; ++n) {
-               if (identifier.length()) {
-                       snprintf (buf, sizeof(buf), "%s%s%" PRIu32 "%s", name.c_str(),
-                                 identifier.c_str(), n, ext.c_str());
-               } else {
-                       snprintf (buf, sizeof(buf), "%s-%" PRIu32 "%s", name.c_str(),
-                                       n, ext.c_str());
-               }
-
-               std::string source_path = Glib::build_filename (source_dir, buf);
-
-               if (!Glib::file_test (source_path, Glib::FILE_TEST_EXISTS)) {
-                       return source_path;
-               }
-       }
-
-       error << string_compose (_("cannot create new file from region name \"%1\" with ident = \"%2\": too many existing files with similar names"),
-                                name, identifier)
-             << endmsg;
-
-       return "";
-}
-
-
 int
 Session::load_sources (const XMLNode& node)
 {
@@ -2099,7 +2086,7 @@ Session::refresh_disk_space ()
 }
 
 string
-Session::get_best_session_directory_for_new_source ()
+Session::get_best_session_directory_for_new_audio ()
 {
        vector<space_and_path>::iterator i;
        string result = _session_dir->root_path();
@@ -2287,16 +2274,10 @@ state_file_filter (const string &str, void* /*arg*/)
                str.find (statefile_suffix) == (str.length() - strlen (statefile_suffix)));
 }
 
-struct string_cmp {
-       bool operator()(const string* a, const string* b) {
-               return *a < *b;
-       }
-};
-
-static string*
-remove_end(string* state)
+static string
+remove_end(string state)
 {
-       string statename(*state);
+       string statename(state);
 
        string::size_type start,end;
        if ((start = statename.find_last_of (G_DIR_SEPARATOR)) != string::npos) {
@@ -2307,24 +2288,23 @@ remove_end(string* state)
                end = statename.length();
        }
 
-       return new string(statename.substr (0, end));
+       return string(statename.substr (0, end));
 }
 
-vector<string *> *
+vector<string>
 Session::possible_states (string path)
 {
-       PathScanner scanner;
-       vector<string*>* states = scanner (path, state_file_filter, 0, false, false);
+       vector<string> states;
+       find_files_matching_filter (states, path, state_file_filter, 0, false, false);
 
-       transform(states->begin(), states->end(), states->begin(), remove_end);
+       transform(states.begin(), states.end(), states.begin(), remove_end);
 
-       string_cmp cmp;
-       sort (states->begin(), states->end(), cmp);
+       sort (states.begin(), states.end());
 
        return states;
 }
 
-vector<string *> *
+vector<string>
 Session::possible_states () const
 {
        return possible_states(_path);
@@ -2548,8 +2528,7 @@ Session::find_all_sources (string path, set<string>& result)
 int
 Session::find_all_sources_across_snapshots (set<string>& result, bool exclude_this_snapshot)
 {
-       PathScanner scanner;
-       vector<string*>* state_files;
+       vector<string> state_files;
        string ripped;
        string this_snapshot_path;
 
@@ -2561,9 +2540,9 @@ Session::find_all_sources_across_snapshots (set<string>& result, bool exclude_th
                ripped = ripped.substr (0, ripped.length() - 1);
        }
 
-       state_files = scanner (ripped, accept_all_state_files, (void *) 0, true, true);
+       find_files_matching_filter (state_files, ripped, accept_all_state_files, (void *) 0, true, true);
 
-       if (state_files == 0) {
+       if (state_files.empty()) {
                /* impossible! */
                return 0;
        }
@@ -2572,13 +2551,13 @@ Session::find_all_sources_across_snapshots (set<string>& result, bool exclude_th
        this_snapshot_path += legalize_for_path (_current_snapshot_name);
        this_snapshot_path += statefile_suffix;
 
-       for (vector<string*>::iterator i = state_files->begin(); i != state_files->end(); ++i) {
+       for (vector<string>::iterator i = state_files.begin(); i != state_files.end(); ++i) {
 
-               if (exclude_this_snapshot && **i == this_snapshot_path) {
+               if (exclude_this_snapshot && *i == this_snapshot_path) {
                        continue;
                }
 
-               if (find_all_sources (**i, result) < 0) {
+               if (find_all_sources (*i, result) < 0) {
                        return -1;
                }
        }
@@ -2628,13 +2607,9 @@ Session::cleanup_sources (CleanupReport& rep)
        // FIXME: needs adaptation to midi
 
        vector<boost::shared_ptr<Source> > dead_sources;
-       PathScanner scanner;
        string audio_path;
        string midi_path;
-       vector<space_and_path>::iterator i;
-       vector<space_and_path>::iterator nexti;
-       vector<string*>* candidates;
-       vector<string*>* candidates2;
+       vector<string> candidates;
        vector<string> unused;
        set<string> all_sources;
        bool used;
@@ -2642,6 +2617,8 @@ Session::cleanup_sources (CleanupReport& rep)
        int ret = -1;
        string tmppath1;
        string tmppath2;
+       Searchpath asp;
+       Searchpath msp;
 
        _state_of_the_state = (StateOfTheState) (_state_of_the_state | InCleanup);
 
@@ -2683,54 +2660,23 @@ Session::cleanup_sources (CleanupReport& rep)
 
        /* build a list of all the possible audio directories for the session */
 
-       for (i = session_dirs.begin(); i != session_dirs.end(); ) {
-
-               nexti = i;
-               ++nexti;
-
+       for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
                SessionDirectory sdir ((*i).path);
-               audio_path += sdir.sound_path();
-
-               if (nexti != session_dirs.end()) {
-                       audio_path += G_SEARCHPATH_SEPARATOR;
-               }
-
-               i = nexti;
+               asp += sdir.sound_path();
        }
+       audio_path += asp.to_string();
 
 
        /* build a list of all the possible midi directories for the session */
 
-       for (i = session_dirs.begin(); i != session_dirs.end(); ) {
-
-               nexti = i;
-               ++nexti;
-
+       for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
                SessionDirectory sdir ((*i).path);
-               midi_path += sdir.midi_path();
-
-               if (nexti != session_dirs.end()) {
-                       midi_path += G_SEARCHPATH_SEPARATOR;
-               }
-
-               i = nexti;
+               msp += sdir.midi_path();
        }
+       midi_path += msp.to_string();
 
-       candidates = scanner (audio_path, accept_all_audio_files, (void *) 0, true, true);
-       candidates2 = scanner (midi_path, accept_all_midi_files, (void *) 0, true, true);
-
-        /* merge them */
-
-        if (candidates) {
-                if (candidates2) {
-                        for (vector<string*>::iterator i = candidates2->begin(); i != candidates2->end(); ++i) {
-                                candidates->push_back (*i);
-                        }
-                        delete candidates2;
-                }
-        } else {
-                candidates = candidates2; // might still be null
-        }
+       find_files_matching_filter (candidates, audio_path, accept_all_audio_files, (void *) 0, true, true);
+       find_files_matching_filter (candidates, midi_path, accept_all_midi_files, (void *) 0, true, true);
 
        /* find all sources, but don't use this snapshot because the
           state file on disk still references sources we may have already
@@ -2770,32 +2716,26 @@ Session::cleanup_sources (CleanupReport& rep)
                 i = tmp;
        }
 
-        if (candidates) {
-                for (vector<string*>::iterator x = candidates->begin(); x != candidates->end(); ++x) {
+       for (vector<string>::iterator x = candidates.begin(); x != candidates.end(); ++x) {
 
-                        used = false;
-                        spath = **x;
+               used = false;
+               spath = *x;
 
-                        for (set<string>::iterator i = all_sources.begin(); i != all_sources.end(); ++i) {
-
-                               tmppath1 = canonical_path (spath);
-                               tmppath2 = canonical_path ((*i));
-
-                               if (tmppath1 == tmppath2) {
-                                        used = true;
-                                        break;
-                                }
-                        }
+               for (set<string>::iterator i = all_sources.begin(); i != all_sources.end(); ++i) {
 
-                        if (!used) {
-                                unused.push_back (spath);
-                        }
+                       tmppath1 = canonical_path (spath);
+                       tmppath2 = canonical_path ((*i));
 
-                        delete *x;
-                }
+                       if (tmppath1 == tmppath2) {
+                               used = true;
+                               break;
+                       }
+               }
 
-                delete candidates;
-        }
+               if (!used) {
+                       unused.push_back (spath);
+               }
+       }
 
        /* now try to move all unused files into the "dead" directory(ies) */
 
index 13d65e5b3c404d42187905637269bd78a809bea6..5d5c9cc8df6eefd605da55d2c7fec17add13c7c4 100644 (file)
@@ -47,10 +47,8 @@ void
 get_state_files_in_directory (const std::string & directory_path,
                              vector<std::string> & result)
 {
-       Glib::PatternSpec state_file_pattern('*' + string(statefile_suffix));
-
-       find_matching_files_in_directory (directory_path, state_file_pattern,
-                       result);
+       find_files_matching_pattern (result, directory_path,
+                                    '*' + string(statefile_suffix));
 }
 
 vector<string>
index 05233c030a67bbee1d7d7e4333510984f5cb63e1..85b36e5a66231a0548f7f99e83358adf9a140f4e 100644 (file)
@@ -534,6 +534,11 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
 
        if (did_record) {
                commit_reversible_command ();
+               /* increase take name */
+               if (config.get_track_name_take () && !config.get_take_name ().empty()) {
+                       string newname = config.get_take_name();
+                       config.set_take_name(bump_name_number (newname));
+               }
        }
 
        if (_engine.running()) {
index 812e06c27b08d30252630d1bb776fc4178567a8c..e39ef3f548408aa36e2681279710b6f2f8cbafbd 100644 (file)
@@ -26,7 +26,6 @@
 #include <errno.h>
 #include <regex.h>
 
-#include "pbd/pathscanner.h"
 #include "pbd/stl_delete.h"
 #include "pbd/strsplit.h"
 
@@ -35,6 +34,7 @@
 #include <glibmm/fileutils.h>
 
 #include "evoral/Control.hpp"
+#include "evoral/SMF.hpp"
 
 #include "ardour/event_type_map.h"
 #include "ardour/midi_model.h"
@@ -49,6 +49,7 @@
 using namespace ARDOUR;
 using namespace Glib;
 using namespace PBD;
+using namespace Evoral;
 
 /** Constructor used for new internal-to-session files.  File cannot exist. */
 SMFSource::SMFSource (Session& s, const string& path, Source::Flag flags)
@@ -83,6 +84,39 @@ SMFSource::SMFSource (Session& s, const string& path, Source::Flag flags)
        _open = true;
 }
 
+/** Constructor used for external-to-session files.  File must exist. */
+SMFSource::SMFSource (Session& s, const string& path)
+       : Source(s, DataType::MIDI, path, Source::Flag (0))
+       , MidiSource(s, path, Source::Flag (0))
+       , FileSource(s, DataType::MIDI, path, string(), Source::Flag (0))
+       , Evoral::SMF()
+       , _last_ev_time_beats(0.0)
+       , _last_ev_time_frames(0)
+       , _smf_last_read_end (0)
+       , _smf_last_read_time (0)
+{
+       /* note that origin remains empty */
+
+       if (init (_path, false)) {
+               throw failed_constructor ();
+       }
+        assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
+       existence_check ();
+
+       /* file is not opened until write */
+
+       if (_flags & Writable) {
+               return;
+       }
+
+       if (open (_path)) {
+               throw failed_constructor ();
+       }
+
+       _open = true;
+}
+
 /** Constructor used for existing internal-to-session files. */
 SMFSource::SMFSource (Session& s, const XMLNode& node, bool must_exist)
        : Source(s, node)
@@ -464,6 +498,15 @@ SMFSource::mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::Musical
        mark_nonremovable ();
 }
 
+bool
+SMFSource::valid_midi_file (const string& file)
+{
+       if (safe_midi_file_extension (file) ) {
+               return (SMF::test (file) );
+       }
+       return false;
+}
+
 bool
 SMFSource::safe_midi_file_extension (const string& file)
 {
@@ -675,34 +718,3 @@ SMFSource::prevent_deletion ()
        _flags = Flag (_flags & ~(Removable|RemovableIfEmpty|RemoveAtDestroy));
 }
 
-int
-SMFSource::rename (const string& newname)
-{
-       Glib::Threads::Mutex::Lock lm (_lock);
-       string oldpath = _path;
-       string newpath = _session.new_source_path_from_name (DataType::MIDI, newname);
-
-       if (newpath.empty()) {
-               error << string_compose (_("programming error: %1"), "cannot generate a changed file path") << endmsg;
-               return -1;
-       }
-
-       // Test whether newpath exists, if yes notify the user but continue.
-       if (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)) {
-               error << string_compose (_("Programming error! %1 tried to rename a file over another file! It's safe to continue working, but please report this to the developers."), PROGRAM_NAME) << endmsg;
-               return -1;
-       }
-
-       if (Glib::file_test (oldpath.c_str(), Glib::FILE_TEST_EXISTS)) { 
-               /* rename only needed if file exists on disk */
-               if (::rename (oldpath.c_str(), newpath.c_str()) != 0) {
-                       error << string_compose (_("cannot rename file %1 to %2 (%3)"), oldpath, newpath, strerror(errno)) << endmsg;
-                       return -1;
-               }
-       }
-
-       _name = Glib::path_get_basename (newpath);
-       _path = newpath;
-
-       return 0;
-}
index e0851602fc0e685ff0cb3040ba87c68ac9f64270..af25b3e76f84ca9cee2a1840d1ec1e705a444beb 100644 (file)
@@ -185,6 +185,34 @@ SndFileSource::SndFileSource (Session& s, const string& path, const string& orig
        }
 }
 
+/** Constructor to be called for recovering files being used for
+ * capture. They are in-session, they already exist, they should not
+ * be writable. They are an odd hybrid (from a constructor point of
+ * view) of the previous two constructors.
+ */
+SndFileSource::SndFileSource (Session& s, const string& path, int chn)
+       : Source (s, DataType::AUDIO, path, Flag (0))
+         /* the final boolean argument is not used, its value is irrelevant. see audiofilesource.h for explanation */
+       , AudioFileSource (s, path, Flag (0))
+       , _descriptor (0)
+       , _broadcast_info (0)
+       , _capture_start (false)
+       , _capture_end (false)
+       , file_pos (0)
+       , xfade_buf (0)
+{
+       _channel = chn;
+
+       init_sndfile ();
+
+        assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
+       existence_check ();
+
+       if (open()) {
+               throw failed_constructor ();
+       }
+}
+
 void
 SndFileSource::init_sndfile ()
 {
@@ -266,6 +294,14 @@ SndFileSource::open ()
                delete _broadcast_info;
                _broadcast_info = 0;
                _flags = Flag (_flags & ~Broadcast);
+       } 
+
+       /* Set the broadcast flag if the BWF info is already there. We need
+        * this when recovering or using existing files.
+        */
+       
+       if (bwf_info_exists) {
+               _flags = Flag (_flags | Broadcast);
        }
 
        if (writable()) {
diff --git a/libs/ardour/soundcloud_upload.cc b/libs/ardour/soundcloud_upload.cc
new file mode 100644 (file)
index 0000000..da887d4
--- /dev/null
@@ -0,0 +1,355 @@
+/* soundcloud_export.cpp **********************************************************************
+
+       Adapted for Ardour by Ben Loftis, March 2012
+
+       Licence GPL:
+
+       This program is free software; you can redistribute it and/or
+       modify it under the terms of the GNU General Public License
+       as published by the Free Software Foundation; either version 2
+       of the License, or (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with this program; if not, write to the Free Software
+       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+
+*************************************************************************************/
+#include "ardour/debug.h"
+#include "ardour/soundcloud_upload.h"
+
+#include "pbd/xml++.h"
+#include <pbd/error.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <iostream>
+#include <glib/gstdio.h>
+
+#include "i18n.h"
+
+using namespace PBD;
+
+size_t
+WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
+{
+       register int realsize = (int)(size * nmemb);
+       struct MemoryStruct *mem = (struct MemoryStruct *)data;
+
+       mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
+
+       if (mem->memory) {
+               memcpy(&(mem->memory[mem->size]), ptr, realsize);
+               mem->size += realsize;
+               mem->memory[mem->size] = 0;
+       }
+       return realsize;
+}
+
+SoundcloudUploader::SoundcloudUploader()
+{
+       curl_handle = curl_easy_init();
+       multi_handle = curl_multi_init();
+}
+
+std::string
+SoundcloudUploader::Get_Auth_Token( std::string username, std::string password )
+{
+       struct MemoryStruct xml_page;
+       xml_page.memory = NULL;
+       xml_page.size = 0;
+
+       setcUrlOptions();
+
+       curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+       curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
+
+       struct curl_httppost *formpost=NULL;
+       struct curl_httppost *lastptr=NULL;
+
+       /* Fill in the filename field */ 
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "client_id",
+                       CURLFORM_COPYCONTENTS, "6dd9cf0ad281aa57e07745082cec580b",
+                       CURLFORM_END);
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "client_secret",
+                       CURLFORM_COPYCONTENTS, "53f5b0113fb338800f8a7a9904fc3569",
+                       CURLFORM_END);
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "grant_type",
+                       CURLFORM_COPYCONTENTS, "password",
+                       CURLFORM_END);
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "username",
+                       CURLFORM_COPYCONTENTS, username.c_str(),
+                       CURLFORM_END);
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "password",
+                       CURLFORM_COPYCONTENTS, password.c_str(),
+                       CURLFORM_END);
+
+       struct curl_slist *headerlist=NULL;
+       headerlist = curl_slist_append(headerlist, "Expect:");
+       headerlist = curl_slist_append(headerlist, "Accept: application/xml");
+       curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
+
+       /* what URL that receives this POST */ 
+       std::string url = "https://api.soundcloud.com/oauth2/token";
+       curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
+       curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
+
+       // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
+
+       // perform online request
+       CURLcode res = curl_easy_perform(curl_handle);
+       if (res != 0) {
+               DEBUG_TRACE (DEBUG::Soundcloud, string_compose ("curl error %1 (%2)", res, curl_easy_strerror(res) ) );
+               return "";
+       }
+
+       if (xml_page.memory){
+               // cheesy way to parse the json return value.  find access_token, then advance 3 quotes
+
+               if ( strstr ( xml_page.memory , "access_token" ) == NULL) {
+                       error << _("Upload to Soundcloud failed.  Perhaps your email or password are incorrect?\n") << endmsg;
+                       return "";
+               }
+
+               std::string token = strtok( xml_page.memory, "access_token" );
+               token = strtok( NULL, "\"" );
+               token = strtok( NULL, "\"" );
+               token = strtok( NULL, "\"" );
+
+               free( xml_page.memory );
+               return token;
+       }
+
+       return "";
+}
+
+int
+SoundcloudUploader::progress_callback(void *caller, double dltotal, double dlnow, double ultotal, double ulnow)
+{
+       SoundcloudUploader *scu = (SoundcloudUploader *) caller;
+       DEBUG_TRACE (DEBUG::Soundcloud, string_compose ("%1: uploaded %2 of %3", scu->title, ulnow, ultotal) );
+       scu->caller->SoundcloudProgress(ultotal, ulnow, scu->title); /* EMIT SIGNAL */
+       return 0;
+}
+
+
+std::string
+SoundcloudUploader::Upload(std::string file_path, std::string title, std::string token, bool ispublic, bool downloadable, ARDOUR::ExportHandler *caller)
+{
+       int still_running;
+
+       struct MemoryStruct xml_page;
+       xml_page.memory = NULL;
+       xml_page.size = 0;
+
+       setcUrlOptions();
+
+       curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+       curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
+
+       struct curl_httppost *formpost=NULL;
+       struct curl_httppost *lastptr=NULL;
+
+       /* Fill in the file upload field. This makes libcurl load data from
+          the given file name when curl_easy_perform() is called. */ 
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "track[asset_data]",
+                       CURLFORM_FILE, file_path.c_str(),
+                       CURLFORM_END);
+
+       /* Fill in the filename field */ 
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "oauth_token",
+                       CURLFORM_COPYCONTENTS, token.c_str(),
+                       CURLFORM_END);
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "track[title]",
+                       CURLFORM_COPYCONTENTS, title.c_str(),
+                       CURLFORM_END);
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "track[sharing]",
+                       CURLFORM_COPYCONTENTS, ispublic ? "public" : "private",
+                       CURLFORM_END);
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "track[downloadable]",
+                       CURLFORM_COPYCONTENTS, downloadable ? "true" : "false",
+                       CURLFORM_END);
+
+
+
+       /* initalize custom header list (stating that Expect: 100-continue is not
+          wanted */ 
+       struct curl_slist *headerlist=NULL;
+       static const char buf[] = "Expect:";
+       headerlist = curl_slist_append(headerlist, buf);
+
+
+       if (curl_handle && multi_handle) {
+
+               /* what URL that receives this POST */ 
+               std::string url = "https://api.soundcloud.com/tracks";
+               curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
+               // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
+
+               curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
+               curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
+
+               this->title = title; // save title to show in progress bar
+               this->caller = caller;
+
+               curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 0); // turn on the progress bar
+               curl_easy_setopt (curl_handle, CURLOPT_PROGRESSFUNCTION, &SoundcloudUploader::progress_callback);
+               curl_easy_setopt (curl_handle, CURLOPT_PROGRESSDATA, this);
+
+               curl_multi_add_handle(multi_handle, curl_handle);
+
+               curl_multi_perform(multi_handle, &still_running);
+
+
+               while(still_running) {
+                       struct timeval timeout;
+                       int rc; /* select() return code */ 
+
+                       fd_set fdread;
+                       fd_set fdwrite;
+                       fd_set fdexcep;
+                       int maxfd = -1;
+
+                       long curl_timeo = -1;
+
+                       FD_ZERO(&fdread);
+                       FD_ZERO(&fdwrite);
+                       FD_ZERO(&fdexcep);
+
+                       /* set a suitable timeout to play around with */ 
+                       timeout.tv_sec = 1;
+                       timeout.tv_usec = 0;
+
+                       curl_multi_timeout(multi_handle, &curl_timeo);
+                       if(curl_timeo >= 0) {
+                               timeout.tv_sec = curl_timeo / 1000;
+                               if(timeout.tv_sec > 1)
+                                       timeout.tv_sec = 1;
+                               else
+                                       timeout.tv_usec = (curl_timeo % 1000) * 1000;
+                       }
+
+                       /* get file descriptors from the transfers */ 
+                       curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
+
+                       /* In a real-world program you OF COURSE check the return code of the
+                          function calls.  On success, the value of maxfd is guaranteed to be
+                          greater or equal than -1.  We call select(maxfd + 1, ...), specially in
+                          case of (maxfd == -1), we call select(0, ...), which is basically equal
+                          to sleep. */ 
+
+                       rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
+
+                       switch(rc) {
+                               case -1:
+                                       /* select error */ 
+                                       break;
+                               case 0:
+                               default:
+                                       /* timeout or readable/writable sockets */ 
+                                       curl_multi_perform(multi_handle, &still_running);
+                                       break;
+                       }
+               } 
+
+               /* then cleanup the formpost chain */ 
+               curl_formfree(formpost);
+
+               /* free slist */ 
+               curl_slist_free_all (headerlist);
+       }
+
+       curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 1); // turn off the progress bar
+
+       if(xml_page.memory){
+
+               DEBUG_TRACE (DEBUG::Soundcloud, xml_page.memory);
+
+               XMLTree doc;
+               doc.read_buffer( xml_page.memory );
+               XMLNode *root = doc.root();
+
+               if (!root) {
+                       DEBUG_TRACE (DEBUG::Soundcloud, "no root XML node!");
+                       return "";
+               }
+
+               XMLNode *url_node = root->child("permalink-url");
+               if (!url_node) {
+                       DEBUG_TRACE (DEBUG::Soundcloud, "no child node \"permalink-url\" found!");
+                       return "";
+               }
+
+               XMLNode *text_node = url_node->child("text");
+               if (!text_node) {
+                       DEBUG_TRACE (DEBUG::Soundcloud, "no text node found!");
+                       return "";
+               }
+
+               free( xml_page.memory );
+               return text_node->content();
+       }
+
+       return "";
+};
+
+
+SoundcloudUploader:: ~SoundcloudUploader()
+{
+       curl_easy_cleanup(curl_handle);
+       curl_multi_cleanup(multi_handle);
+}
+
+
+void
+SoundcloudUploader::setcUrlOptions()
+{
+       // basic init for curl
+       curl_global_init(CURL_GLOBAL_ALL);
+       // some servers don't like requests that are made without a user-agent field, so we provide one
+       curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
+       // setup curl error buffer
+       curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorBuffer);
+       // Allow redirection
+       curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1);
+       
+       // Allow connections to time out (without using signals)
+       curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
+       curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 30);
+
+       curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
+       curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
+}
+
index 0729f21592040c615edc9bf9154247cc59404f6c..6d2bb80b30be8859716bb09fea2a3d17b6e27c07 100644 (file)
@@ -272,7 +272,7 @@ SourceFactory::createExternal (DataType type, Session& s, const string& path,
 
        } else if (type == DataType::MIDI) {
 
-               boost::shared_ptr<SMFSource> src (new SMFSource (s, path, SMFSource::Flag(0)));
+               boost::shared_ptr<SMFSource> src (new SMFSource (s, path));
                src->load_model (true, true);
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
                // boost_debug_shared_ptr_mark_interesting (src, "Source");
@@ -341,6 +341,39 @@ SourceFactory::createWritable (DataType type, Session& s, const std::string& pat
        return boost::shared_ptr<Source> ();
 }
 
+boost::shared_ptr<Source>
+SourceFactory::createForRecovery (DataType type, Session& s, const std::string& path, int chn)
+{
+       /* this might throw failed_constructor(), which is OK */
+
+       if (type == DataType::AUDIO) {
+               Source* src = new SndFileSource (s, path, chn);
+
+#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
+               // boost_debug_shared_ptr_mark_interesting (src, "Source");
+#endif
+               boost::shared_ptr<Source> ret (src);
+
+               if (setup_peakfile (ret, false)) {
+                       return boost::shared_ptr<Source>();
+               }
+
+               // no analysis data - this is still basically a new file (we
+               // crashed while recording.
+
+               // always announce these files
+
+               SourceCreated (ret);
+
+               return ret;
+
+       } else if (type == DataType::MIDI) {
+               error << _("Recovery attempted on a MIDI file - not implemented") << endmsg;
+       }
+
+       return boost::shared_ptr<Source> ();
+}
+
 boost::shared_ptr<Source>
 SourceFactory::createFromPlaylist (DataType type, Session& s, boost::shared_ptr<Playlist> p, const PBD::ID& orig, const std::string& name,
                                   uint32_t chn, frameoffset_t start, framecnt_t len, bool copy, bool defer_peaks)
index 90e729a7f683d51df0f761fb3947ebf942afd6f1..456f8351f26a33586294f782f433b16dc997f3b0 100644 (file)
@@ -19,7 +19,6 @@
 */
 
 #include <glibmm/miscutils.h>
-#include "pbd/pathscanner.h"
 #include "pbd/file_utils.h"
 #include "pbd/error.h"
 
@@ -35,7 +34,7 @@ static char *vfork_exec_wrapper_path() {
        return NULL;
 #else
        std::string vfork_exec_wrapper;
-       if (!PBD::find_file_in_search_path (
+       if (!PBD::find_file (
                                PBD::Searchpath(Glib::build_filename(ARDOUR::ardour_dll_directory(), "vfork")),
                                "ardour-exec-wrapper", vfork_exec_wrapper)) {
                PBD::warning << "vfork exec wrapper not found..'" << endmsg;
@@ -65,4 +64,14 @@ SystemExec::SystemExec (std::string c, std::string a)
 #endif
 }
 
+SystemExec::SystemExec (std::string c, const std::map<char, std::string> subs)
+       : PBD::SystemExec(c, subs)
+{
+#ifndef PLATFORM_WINDOWS
+       if (!_vfork_exec_wrapper) {
+               _vfork_exec_wrapper = vfork_exec_wrapper_path();
+       }
+#endif
+}
+
 SystemExec::~SystemExec() { }
index 8efe3115366594cbebc75d0de974a13b1b07fc80..f5a4dc78a6bcd1384666f774408da68e3d192a6b 100644 (file)
@@ -23,7 +23,8 @@
 #include <glibmm.h>
 
 #include "pbd/basename.h"
-#include "pbd/pathscanner.h"
+#include "pbd/file_utils.h"
+#include "pbd/stl_delete.h"
 #include "pbd/xml++.h"
 
 #include "ardour/template_utils.h"
@@ -80,21 +81,19 @@ session_template_dir_to_file (string const & dir)
 void
 find_session_templates (vector<TemplateInfo>& template_names)
 {
-       vector<string *> *templates;
-       PathScanner scanner;
-       Searchpath spath (template_search_path());
+       vector<string> templates;
 
-       templates = scanner (spath.to_string(), template_filter, 0, true, true);
+       find_files_matching_filter (templates, template_search_path(), template_filter, 0, true, true);
 
-       if (!templates) {
-               cerr << "Found nothing along " << spath.to_string() << endl;
+       if (templates.empty()) {
+               cerr << "Found nothing along " << template_search_path().to_string() << endl;
                return;
        }
 
-       cerr << "Found " << templates->size() << " along " << spath.to_string() << endl;
+       cerr << "Found " << templates.size() << " along " << template_search_path().to_string() << endl;
 
-       for (vector<string*>::iterator i = templates->begin(); i != templates->end(); ++i) {
-               string file = session_template_dir_to_file (**i);
+       for (vector<string>::iterator i = templates.begin(); i != templates.end(); ++i) {
+               string file = session_template_dir_to_file (*i);
 
                XMLTree tree;
 
@@ -104,30 +103,26 @@ find_session_templates (vector<TemplateInfo>& template_names)
 
                TemplateInfo rti;
 
-               rti.name = basename_nosuffix (**i);
-               rti.path = **i;
+               rti.name = basename_nosuffix (*i);
+               rti.path = *i;
 
                template_names.push_back (rti);
        }
-
-       delete templates;
 }
 
 void
 find_route_templates (vector<TemplateInfo>& template_names)
 {
-       vector<string *> *templates;
-       PathScanner scanner;
-       Searchpath spath (route_template_search_path());
+       vector<string> templates;
 
-       templates = scanner (spath.to_string(), route_template_filter, 0, false, true);
+       find_files_matching_filter (templates, route_template_search_path(), route_template_filter, 0, false, true);
 
-       if (!templates) {
+       if (templates.empty()) {
                return;
        }
 
-       for (vector<string*>::iterator i = templates->begin(); i != templates->end(); ++i) {
-               string fullpath = *(*i);
+       for (vector<string>::iterator i = templates.begin(); i != templates.end(); ++i) {
+               string fullpath = *i;
 
                XMLTree tree;
 
@@ -144,8 +139,6 @@ find_route_templates (vector<TemplateInfo>& template_names)
 
                template_names.push_back (rti);
        }
-
-       delete templates;
 }
 
 }
index 0bb2fea0cf3e0c6a22a1f25a07a916bdbab49045..621d1e2c747d0d30bfe4322184cee1db044b8b54 100644 (file)
@@ -884,6 +884,7 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
        TempoSection* ts;
        MeterSection* ms;
        double beat_frames;
+       double current_frame_exact;
        framepos_t bar_start_frame;
 
        DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Extend map to %1 from %2 = %3\n", end, current, current_frame));
@@ -895,11 +896,13 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
        }
 
        beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
+       current_frame_exact = current_frame;
 
        while (current_frame < end) {
 
                current.beats++;
-               current_frame += beat_frames;
+               current_frame_exact += beat_frames;
+               current_frame = llrint(current_frame_exact);
 
                if (current.beats > meter->divisions_per_bar()) {
                        current.bars++;
@@ -942,7 +945,8 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
                                                                                               tempo->start(), current_frame, tempo->bar_offset()));
                                                
                                                /* back up to previous beat */
-                                               current_frame -= beat_frames;
+                                               current_frame_exact -= beat_frames;
+                                               current_frame = llrint(current_frame_exact);
 
                                                /* set tempo section location
                                                 * based on offset from last
@@ -963,7 +967,8 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
 
                                                double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
 
-                                               current_frame += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
+                                               current_frame_exact += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
+                                               current_frame = llrint(current_frame_exact);
 
                                                /* next metric doesn't have to
                                                 * match this precisely to
@@ -1012,11 +1017,11 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
 
                if (current.beats == 1) {
                        DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
-                       _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1));
+                       _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, 1));
                        bar_start_frame = current_frame;
                } else {
                        DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
-                       _map.push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats));
+                       _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, current.beats));
                }
 
                if (next_metric == metrics.end()) {
index c8ef0f4a95b84cef5926b8278934c55a9af73f66..9aa898999261789ea71e0c4276bee8d68f2ef9f3 100644 (file)
@@ -19,7 +19,7 @@ ResampledSourceTest::seekTest ()
        std::string test_file_path;
        const string test_filename = "test.wav";
 
-       CPPUNIT_ASSERT (find_file_in_search_path (test_search_path (), test_filename, test_file_path));
+       CPPUNIT_ASSERT (find_file (test_search_path (), test_filename, test_file_path));
 
        boost::shared_ptr<SndFileImportableSource> s (new SndFileImportableSource (test_file_path));
        ResampledImportableSource r (s, 48000, SrcBest);
index e469187ce9497709d8a54240a05aef9a3ed8757e..b51576bfc920e1e46a5b908f38b5fe58dd106182 100644 (file)
@@ -40,7 +40,7 @@ ThreadBuffers::ThreadBuffers ()
 }
 
 void
-ThreadBuffers::ensure_buffers (ChanCount howmany)
+ThreadBuffers::ensure_buffers (ChanCount howmany, size_t custom)
 {
        // std::cerr << "ThreadBuffers " << this << " resize buffers with count = " << howmany << std::endl;
 
@@ -60,7 +60,14 @@ ThreadBuffers::ensure_buffers (ChanCount howmany)
 
        for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
                size_t count = std::max (scratch_buffers->available().get(*t), howmany.get(*t));
-               size_t size = _engine->raw_buffer_size (*t) / sizeof (Sample);
+               size_t size;
+               if (custom > 0) {
+                       size = custom;
+               } else {
+                       size = (*t == DataType::MIDI)
+                               ? _engine->raw_buffer_size (*t)
+                               : _engine->raw_buffer_size (*t) / sizeof (Sample);
+               }
 
                scratch_buffers->ensure_buffers (*t, count, size);
                mix_buffers->ensure_buffers (*t, count, size);
@@ -68,7 +75,7 @@ ThreadBuffers::ensure_buffers (ChanCount howmany)
                route_buffers->ensure_buffers (*t, count, size);
        }
 
-       size_t audio_buffer_size = _engine->raw_buffer_size (DataType::AUDIO) / sizeof (Sample);
+       size_t audio_buffer_size = custom > 0 ? custom : _engine->raw_buffer_size (DataType::AUDIO) / sizeof (Sample);
 
        delete [] gain_automation_buffer;
        gain_automation_buffer = new gain_t[audio_buffer_size];
index 5c95de0c4118fbdd97a87385a9cc1e49eb67c2a2..d463be598f22fdc0021b7a3b3bdcb9e0beaca80a 100644 (file)
@@ -68,8 +68,10 @@ Track::init ()
        /* don't add rec_enable_control to controls because we don't want it to
         * appear as an automatable parameter
         */
+       track_number_changed.connect_same_thread (*this, boost::bind (&Track::resync_track_name, this));
+       _session.config.ParameterChanged.connect_same_thread (*this, boost::bind (&Track::parameter_changed, this, _1));
 
-        return 0;
+       return 0;
 }
 
 void
@@ -284,6 +286,28 @@ Track::set_record_enabled (bool yn, void *src)
        _rec_enable_control->Changed ();
 }
 
+void
+Track::parameter_changed (string const & p)
+{
+       if (p == "track-name-number") {
+               resync_track_name ();
+       }
+       else if (p == "track-name-take") {
+               resync_track_name ();
+       }
+       else if (p == "take-name") {
+               if (_session.config.get_track_name_take()) {
+                       resync_track_name ();
+               }
+       }
+}
+
+void
+Track::resync_track_name ()
+{
+       set_name(name());
+}
+
 bool
 Track::set_name (const string& str)
 {
@@ -294,6 +318,29 @@ Track::set_name (const string& str)
                return false;
        }
 
+       string diskstream_name = "";
+       if (_session.config.get_track_name_take () && !_session.config.get_take_name ().empty()) {
+               // Note: any text is fine, legalize_for_path() fixes this later
+               diskstream_name += _session.config.get_take_name ();
+               diskstream_name += "_";
+       }
+       const int64_t tracknumber = track_number();
+       if (tracknumber > 0 && _session.config.get_track_name_number()) {
+               char num[64], fmt[10];
+               snprintf(fmt, sizeof(fmt), "%%0%d" PRId64, _session.track_number_decimals());
+               snprintf(num, sizeof(num), fmt, tracknumber);
+               diskstream_name += num;
+               diskstream_name += "_";
+       }
+       diskstream_name += str;
+
+       if (diskstream_name == _diskstream_name) {
+               return true;
+       }
+       _diskstream_name = diskstream_name;
+
+       _diskstream->set_write_source_name (diskstream_name);
+
        boost::shared_ptr<Track> me = boost::dynamic_pointer_cast<Track> (shared_from_this ());
        if (_diskstream->playlist()->all_regions_empty () && _session.playlists->playlists_for_track (me).size() == 1) {
                /* Only rename the diskstream (and therefore the playlist) if
index 715c0d67dc26f7dfde72420602f2bc0504558c24..9c0aea885bba3d9213e2a1c8cdfc21731cd8e5bb 100644 (file)
@@ -93,7 +93,7 @@ replace_chars (const string& str, const string& illegal_chars)
  * the goal there is to be legal across filesystems.
  */
 string
-legalize_for_path (const string& str)
+ARDOUR::legalize_for_path (const string& str)
 {
        return replace_chars (str, "/\\");
 }
@@ -108,7 +108,7 @@ legalize_for_path (const string& str)
  * ANY filesystem.
  */
 string
-legalize_for_universal_path (const string& str)
+ARDOUR::legalize_for_universal_path (const string& str)
 {
        return replace_chars (str, "<>:\"/\\|?*");
 }
@@ -119,7 +119,7 @@ legalize_for_universal_path (const string& str)
  * correct.
  */
 string
-legalize_for_uri (const string& str)
+ARDOUR::legalize_for_uri (const string& str)
 {
        return replace_chars (str, "<>:\"/\\|?* #");
 }
@@ -133,7 +133,7 @@ legalize_for_uri (const string& str)
  */
 
 string 
-legalize_for_path_2X (const string& str)
+ARDOUR::legalize_for_path_2X (const string& str)
 {
        string::size_type pos;
        string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+=: ";
@@ -155,7 +155,7 @@ legalize_for_path_2X (const string& str)
 }
 
 string
-bump_name_once (const std::string& name, char delimiter)
+ARDOUR::bump_name_once (const std::string& name, char delimiter)
 {
        string::size_type delim;
        string newname;
@@ -196,8 +196,34 @@ bump_name_once (const std::string& name, char delimiter)
 
 }
 
+string
+ARDOUR::bump_name_number (const std::string& name)
+{
+       size_t pos = name.length();
+       size_t num = 0;
+       bool have_number = false;
+       while (pos > 0 && isdigit(name.at(--pos))) {
+               have_number = true;
+               num = pos;
+       }
+
+       string newname;
+       if (have_number) {
+               int32_t seq = strtol (name.c_str() + num, (char **)NULL, 10);
+               char buf[32];
+               snprintf (buf, sizeof(buf), "%d", seq + 1);
+               newname = name.substr (0, num);
+               newname += buf;
+       } else {
+               newname = name;
+               newname += "1";
+       }
+
+       return newname;
+}
+
 XMLNode *
-find_named_node (const XMLNode& node, string name)
+ARDOUR::find_named_node (const XMLNode& node, string name)
 {
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
@@ -218,7 +244,7 @@ find_named_node (const XMLNode& node, string name)
 }
 
 int
-cmp_nocase (const string& s, const string& s2)
+ARDOUR::cmp_nocase (const string& s, const string& s2)
 {
        string::const_iterator p = s.begin();
        string::const_iterator p2 = s2.begin();
@@ -234,7 +260,8 @@ cmp_nocase (const string& s, const string& s2)
        return (s2.size() == s.size()) ? 0 : (s.size() < s2.size()) ? -1 : 1;
 }
 
-int cmp_nocase_utf8 (const string& s1, const string& s2)
+int
+ARDOUR::cmp_nocase_utf8 (const string& s1, const string& s2)
 {
        const char *cstr1 = s1.c_str();
        const char *cstr2 = s2.c_str();
@@ -270,7 +297,7 @@ int cmp_nocase_utf8 (const string& s1, const string& s2)
 }
 
 int
-touch_file (string path)
+ARDOUR::touch_file (string path)
 {
        int fd = open (path.c_str(), O_RDWR|O_CREAT, 0660);
        if (fd >= 0) {
@@ -281,7 +308,7 @@ touch_file (string path)
 }
 
 string
-region_name_from_path (string path, bool strip_channels, bool add_channel_suffix, uint32_t total, uint32_t this_one)
+ARDOUR::region_name_from_path (string path, bool strip_channels, bool add_channel_suffix, uint32_t total, uint32_t this_one)
 {
        path = PBD::basename_nosuffix (path);
 
@@ -313,7 +340,7 @@ region_name_from_path (string path, bool strip_channels, bool add_channel_suffix
 }
 
 bool
-path_is_paired (string path, string& pair_base)
+ARDOUR::path_is_paired (string path, string& pair_base)
 {
        string::size_type pos;
 
@@ -346,7 +373,7 @@ path_is_paired (string path, string& pair_base)
 
 #if __APPLE__
 string
-CFStringRefToStdString(CFStringRef stringRef)
+ARDOUR::CFStringRefToStdString(CFStringRef stringRef)
 {
        CFIndex size =
                CFStringGetMaximumSizeForEncoding(CFStringGetLength(stringRef) ,
@@ -364,7 +391,7 @@ CFStringRefToStdString(CFStringRef stringRef)
 #endif // __APPLE__
 
 void
-compute_equal_power_fades (framecnt_t nframes, float* in, float* out)
+ARDOUR::compute_equal_power_fades (framecnt_t nframes, float* in, float* out)
 {
        double step;
 
@@ -390,7 +417,7 @@ compute_equal_power_fades (framecnt_t nframes, float* in, float* out)
 }
 
 EditMode
-string_to_edit_mode (string str)
+ARDOUR::string_to_edit_mode (string str)
 {
        if (str == _("Splice")) {
                return Splice;
@@ -407,7 +434,7 @@ string_to_edit_mode (string str)
 }
 
 const char*
-edit_mode_to_string (EditMode mode)
+ARDOUR::edit_mode_to_string (EditMode mode)
 {
        switch (mode) {
        case Slide:
@@ -426,7 +453,7 @@ edit_mode_to_string (EditMode mode)
 }
 
 SyncSource
-string_to_sync_source (string str)
+ARDOUR::string_to_sync_source (string str)
 {
        if (str == _("MIDI Timecode") || str == _("MTC")) {
                return MTC;
@@ -447,7 +474,7 @@ string_to_sync_source (string str)
 
 /** @param sh Return a short version of the string */
 const char*
-sync_source_to_string (SyncSource src, bool sh)
+ARDOUR::sync_source_to_string (SyncSource src, bool sh)
 {
        switch (src) {
        case Engine:
@@ -478,7 +505,7 @@ sync_source_to_string (SyncSource src, bool sh)
 }
 
 float
-meter_falloff_to_float (MeterFalloff falloff)
+ARDOUR::meter_falloff_to_float (MeterFalloff falloff)
 {
        switch (falloff) {
        case MeterFalloffOff:
@@ -505,7 +532,7 @@ meter_falloff_to_float (MeterFalloff falloff)
 }
 
 MeterFalloff
-meter_falloff_from_float (float val)
+ARDOUR::meter_falloff_from_float (float val)
 {
        if (val == METER_FALLOFF_OFF) {
                return MeterFalloffOff;
@@ -618,7 +645,7 @@ bool_as_string (bool yn)
 }
 
 const char*
-native_header_format_extension (HeaderFormat hf, const DataType& type)
+ARDOUR::native_header_format_extension (HeaderFormat hf, const DataType& type)
 {
         if (type == DataType::MIDI) {
                 return ".mid";
@@ -647,7 +674,7 @@ native_header_format_extension (HeaderFormat hf, const DataType& type)
 }
 
 bool
-matching_unsuffixed_filename_exists_in (const string& dir, const string& path)
+ARDOUR::matching_unsuffixed_filename_exists_in (const string& dir, const string& path)
 {
         string bws = basename_nosuffix (path);
        struct dirent* dentry;
@@ -692,7 +719,7 @@ matching_unsuffixed_filename_exists_in (const string& dir, const string& path)
 }
 
 uint32_t
-how_many_dsp_threads ()
+ARDOUR::how_many_dsp_threads ()
 {
         /* CALLER MUST HOLD PROCESS LOCK */
 
@@ -725,12 +752,14 @@ how_many_dsp_threads ()
         return num_threads;
 }
 
-double gain_to_slider_position_with_max (double g, double max_gain)
+double
+ARDOUR::gain_to_slider_position_with_max (double g, double max_gain)
 {
         return gain_to_slider_position (g * 2.0/max_gain);
 }
 
-double slider_position_to_gain_with_max (double g, double max_gain)
+double
+ARDOUR::slider_position_to_gain_with_max (double g, double max_gain)
 {
        return slider_position_to_gain (g * max_gain/2.0);
 }
index bf9d196e61dedaf830644cedf29fdb63e16254d6..da49d1b8eb79fd18ca30ff9f02a7cd14999e16da 100644 (file)
 #include <glib/gstdio.h>
 #include <glibmm.h>
 
+#include "pbd/error.h"
 
-#ifdef VST_SCANNER_APP
-#define errormsg cerr
-#define warningmsg cerr
-#define endmsg endl
-#else
+#ifndef VST_SCANNER_APP
 #include "ardour/plugin_manager.h" // scanner_bin_path
 #include "ardour/rc_configuration.h"
 #include "ardour/system_exec.h"
-#include "pbd/error.h"
-#define errormsg PBD::error
-#define warningmsg PBD::warning
 #endif
 
 #include "ardour/filesystem_paths.h"
@@ -330,7 +324,7 @@ vstfx_write_info_file (FILE* fp, vector<VSTInfo *> *infos)
        } else if (infos->size() == 1) {
                vstfx_write_info_block(fp, infos->front());
        } else {
-               errormsg << "Zero plugins in VST." << endmsg; // XXX here? rather make this impossible before if it ain't already.
+               PBD::error << "Zero plugins in VST." << endmsg; // XXX here? rather make this impossible before if it ain't already.
        }
 }
 
@@ -406,7 +400,6 @@ vstfx_un_blacklist (const char *dllpath)
        ::g_unlink(vstfx_blacklist_path (dllpath, 1).c_str());
 }
 
-#ifndef VST_SCANNER_APP
 /** remove info file from cache */
 static void
 vstfx_remove_infofile (const char *dllpath)
@@ -414,7 +407,6 @@ vstfx_remove_infofile (const char *dllpath)
        ::g_unlink(vstfx_infofile_path (dllpath, 0).c_str());
        ::g_unlink(vstfx_infofile_path (dllpath, 1).c_str());
 }
-#endif
 
 /** helper function, check if cache is newer than plugin
  * @return path to cache file */
@@ -516,7 +508,7 @@ vstfx_get_info_from_file(const char* dllpath, vector<VSTInfo*> *infos)
                rv = vstfx_load_info_file(infofile, infos);
                fclose (infofile);
                if (!rv) {
-                       warningmsg << "Cannot get VST information form " << dllpath << ": info file load failed." << endmsg;
+                       PBD::warning << "Cannot get VST information form " << dllpath << ": info file load failed." << endmsg;
                }
        }
        return rv;
@@ -618,14 +610,26 @@ vstfx_parse_vst_state (VSTState* vstfx)
          fail to implement getVendorString, and so won't stuff the
          string with any name*/
 
-       char creator[65] = "Unknown\0";
+       char creator[65] = "Unknown";
+       char name[65] = "";
 
        AEffect* plugin = vstfx->plugin;
 
-       info->name = strdup (vstfx->handle->name);
+
+       plugin->dispatcher (plugin, effGetEffectName, 0, 0, name, 0);
+
+       if (strlen(name) == 0) {
+               plugin->dispatcher (plugin, effGetProductString, 0, 0, name, 0);
+       }
+
+       if (strlen(name) == 0) {
+               info->name = strdup (vstfx->handle->name);
+       } else {
+               info->name = strdup (name);
+       }
 
        /*If the plugin doesn't bother to implement GetVendorString we will
-         have pre-stuffed the string with 'Unkown' */
+         have pre-stuffed the string with 'Unknown' */
 
        plugin->dispatcher (plugin, effGetVendorString, 0, 0, creator, 0);
 
@@ -711,7 +715,7 @@ vstfx_info_from_plugin (const char *dllpath, VSTState* vstfx, vector<VSTInfo *>
                string path = vstfx->handle->path;
 
                do {
-                       char name[65] = "Unknown\0";
+                       char name[65] = "Unknown";
                        id = plugin->dispatcher (plugin, effShellGetNextPlugin, 0, 0, name, 0);
                        ids.push_back(std::make_pair(id, name));
                } while ( id != 0 );
@@ -782,7 +786,7 @@ vstfx_instantiate_and_get_info_lx (
        VSTHandle* h;
        VSTState* vstfx;
        if (!(h = vstfx_load(dllpath))) {
-               warningmsg << "Cannot get LinuxVST information from " << dllpath << ": load failed." << endmsg;
+               PBD::warning << "Cannot get LinuxVST information from " << dllpath << ": load failed." << endmsg;
                return false;
        }
 
@@ -790,7 +794,7 @@ vstfx_instantiate_and_get_info_lx (
 
        if (!(vstfx = vstfx_instantiate(h, simple_master_callback, 0))) {
                vstfx_unload(h);
-               warningmsg << "Cannot get LinuxVST information from " << dllpath << ": instantiation failed." << endmsg;
+               PBD::warning << "Cannot get LinuxVST information from " << dllpath << ": instantiation failed." << endmsg;
                return false;
        }
 
@@ -811,7 +815,7 @@ vstfx_instantiate_and_get_info_fst (
        VSTHandle* h;
        VSTState* vstfx;
        if(!(h = fst_load(dllpath))) {
-               warningmsg << "Cannot get Windows VST information from " << dllpath << ": load failed." << endmsg;
+               PBD::warning << "Cannot get Windows VST information from " << dllpath << ": load failed." << endmsg;
                return false;
        }
 
@@ -820,7 +824,7 @@ vstfx_instantiate_and_get_info_fst (
        if(!(vstfx = fst_instantiate(h, simple_master_callback, 0))) {
                fst_unload(&h);
                vstfx_current_loading_id = 0;
-               warningmsg << "Cannot get Windows VST information from " << dllpath << ": instantiation failed." << endmsg;
+               PBD::warning << "Cannot get Windows VST information from " << dllpath << ": instantiation failed." << endmsg;
                return false;
        }
        vstfx_current_loading_id = 0;
@@ -842,14 +846,14 @@ static char * _errorlog_dll = 0;
 static void parse_scanner_output (std::string msg, size_t /*len*/)
 {
        if (!_errorlog_fd && !_errorlog_dll) {
-               errormsg << "VST scanner: " << msg;
+               PBD::error << "VST scanner: " << msg;
                return;
        }
 
        if (!_errorlog_fd) {
                if (!(_errorlog_fd = fopen(vstfx_errorfile_path(_errorlog_dll, 0).c_str(), "w"))) {
                        if (!(_errorlog_fd = fopen(vstfx_errorfile_path(_errorlog_dll, 1).c_str(), "w"))) {
-                               errormsg << "Cannot create plugin error-log for plugin " << _errorlog_dll;
+                               PBD::error << "Cannot create plugin error-log for plugin " << _errorlog_dll;
                                free(_errorlog_dll);
                                _errorlog_dll = NULL;
                        }
@@ -859,7 +863,7 @@ static void parse_scanner_output (std::string msg, size_t /*len*/)
        if (_errorlog_fd) {
                fprintf (_errorlog_fd, "%s\n", msg.c_str());
        } else {
-               errormsg << "VST scanner: " << msg;
+               PBD::error << "VST scanner: " << msg;
        }
 }
 
@@ -919,7 +923,7 @@ vstfx_get_info (const char* dllpath, enum ARDOUR::PluginType type, enum VSTScanM
                PBD::ScopedConnectionList cons;
                scanner.ReadStdout.connect_same_thread (cons, boost::bind (&parse_scanner_output, _1 ,_2));
                if (scanner.start (2 /* send stderr&stdout via signal */)) {
-                       errormsg << "Cannot launch VST scanner app '" << scanner_bin_path << "': "<< strerror(errno) << endmsg;
+                       PBD::error << "Cannot launch VST scanner app '" << scanner_bin_path << "': "<< strerror(errno) << endmsg;
                        close_error_log();
                        return infos;
                } else {
@@ -985,7 +989,7 @@ vstfx_get_info (const char* dllpath, enum ARDOUR::PluginType type, enum VSTScanM
        /* crate cache/whitelist */
        infofile = vstfx_infofile_for_write (dllpath);
        if (!infofile) {
-               warningmsg << "Cannot cache VST information for " << dllpath << ": cannot create new FST info file." << endmsg;
+               PBD::warning << "Cannot cache VST information for " << dllpath << ": cannot create new FST info file." << endmsg;
                return infos;
        } else {
                vstfx_write_info_file (infofile, infos);
@@ -1013,7 +1017,7 @@ get_personal_vst_blacklist_dir() {
        /* if the directory doesn't exist, try to create it */
        if (!Glib::file_test (dir, Glib::FILE_TEST_IS_DIR)) {
                if (g_mkdir (dir.c_str (), 0700)) {
-                       errormsg << "Cannot create VST blacklist folder '" << dir << "'" << endmsg;
+                       PBD::error << "Cannot create VST blacklist folder '" << dir << "'" << endmsg;
                        //exit(1);
                }
        }
@@ -1026,7 +1030,7 @@ get_personal_vst_info_cache_dir() {
        /* if the directory doesn't exist, try to create it */
        if (!Glib::file_test (dir, Glib::FILE_TEST_IS_DIR)) {
                if (g_mkdir (dir.c_str (), 0700)) {
-                       errormsg << "Cannot create VST info folder '" << dir << "'" << endmsg;
+                       PBD::error << "Cannot create VST info folder '" << dir << "'" << endmsg;
                        //exit(1);
                }
        }
index 52e139db84fab0bcc7ab722006271cac9b4e2931..24020e835672f4c2d2de5029706082e600b887a7 100644 (file)
@@ -25,7 +25,6 @@
 
 #include "pbd/floating.h"
 #include "pbd/locale_guard.h"
-#include "pbd/pathscanner.h"
 
 #include "ardour/vst_plugin.h"
 #include "ardour/vestige/aeffectx.h"
@@ -250,6 +249,7 @@ VSTPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc)
 {
        VstParameterProperties prop;
 
+       memset (&prop, 0, sizeof (VstParameterProperties));
        desc.min_unbound = false;
        desc.max_unbound = false;
        prop.flags = 0;
@@ -257,6 +257,7 @@ VSTPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc)
        if (_plugin->dispatcher (_plugin, effGetParameterProperties, which, 0, &prop, 0)) {
 
                /* i have yet to find or hear of a VST plugin that uses this */
+               /* RG: faust2vsti does use this :) */
 
                if (prop.flags & kVstParameterUsesIntegerMinMax) {
                        desc.lower = prop.minInteger;
@@ -287,6 +288,10 @@ VSTPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc)
                        desc.largestep = desc.step * 10.0f;
                }
 
+               if (strlen(prop.label) == 0) {
+                       _plugin->dispatcher (_plugin, effGetParamName, which, 0, prop.label, 0);
+               }
+
                desc.toggled = prop.flags & kVstParameterIsSwitch;
                desc.logarithmic = false;
                desc.sr_dependent = false;
index 4a500645452f046d68c7855799d602e579539dcc..3ce94d892b5ca6e55ecc108ea02ac40d3b0beafc 100644 (file)
@@ -63,6 +63,7 @@ libardour_sources = [
         'data_type.cc',
         'default_click.cc',
         'debug.cc',
+        'delayline.cc',
         'delivery.cc',
         'directory_names.cc',
         'diskstream.cc',
@@ -130,6 +131,7 @@ libardour_sources = [
         'mix.cc',
         'monitor_processor.cc',
         'mtc_slave.cc',
+        'mididm.cc',
         'mtdm.cc',
         'mute_master.cc',
         'onset_detector.cc',
@@ -194,6 +196,7 @@ libardour_sources = [
         'sndfile_helpers.cc',
         'sndfileimportable.cc',
         'sndfilesource.cc',
+        'soundcloud_upload.cc',
         'source.cc',
         'source_factory.cc',
         'speakers.cc',
diff --git a/libs/ardouralsautil/ardouralsautil/devicelist.h b/libs/ardouralsautil/ardouralsautil/devicelist.h
new file mode 100644 (file)
index 0000000..2f8c2c4
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2013 Paul Davis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __libardouralsautil_devicelist_h__
+#define __libardouralsautil_devicelist_h__
+
+#include <string>
+#include <map>
+namespace ARDOUR {
+
+       void get_alsa_audio_device_names (std::map<std::string, std::string>& devices);
+       void get_alsa_rawmidi_device_names (std::map<std::string, std::string>& devices);
+       void get_alsa_sequencer_names (std::map<std::string, std::string>& devices);
+       int card_to_num(const char* device_name);
+
+}
+#endif
diff --git a/libs/ardouralsautil/ardouralsautil/reserve.h b/libs/ardouralsautil/ardouralsautil/reserve.h
new file mode 100644 (file)
index 0000000..6527bd7
--- /dev/null
@@ -0,0 +1,88 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: t -*-*/
+
+#ifndef fooreservehfoo
+#define fooreservehfoo
+
+/***
+  Copyright 2009 Lennart Poettering
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+***/
+
+#include <dbus/dbus.h>
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct rd_device rd_device;
+
+/* Prototype for a function that is called whenever someone else wants
+ * your application to release the device it has locked. A return
+ * value <= 0 denies the request, a positive return value agrees to
+ * it. Before returning your application should close the device in
+ * question completely to make sure the new application may access
+ * it. */
+typedef int (*rd_request_cb_t)(
+       rd_device *d,
+       int forced);                  /* Non-zero if an application forcibly took the lock away without asking. If this is the case then the return value of this call is ignored. */
+
+/* Try to lock the device. Returns 0 on success, a negative errno
+ * style return value on error. The DBus error might be set as well if
+ * the error was caused D-Bus. */
+int rd_acquire(
+       rd_device **d,                /* On success a pointer to the newly allocated rd_device object will be filled in here */
+       DBusConnection *connection,   /* Session bus (when D-Bus learns about user busses we should switch to user busses) */
+       const char *device_name,      /* The device to lock, e.g. "Audio0" */
+       const char *application_name, /* A human readable name of the application, e.g. "PulseAudio Sound Server" */
+       int32_t priority,             /* The priority for this application. If unsure use 0 */
+       rd_request_cb_t request_cb,   /* Will be called whenever someone requests that this device shall be released. May be NULL if priority is INT32_MAX */
+       DBusError *error);            /* If we fail due to a D-Bus related issue the error will be filled in here. May be NULL. */
+
+/* Unlock (if needed) and destroy an rd_device object again */
+void rd_release(rd_device *d);
+
+/* Set the application device name for an rd_device object. Returns 0
+ * on success, a negative errno style return value on error. */
+int rd_set_application_device_name(rd_device *d, const char *name);
+
+/* Attach a userdata pointer to an rd_device */
+void rd_set_userdata(rd_device *d, void *userdata);
+
+/* Query the userdata pointer from an rd_device. Returns NULL if no
+ * userdata was set. */
+void* rd_get_userdata(rd_device *d);
+
+/* Helper function to get the unique connection name owning a given
+ * name. Returns 0 on success, a negative errno style return value on
+ * error. */
+int rd_dbus_get_name_owner(
+       DBusConnection *connection,
+       const char *name,
+       char **name_owner,
+       DBusError *error);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libs/ardouralsautil/devicelist.cc b/libs/ardouralsautil/devicelist.cc
new file mode 100644 (file)
index 0000000..b20df10
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2013 Paul Davis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <alsa/asoundlib.h>
+#include "pbd/convert.h"
+#include "ardouralsautil/devicelist.h"
+
+using namespace std;
+
+void
+ARDOUR::get_alsa_audio_device_names (std::map<std::string, std::string>& devices)
+{
+       snd_ctl_t *handle;
+       snd_ctl_card_info_t *info;
+       snd_pcm_info_t *pcminfo;
+       snd_ctl_card_info_alloca(&info);
+       snd_pcm_info_alloca(&pcminfo);
+       string devname;
+       int cardnum = -1;
+       int device = -1;
+
+       while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
+
+               devname = "hw:";
+               devname += PBD::to_string (cardnum, std::dec);
+
+               if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
+
+                       if (snd_ctl_card_info (handle, info) < 0) {
+                               continue;
+                       }
+
+                       string card_name = snd_ctl_card_info_get_name (info);
+
+                       /* change devname to use ID, not number */
+
+                       devname = "hw:";
+                       devname += snd_ctl_card_info_get_id (info);
+
+                       while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
+
+                               /* only detect duplex devices here. more
+                                * complex arrangements are beyond our scope
+                                */
+
+                               snd_pcm_info_set_device (pcminfo, device);
+                               snd_pcm_info_set_subdevice (pcminfo, 0);
+                               snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_CAPTURE);
+
+                               if (snd_ctl_pcm_info (handle, pcminfo) < 0) {
+                                       continue;
+                               }
+
+                               snd_pcm_info_set_device (pcminfo, device);
+                               snd_pcm_info_set_subdevice (pcminfo, 0);
+                               snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
+
+                               if (snd_ctl_pcm_info (handle, pcminfo) < 0) {
+                                       continue;
+                               }
+                               devname += ',';
+                               devname += PBD::to_string (device, std::dec);
+                               devices.insert (std::make_pair (card_name, devname));
+                       }
+
+                       snd_ctl_close(handle);
+               }
+       }
+}
+
+void
+ARDOUR::get_alsa_rawmidi_device_names (std::map<std::string, std::string>& devices)
+{
+       int cardnum = -1;
+       snd_ctl_card_info_t *cinfo;
+       snd_ctl_card_info_alloca (&cinfo);
+       while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
+               snd_ctl_t *handle;
+               std::string devname = "hw:";
+               devname += PBD::to_string (cardnum, std::dec);
+               if (snd_ctl_open (&handle, devname.c_str (), 0) >= 0 && snd_ctl_card_info (handle, cinfo) >= 0) {
+                       int device = -1;
+                       while (snd_ctl_rawmidi_next_device (handle, &device) >= 0 && device >= 0) {
+                               snd_rawmidi_info_t *info;
+                               snd_rawmidi_info_alloca (&info);
+                               snd_rawmidi_info_set_device (info, device);
+
+                               int subs_in, subs_out;
+
+                               snd_rawmidi_info_set_stream (info, SND_RAWMIDI_STREAM_INPUT);
+                               if (snd_ctl_rawmidi_info (handle, info) >= 0) {
+                                       subs_in = snd_rawmidi_info_get_subdevices_count (info);
+                               } else {
+                                       subs_in = 0;
+                               }
+
+                               snd_rawmidi_info_set_stream (info, SND_RAWMIDI_STREAM_OUTPUT);
+                               if (snd_ctl_rawmidi_info (handle, info) >= 0) {
+                                       subs_out = snd_rawmidi_info_get_subdevices_count (info);
+                               } else {
+                                       subs_out = 0;
+                               }
+
+                               const int subs = subs_in > subs_out ? subs_in : subs_out;
+                               if (!subs) {
+                                       continue;
+                               }
+
+                               for (int sub = 0; sub < subs; ++sub) {
+                                       snd_rawmidi_info_set_stream (info, sub < subs_in ?
+                                                       SND_RAWMIDI_STREAM_INPUT :
+                                                       SND_RAWMIDI_STREAM_OUTPUT);
+
+                                       snd_rawmidi_info_set_subdevice (info, sub);
+                                       if (snd_ctl_rawmidi_info (handle, info) < 0) {
+                                               continue;
+                                       }
+
+                                       const char *sub_name = snd_rawmidi_info_get_subdevice_name (info);
+                                       if (sub == 0 && sub_name[0] == '\0') {
+                                               devname = "hw:";
+                                               devname += snd_ctl_card_info_get_id (cinfo);
+                                               devname += ",";
+                                               devname += PBD::to_string (device, std::dec);
+
+                                               std::string card_name;
+                                               card_name = snd_rawmidi_info_get_name (info);
+                                               card_name += " (";
+                                               if (sub < subs_in) card_name += "I";
+                                               if (sub < subs_out) card_name += "O";
+                                               card_name += ")";
+
+                                               devices.insert (std::make_pair (card_name, devname));
+                                               break;
+                                       } else {
+                                               devname = "hw:";
+                                               devname += snd_ctl_card_info_get_id (cinfo);
+                                               devname += ",";
+                                               devname += PBD::to_string (device, std::dec);
+                                               devname += ",";
+                                               devname += PBD::to_string (sub, std::dec);
+
+                                               std::string card_name = sub_name;
+                                               card_name += " (";
+                                               if (sub < subs_in) card_name += "I";
+                                               if (sub < subs_out) card_name += "O";
+                                               card_name += ")";
+                                               devices.insert (std::make_pair (card_name, devname));
+                                       }
+                               }
+                       }
+                       snd_ctl_close (handle);
+               }
+       }
+}
+
+void
+ARDOUR::get_alsa_sequencer_names (std::map<std::string, std::string>& devices)
+{
+       snd_seq_t *seq= NULL;
+       snd_seq_client_info_t *cinfo;
+       snd_seq_port_info_t *pinfo;
+
+       snd_seq_client_info_alloca (&cinfo);
+       snd_seq_port_info_alloca (&pinfo);
+
+       if (snd_seq_open (&seq, "hw", SND_SEQ_OPEN_DUPLEX, 0) < 0) {
+               return;
+       }
+
+       snd_seq_client_info_set_client(cinfo, -1);
+       while (snd_seq_query_next_client (seq, cinfo) >= 0) {
+               int client = snd_seq_client_info_get_client (cinfo);
+               if (client == SND_SEQ_CLIENT_SYSTEM) {
+                       continue;
+               }
+               if (!strcmp (snd_seq_client_info_get_name(cinfo), "Midi Through")) {
+                       continue;
+               }
+               snd_seq_port_info_set_client (pinfo, client);
+               snd_seq_port_info_set_port (pinfo, -1);
+
+               while (snd_seq_query_next_port (seq, pinfo) >= 0) {
+                       int caps = snd_seq_port_info_get_capability(pinfo);
+                       if (0 == (caps & (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_WRITE))) {
+                               continue;
+                       }
+                       if (caps & SND_SEQ_PORT_CAP_NO_EXPORT) {
+                               continue;
+                       }
+                       std::string card_name;
+                       card_name = snd_seq_port_info_get_name (pinfo);
+
+                       card_name += " (";
+                       if (caps & SND_SEQ_PORT_CAP_READ) card_name += "I";
+                       if (caps & SND_SEQ_PORT_CAP_WRITE) card_name += "O";
+                       card_name += ")";
+
+                       std::string devname;
+                       devname = PBD::to_string(snd_seq_port_info_get_client (pinfo), std::dec);
+                       devname += ":";
+                       devname += PBD::to_string(snd_seq_port_info_get_port (pinfo), std::dec);
+                       devices.insert (std::make_pair (card_name, devname));
+               }
+       }
+       snd_seq_close (seq);
+}
+
+int
+ARDOUR::card_to_num(const char* device_name)
+{
+       char* ctl_name;
+       const char * comma;
+       snd_ctl_t* ctl_handle;
+       int i = -1;
+
+       if (strncasecmp(device_name, "plughw:", 7) == 0) {
+               device_name += 4;
+       }
+       if (!(comma = strchr(device_name, ','))) {
+               ctl_name = strdup(device_name);
+       } else {
+               ctl_name = strndup(device_name, comma - device_name);
+       }
+
+       if (snd_ctl_open (&ctl_handle, ctl_name, 0) >= 0) {
+               snd_ctl_card_info_t *card_info;
+               snd_ctl_card_info_alloca (&card_info);
+               if (snd_ctl_card_info(ctl_handle, card_info) >= 0) {
+                       i = snd_ctl_card_info_get_card(card_info);
+               }
+               snd_ctl_close(ctl_handle);
+       }
+       free(ctl_name);
+       return i;
+}
diff --git a/libs/ardouralsautil/request_device.c b/libs/ardouralsautil/request_device.c
new file mode 100644 (file)
index 0000000..4ba80ec
--- /dev/null
@@ -0,0 +1,256 @@
+/* alsa/ardour dbus device request tool
+ *
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+// NB generate man-page with
+// help2man -N -n "alsa/ardour dbus device request tool" -o ardour-request-device.1 ./build/libs/ardouralsautil/ardour-request-device
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <signal.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "ardouralsautil/reserve.h"
+
+#ifndef ARD_PROG_NAME
+#define ARD_PROG_NAME "alsa_request_device"
+#endif
+#ifndef ARD_APPL_NAME
+#define ARD_APPL_NAME "ALSA User"
+#endif
+#ifndef VERSION
+#define VERSION "v0.3"
+#endif
+
+static int run = 1;
+static int release_wait_for_signal = 0;
+static pid_t parent_pid = 0;
+
+static void wearedone(int sig) {
+       (void) sig; // skip 'unused variable' compiler warning;
+       fprintf(stderr, "caught signal - shutting down.\n");
+       run=0;
+}
+
+static int stdin_available(void) {
+       errno = 0;
+       if (fcntl(STDIN_FILENO, F_GETFD) == 1) return 0;
+       return errno != EBADF;
+}
+
+static void print_version(int status) {
+       printf (ARD_PROG_NAME " " VERSION "\n\n");
+       printf (
+               "Copyright (C) 2014 Robin Gareus <robin@gareus.org>\n"
+               "This is free software; see the source for copying conditions.  There is NO\n"
+               "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"
+               );
+       exit (status);
+}
+
+static void usage(int status) {
+       printf (ARD_PROG_NAME " - DBus Audio Reservation Utility.\n");
+       printf ("Usage: " ARD_PROG_NAME " [ OPTIONS ] <Audio-Device-ID>\n");
+       printf ("Options:\n\
+      -h, --help                 display this help and exit\n\
+      -p, --priority <int>       reservation priority (default: int32_max)\n\
+      -P, --pid <int>            process-id to watch (default 0: none)\n\
+      -n, --name <string>        application name to use for registration\n\
+      -V, --version              print version information and exit\n\
+      -w, --releasewait          wait for signal on yield-release\n\
+");
+
+       printf ("\n\
+This tool issues a dbus request to reserve an ALSA Audio-device.\n\
+If successful other users of the device (e.g. pulseaudio) will\n\
+release the device.\n\
+\n\
+" ARD_PROG_NAME " by default announces itself as \"" ARD_APPL_NAME "\"\n\
+and uses the maximum possible priority for requesting the device.\n\
+These settings can be overriden using the -n and -p options respectively.\n\
+\n\
+If a PID is given the tool will watch the process and if that is not running\n\
+release the device and exit.  Otherwise " ARD_PROG_NAME " runs until\n\
+either stdin is closed, a SIGINT or SIGTERM is received or some other\n\
+application requests the device with a higher priority.\n\
+\n\
+Without the -w option, " ARD_PROG_NAME " yields the device after 500ms to\n\
+any higher-priority request. With the -w option this tool waits until it\n\
+for SIGINT or SIGTERM - but at most 4 sec to acknowledge before releasing.\n\
+\n\
+The audio-device-id is a string e.g. 'Audio1'\n\
+\n\
+Examples:\n\
+" ARD_PROG_NAME " Audio0\n\
+\n");
+
+       printf ("Report bugs to Robin Gareus <robin@gareus.org>\n");
+       exit (status);
+}
+
+static struct option const long_options[] =
+{
+       {"help", no_argument, 0, 'h'},
+       {"name", required_argument, 0, 'n'},
+       {"pid", required_argument, 0, 'P'},
+       {"priority", required_argument, 0, 'p'},
+       {"version", no_argument, 0, 'V'},
+       {"releasewait", no_argument, 0, 'w'},
+       {NULL, 0, NULL, 0}
+};
+
+static int request_cb(rd_device *d, int forced) {
+       (void) d; // skip 'unused variable' compiler warning;
+       (void) forced; // skip 'unused variable' compiler warning;
+       fprintf(stdout, "Received higher priority request - releasing device.\n");
+       fflush(stdout);
+       if(!release_wait_for_signal) {
+               usleep (500000);
+               run = 0;
+       } else if (run) {
+               int timeout = 4000;
+               fprintf(stdout, "Waiting for acknowledge signal to release.\n");
+               while (release_wait_for_signal && run && --timeout) {
+                       if (!stdin_available()) {
+                               break;
+                       }
+                       if (parent_pid > 0 && kill (parent_pid, 0)) {
+                               break;
+                       }
+                       usleep (1000);
+               }
+               run = 0;
+       }
+       return 1; // OK
+}
+
+int main(int argc, char **argv) {
+       DBusConnection* dbus_connection = NULL;
+       rd_device * reserved_device = NULL;
+       DBusError error;
+       int ret, c;
+
+       int32_t priority = INT32_MAX;
+       char *name = strdup(ARD_APPL_NAME);
+
+       while ((c = getopt_long (argc, argv,
+                                       "h"  /* help */
+                                       "n:" /* name */
+                                       "P:" /* pid */
+                                       "p:" /* priority */
+                                       "V"  /* version */
+                                       "w", /* release wait for signal */
+                                       long_options, (int *) 0)) != EOF)
+       {
+               switch (c) {
+                       case 'h':
+                               free(name);
+                               usage(EXIT_SUCCESS);
+                               break;
+                       case 'n':
+                               free(name);
+                               name = strdup(optarg);
+                               break;
+                       case 'p':
+                               priority = atoi (optarg);
+                               if (priority < 0) priority = 0;
+                               break;
+                       case 'P':
+                               parent_pid = atoi (optarg);
+                               break;
+                       case 'V':
+                               free(name);
+                               print_version(EXIT_SUCCESS);
+                               break;
+                       case 'w':
+                               release_wait_for_signal = 1;
+                               break;
+                       default:
+                               free(name);
+                               usage(EXIT_FAILURE);
+                               break;
+               }
+       }
+
+       if (optind + 1 != argc) {
+               free(name);
+               usage(EXIT_FAILURE);
+       }
+       const char *device_name = argv[optind];
+
+       if (parent_pid > 0 && kill (parent_pid, 0)) {
+               fprintf(stderr, "Given PID to watch is not running.\n");
+               free(name);
+               return EXIT_FAILURE;
+       }
+
+       dbus_error_init(&error);
+
+       if (!(dbus_connection = dbus_bus_get (DBUS_BUS_SESSION, &error))) {
+               fprintf(stderr, "Failed to connect to session bus for device reservation: %s\n", error.message ? error.message : "unknown error.");
+               dbus_error_free(&error);
+               free(name);
+               return EXIT_FAILURE;
+       }
+
+       if ((ret = rd_acquire (
+                                       &reserved_device,
+                                       dbus_connection,
+                                       device_name,
+                                       name,
+                                       priority,
+                                       request_cb,
+                                       &error)) < 0)
+       {
+               fprintf(stderr, "Failed to acquire device: '%s'\n%s\n", device_name, (error.message ? error.message : strerror(-ret)));
+               dbus_error_free(&error);
+               dbus_connection_unref(dbus_connection);
+               free(name);
+               return EXIT_FAILURE;
+       }
+
+       fprintf(stdout, "Acquired audio-card '%s'\n", device_name);
+       fprintf(stdout, "Press Ctrl+C or close stdin to release the device.\n");
+       fflush(stdout);
+
+       signal(SIGTERM, wearedone);
+       signal(SIGINT, wearedone);
+
+       while (run && dbus_connection_read_write_dispatch (dbus_connection, 200)) {
+               if (!stdin_available()) {
+                       fprintf(stderr, "stdin closed - releasing device.\n");
+                       break;
+               }
+               if (parent_pid > 0 && kill (parent_pid, 0)) {
+                       fprintf(stderr, "watched PID no longer exists - releasing device.\n");
+                       break;
+               }
+       }
+
+       rd_release (reserved_device);
+       fprintf(stdout, "Released audio-card '%s'\n", device_name);
+
+       dbus_connection_unref(dbus_connection);
+       dbus_error_free(&error);
+       free(name);
+       return EXIT_SUCCESS;
+}
diff --git a/libs/ardouralsautil/reserve.c b/libs/ardouralsautil/reserve.c
new file mode 100644 (file)
index 0000000..78df714
--- /dev/null
@@ -0,0 +1,685 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: t -*-*/
+
+/***
+  Copyright 2009 Lennart Poettering
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+***/
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "ardouralsautil/reserve.h"
+
+#ifndef DBUS_TIMEOUT_USE_DEFAULT
+#define DBUS_TIMEOUT_USE_DEFAULT (-1)
+#endif
+
+struct rd_device {
+       int ref;
+
+       char *device_name;
+       char *application_name;
+       char *application_device_name;
+       char *service_name;
+       char *object_path;
+       int32_t priority;
+
+       DBusConnection *connection;
+
+       unsigned owning:1;
+       unsigned registered:1;
+       unsigned filtering:1;
+       unsigned gave_up:1;
+
+       rd_request_cb_t request_cb;
+       void *userdata;
+};
+
+#define SERVICE_PREFIX "org.freedesktop.ReserveDevice1."
+#define OBJECT_PREFIX "/org/freedesktop/ReserveDevice1/"
+
+static const char introspection[] =
+       DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
+       "<node>"
+       " <!-- If you are looking for documentation make sure to check out\n"
+       "      http://git.0pointer.de/?p=reserve.git;a=blob;f=reserve.txt -->\n"
+       " <interface name=\"org.freedesktop.ReserveDevice1\">"
+       "  <method name=\"RequestRelease\">"
+       "   <arg name=\"priority\" type=\"i\" direction=\"in\"/>"
+       "   <arg name=\"result\" type=\"b\" direction=\"out\"/>"
+       "  </method>"
+       "  <property name=\"Priority\" type=\"i\" access=\"read\"/>"
+       "  <property name=\"ApplicationName\" type=\"s\" access=\"read\"/>"
+       "  <property name=\"ApplicationDeviceName\" type=\"s\" access=\"read\"/>"
+       " </interface>"
+       " <interface name=\"org.freedesktop.DBus.Properties\">"
+       "  <method name=\"Get\">"
+       "   <arg name=\"interface\" direction=\"in\" type=\"s\"/>"
+       "   <arg name=\"property\" direction=\"in\" type=\"s\"/>"
+       "   <arg name=\"value\" direction=\"out\" type=\"v\"/>"
+       "  </method>"
+       " </interface>"
+       " <interface name=\"org.freedesktop.DBus.Introspectable\">"
+       "  <method name=\"Introspect\">"
+       "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"
+       "  </method>"
+       " </interface>"
+       "</node>";
+
+static dbus_bool_t add_variant(
+       DBusMessage *m,
+       int type,
+       const void *data) {
+
+       DBusMessageIter iter, sub;
+       char t[2];
+
+       t[0] = (char) type;
+       t[1] = 0;
+
+       dbus_message_iter_init_append(m, &iter);
+
+       if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, t, &sub))
+               return FALSE;
+
+       if (!dbus_message_iter_append_basic(&sub, type, data))
+               return FALSE;
+
+       if (!dbus_message_iter_close_container(&iter, &sub))
+               return FALSE;
+
+       return TRUE;
+}
+
+static DBusHandlerResult object_handler(
+       DBusConnection *c,
+       DBusMessage *m,
+       void *userdata) {
+
+       rd_device *d;
+       DBusError error;
+       DBusMessage *reply = NULL;
+
+       dbus_error_init(&error);
+
+       d = userdata;
+       assert(d->ref >= 1);
+
+       if (dbus_message_is_method_call(
+                   m,
+                   "org.freedesktop.ReserveDevice1",
+                   "RequestRelease")) {
+
+               int32_t priority;
+               dbus_bool_t ret;
+
+               if (!dbus_message_get_args(
+                           m,
+                           &error,
+                           DBUS_TYPE_INT32, &priority,
+                           DBUS_TYPE_INVALID))
+                       goto invalid;
+
+               ret = FALSE;
+
+               if (priority > d->priority && d->request_cb) {
+                       d->ref++;
+
+                       if (d->request_cb(d, 0) > 0) {
+                               ret = TRUE;
+                               d->gave_up = 1;
+                       }
+
+                       rd_release(d);
+               }
+
+               if (!(reply = dbus_message_new_method_return(m)))
+                       goto oom;
+
+               if (!dbus_message_append_args(
+                           reply,
+                           DBUS_TYPE_BOOLEAN, &ret,
+                           DBUS_TYPE_INVALID))
+                       goto oom;
+
+               if (!dbus_connection_send(c, reply, NULL))
+                       goto oom;
+
+               dbus_message_unref(reply);
+
+               return DBUS_HANDLER_RESULT_HANDLED;
+
+       } else if (dbus_message_is_method_call(
+                          m,
+                          "org.freedesktop.DBus.Properties",
+                          "Get")) {
+
+               const char *interface, *property;
+
+               if (!dbus_message_get_args(
+                           m,
+                           &error,
+                           DBUS_TYPE_STRING, &interface,
+                           DBUS_TYPE_STRING, &property,
+                           DBUS_TYPE_INVALID))
+                       goto invalid;
+
+               if (strcmp(interface, "org.freedesktop.ReserveDevice1") == 0) {
+                       const char *empty = "";
+
+                       if (strcmp(property, "ApplicationName") == 0 && d->application_name) {
+                               if (!(reply = dbus_message_new_method_return(m)))
+                                       goto oom;
+
+                               if (!add_variant(
+                                           reply,
+                                           DBUS_TYPE_STRING,
+                                           d->application_name ? (const char * const *) &d->application_name : &empty))
+                                       goto oom;
+
+                       } else if (strcmp(property, "ApplicationDeviceName") == 0) {
+                               if (!(reply = dbus_message_new_method_return(m)))
+                                       goto oom;
+
+                               if (!add_variant(
+                                           reply,
+                                           DBUS_TYPE_STRING,
+                                           d->application_device_name ? (const char * const *) &d->application_device_name : &empty))
+                                       goto oom;
+
+                       } else if (strcmp(property, "Priority") == 0) {
+                               if (!(reply = dbus_message_new_method_return(m)))
+                                       goto oom;
+
+                               if (!add_variant(
+                                           reply,
+                                           DBUS_TYPE_INT32,
+                                           &d->priority))
+                                       goto oom;
+                       } else {
+                               if (!(reply = dbus_message_new_error_printf(
+                                             m,
+                                             DBUS_ERROR_UNKNOWN_METHOD,
+                                             "Unknown property %s",
+                                             property)))
+                                       goto oom;
+                       }
+
+                       if (!dbus_connection_send(c, reply, NULL))
+                               goto oom;
+
+                       dbus_message_unref(reply);
+
+                       return DBUS_HANDLER_RESULT_HANDLED;
+               }
+
+       } else if (dbus_message_is_method_call(
+                          m,
+                          "org.freedesktop.DBus.Introspectable",
+                          "Introspect")) {
+                           const char *i = introspection;
+
+               if (!(reply = dbus_message_new_method_return(m)))
+                       goto oom;
+
+               if (!dbus_message_append_args(
+                           reply,
+                           DBUS_TYPE_STRING,
+                           &i,
+                           DBUS_TYPE_INVALID))
+                       goto oom;
+
+               if (!dbus_connection_send(c, reply, NULL))
+                       goto oom;
+
+               dbus_message_unref(reply);
+
+               return DBUS_HANDLER_RESULT_HANDLED;
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+invalid:
+       if (reply)
+               dbus_message_unref(reply);
+
+       if (!(reply = dbus_message_new_error(
+                     m,
+                     DBUS_ERROR_INVALID_ARGS,
+                     "Invalid arguments")))
+               goto oom;
+
+       if (!dbus_connection_send(c, reply, NULL))
+               goto oom;
+
+       dbus_message_unref(reply);
+
+       dbus_error_free(&error);
+
+       return DBUS_HANDLER_RESULT_HANDLED;
+
+oom:
+       if (reply)
+               dbus_message_unref(reply);
+
+       dbus_error_free(&error);
+
+       return DBUS_HANDLER_RESULT_NEED_MEMORY;
+}
+
+static DBusHandlerResult filter_handler(
+       DBusConnection *c,
+       DBusMessage *m,
+       void *userdata) {
+
+       rd_device *d;
+       DBusError error;
+       char *name_owner = NULL;
+
+       dbus_error_init(&error);
+
+       d = userdata;
+       assert(d->ref >= 1);
+
+       if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameLost")) {
+               const char *name;
+
+               if (!dbus_message_get_args(
+                           m,
+                           &error,
+                           DBUS_TYPE_STRING, &name,
+                           DBUS_TYPE_INVALID))
+                       goto invalid;
+
+               if (strcmp(name, d->service_name) == 0 && d->owning) {
+                       /* Verify the actual owner of the name to avoid leaked NameLost
+                        * signals from previous reservations. The D-Bus daemon will send
+                        * all messages asynchronously in the correct order, but we could
+                        * potentially process them too late due to the pseudo-blocking
+                        * call mechanism used during both acquisition and release. This
+                        * can happen if we release the device and immediately after
+                        * reacquire it before NameLost is processed. */
+                       if (!d->gave_up) {
+                               const char *un;
+
+                               if ((un = dbus_bus_get_unique_name(c)) && rd_dbus_get_name_owner(c, d->service_name, &name_owner, &error) == 0)
+                                       if (name_owner && strcmp(name_owner, un) == 0)
+                                               goto invalid; /* Name still owned by us */
+                       }
+
+                       d->owning = 0;
+
+                       if (!d->gave_up)  {
+                               d->ref++;
+
+                               if (d->request_cb)
+                                       d->request_cb(d, 1);
+                               d->gave_up = 1;
+
+                               rd_release(d);
+                       }
+
+               }
+       }
+
+invalid:
+       free(name_owner);
+       dbus_error_free(&error);
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+static const struct DBusObjectPathVTable vtable ={
+       .message_function = object_handler
+};
+
+int rd_acquire(
+       rd_device **_d,
+       DBusConnection *connection,
+       const char *device_name,
+       const char *application_name,
+       int32_t priority,
+       rd_request_cb_t request_cb,
+       DBusError *error) {
+
+       rd_device *d = NULL;
+       int r, k;
+       DBusError _error;
+       DBusMessage *m = NULL, *reply = NULL;
+       dbus_bool_t good;
+
+       if (!error)
+               error = &_error;
+
+       dbus_error_init(error);
+
+       if (!_d)
+               return -EINVAL;
+
+       if (!connection)
+               return -EINVAL;
+
+       if (!device_name)
+               return -EINVAL;
+
+       if (!request_cb && priority != INT32_MAX)
+               return -EINVAL;
+
+       if (!(d = calloc(sizeof(rd_device), 1)))
+               return -ENOMEM;
+
+       d->ref = 1;
+
+       if (!(d->device_name = strdup(device_name))) {
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       if (!(d->application_name = strdup(application_name))) {
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       d->priority = priority;
+       d->connection = dbus_connection_ref(connection);
+       d->request_cb = request_cb;
+
+       if (!(d->service_name = malloc(sizeof(SERVICE_PREFIX) + strlen(device_name)))) {
+               r = -ENOMEM;
+               goto fail;
+       }
+       sprintf(d->service_name, SERVICE_PREFIX "%s", d->device_name);
+
+       if (!(d->object_path = malloc(sizeof(OBJECT_PREFIX) + strlen(device_name)))) {
+               r = -ENOMEM;
+               goto fail;
+       }
+       sprintf(d->object_path, OBJECT_PREFIX "%s", d->device_name);
+
+       if ((k = dbus_bus_request_name(
+                    d->connection,
+                    d->service_name,
+                    DBUS_NAME_FLAG_DO_NOT_QUEUE|
+                    (priority < INT32_MAX ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT : 0),
+                    error)) < 0) {
+               r = -EIO;
+               goto fail;
+       }
+
+       if (k == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
+               goto success;
+
+       if (k != DBUS_REQUEST_NAME_REPLY_EXISTS) {
+               r = -EIO;
+               goto fail;
+       }
+
+       if (priority <= INT32_MIN) {
+               r = -EBUSY;
+               goto fail;
+       }
+
+       if (!(m = dbus_message_new_method_call(
+                     d->service_name,
+                     d->object_path,
+                     "org.freedesktop.ReserveDevice1",
+                     "RequestRelease"))) {
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       if (!dbus_message_append_args(
+                   m,
+                   DBUS_TYPE_INT32, &d->priority,
+                   DBUS_TYPE_INVALID)) {
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       if (!(reply = dbus_connection_send_with_reply_and_block(
+                     d->connection,
+                     m,
+                     5000, /* 5s */
+                     error))) {
+
+               if (dbus_error_has_name(error, DBUS_ERROR_TIMED_OUT) ||
+                   dbus_error_has_name(error, DBUS_ERROR_UNKNOWN_METHOD) ||
+                   dbus_error_has_name(error, DBUS_ERROR_NO_REPLY)) {
+                       /* This must be treated as denied. */
+                       r = -EBUSY;
+                       goto fail;
+               }
+
+               r = -EIO;
+               goto fail;
+       }
+
+       if (!dbus_message_get_args(
+                   reply,
+                   error,
+                   DBUS_TYPE_BOOLEAN, &good,
+                   DBUS_TYPE_INVALID)) {
+               r = -EIO;
+               goto fail;
+       }
+
+       if (!good) {
+               r = -EBUSY;
+               goto fail;
+       }
+
+       if ((k = dbus_bus_request_name(
+                    d->connection,
+                    d->service_name,
+                    DBUS_NAME_FLAG_DO_NOT_QUEUE|
+                    (priority < INT32_MAX ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT : 0)|
+                    DBUS_NAME_FLAG_REPLACE_EXISTING,
+                    error)) < 0) {
+               r = -EIO;
+               goto fail;
+       }
+
+       if (k != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+               r = -EIO;
+               goto fail;
+       }
+
+success:
+       d->owning = 1;
+
+       if (!(dbus_connection_register_object_path(
+                     d->connection,
+                     d->object_path,
+                     &vtable,
+                     d))) {
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       d->registered = 1;
+
+       if (!dbus_connection_add_filter(
+                   d->connection,
+                   filter_handler,
+                   d,
+                   NULL)) {
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       d->filtering = 1;
+
+       *_d = d;
+       return 0;
+
+fail:
+       if (m)
+               dbus_message_unref(m);
+
+       if (reply)
+               dbus_message_unref(reply);
+
+       if (&_error == error)
+               dbus_error_free(&_error);
+
+       if (d)
+               rd_release(d);
+
+       return r;
+}
+
+void rd_release(
+       rd_device *d) {
+
+       if (!d)
+               return;
+
+       assert(d->ref > 0);
+
+       if (--d->ref > 0)
+               return;
+
+
+       if (d->filtering)
+               dbus_connection_remove_filter(
+                       d->connection,
+                       filter_handler,
+                       d);
+
+       if (d->registered)
+               dbus_connection_unregister_object_path(
+                       d->connection,
+                       d->object_path);
+
+       if (d->owning)
+               dbus_bus_release_name(
+                       d->connection,
+                       d->service_name,
+                       NULL);
+
+       free(d->device_name);
+       free(d->application_name);
+       free(d->application_device_name);
+       free(d->service_name);
+       free(d->object_path);
+
+       if (d->connection)
+               dbus_connection_unref(d->connection);
+
+       free(d);
+}
+
+int rd_set_application_device_name(rd_device *d, const char *n) {
+       char *t;
+
+       if (!d)
+               return -EINVAL;
+
+       assert(d->ref > 0);
+
+       if (!(t = strdup(n)))
+               return -ENOMEM;
+
+       free(d->application_device_name);
+       d->application_device_name = t;
+       return 0;
+}
+
+void rd_set_userdata(rd_device *d, void *userdata) {
+
+       if (!d)
+               return;
+
+       assert(d->ref > 0);
+       d->userdata = userdata;
+}
+
+void* rd_get_userdata(rd_device *d) {
+
+       if (!d)
+               return NULL;
+
+       assert(d->ref > 0);
+
+       return d->userdata;
+}
+
+int rd_dbus_get_name_owner(
+       DBusConnection *connection,
+       const char *name,
+       char **name_owner,
+       DBusError *error) {
+
+       DBusMessage *msg, *reply;
+       int r;
+
+       *name_owner = NULL;
+
+       if (!(msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner"))) {
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) {
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       reply = dbus_connection_send_with_reply_and_block(connection, msg, DBUS_TIMEOUT_USE_DEFAULT, error);
+       dbus_message_unref(msg);
+       msg = NULL;
+
+       if (reply) {
+               if (!dbus_message_get_args(reply, error, DBUS_TYPE_STRING, name_owner, DBUS_TYPE_INVALID)) {
+                       dbus_message_unref(reply);
+                       r = -EIO;
+                       goto fail;
+               }
+
+               *name_owner = strdup(*name_owner);
+               dbus_message_unref(reply);
+
+               if (!*name_owner) {
+                       r = -ENOMEM;
+                       goto fail;
+               }
+
+       } else if (dbus_error_has_name(error, "org.freedesktop.DBus.Error.NameHasNoOwner"))
+               dbus_error_free(error);
+       else {
+               r = -EIO;
+               goto fail;
+       }
+
+       return 0;
+
+fail:
+       if (msg)
+               dbus_message_unref(msg);
+
+       return r;
+}
diff --git a/libs/ardouralsautil/wscript b/libs/ardouralsautil/wscript
new file mode 100644 (file)
index 0000000..d32b2ab
--- /dev/null
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+from waflib.extras import autowaf as autowaf
+from waflib import Options
+import os
+import sys
+import re
+
+# Mandatory variables
+top = '.'
+out = 'build'
+
+path_prefix = 'libs/ardouralsautil/'
+
+def options(opt):
+    autowaf.set_options(opt)
+
+
+def configure(conf):
+    autowaf.configure(conf)
+    if re.search ("linux", sys.platform) != None and Options.options.dist_target != 'mingw':
+        autowaf.check_pkg(conf, 'alsa', uselib_store='ALSA')
+        autowaf.check_pkg(conf, 'dbus-1', uselib_store='DBUS', mandatory = False)
+
+def build(bld):
+    if re.search ("linux", sys.platform) != None:
+        if bld.is_defined('HAVE_ALSA'):
+            obj = bld(features = 'cxx cxxshlib')
+            obj.source = [
+                'devicelist.cc'
+            ]
+            obj.export_includes = ['.']
+            obj.includes = ['.']
+            obj.name     = 'ardouralsautil'
+            obj.target   = 'ardouralsautil'
+            obj.use      = 'libpbd'
+            obj.uselib   = [ 'ALSA' ]
+            obj.vnum     = '0.0.1'
+            obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardouralsautil')
+
+        if bld.env['BUILD_ALSABACKEND'] and bld.is_defined('HAVE_ALSA') and bld.is_defined('HAVE_DBUS'):
+            obj = bld(features = 'c cprogram')
+            obj.source = [
+                'reserve.c',
+                'request_device.c'
+            ]
+            obj.includes = ['.']
+            obj.target   = 'ardour-request-device'
+            obj.uselib   = [ 'DBUS' ]
+            obj.install_path = os.path.join(bld.env['LIBDIR'])
+            obj.defines = [
+                    '_POSIX_SOURCE',
+                    '_XOPEN_SOURCE=500',
+                    'ARD_PROG_NAME="ardour-request-device"',
+                    'ARD_APPL_NAME="Ardour ALSA Backend"',
+            ]
diff --git a/libs/backends/MSVCbackends/dummy_audiobackend.vcproj b/libs/backends/MSVCbackends/dummy_audiobackend.vcproj
new file mode 100644 (file)
index 0000000..95e26c2
--- /dev/null
@@ -0,0 +1,289 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+       ProjectType="Visual C++"
+       Version="8.00"
+       Name="dummy_audiobackend"
+       ProjectGUID="{A72277FF-4C0C-4851-9751-2F524507A052}"
+       RootNamespace="dummy_audiobackend"
+       >
+       <Platforms>
+               <Platform
+                       Name="Win32"
+               />
+       </Platforms>
+       <ToolFiles>
+       </ToolFiles>
+       <Configurations>
+               <Configuration
+                       Name="Debug 32|Win32"
+                       OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
+                       IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
+                       ConfigurationType="2"
+                       InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
+                       CharacterSet="2"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
+                               Optimization="0"
+                               AdditionalIncludeDirectories="..;..\dummy;..\..\ardour;..\..\pbd;..\..\timecode;..\..\evoral;&quot;..\..\midi++2&quot;;&quot;$(GenericIncludeFolder)\ardourext&quot;;&quot;$(GenericLibraryFolder)\glib-2.0\include&quot;;&quot;$(GenericIncludeFolder)\libsndfile&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0&quot;;&quot;$(GenericIncludeFolder)\cairo&quot;;&quot;$(GenericIncludeFolder)\freetype2&quot;;&quot;$(GenericIncludeFolder)\pango-1.0&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0\gdk&quot;;&quot;$(GenericIncludeFolder)\atk-2.0&quot;;&quot;$(GenericIncludeFolder)\lrdf&quot;;&quot;$(GenericIncludeFolder)\raptor&quot;;&quot;$(GenericIncludeFolder)\boost&quot;;..\.."
+                               PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;BUILDING_DUMMY_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_WINDOWS;_DEBUG;DEBUG=&quot;Debug&quot;;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME=&quot;\&quot;Mixbus\&quot;&quot;;PACKAGE=&quot;\&quot;libardour_dummy\&quot;&quot;;_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\&quot;Mod4&gt;&lt;Super\&quot;;IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\&quot;1.2\&quot;;CURRENT_SESSION_FILE_VERSION=3001"
+                               MinimalRebuild="true"
+                               RuntimeLibrary="3"
+                               WarningLevel="3"
+                               DebugInformationFormat="3"
+                               CompileAs="2"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalDependencies="$(DllPrefix)glibmm32-2.4-0D.lib $(DllPrefix)giomm32-2.4-0D.lib boost-regex32D.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0D.lib $(DllPrefix)gobject32-2.0-0D.lib $(DllPrefix)gmodule32-2.0-0D.lib $(DllPrefix)glib32-2.0-0D.lib $(DllPrefix)gio32-2.0-0D.lib $(DllPrefix)sigc++32-2.0D.lib $(DllPrefix)timecode32D.lib $(DllPrefix)ardour32D.lib $(DllPrefix)pbd32D.lib intlD.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib"
+                               OutputFile="$(OutDir)\$(ProjectName)D.dll"
+                               AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
+                               IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;msvcrt.lib;dsound.lib"
+                               GenerateDebugInformation="true"
+                               SubSystem="2"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                               UseFAT32Workaround="true"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCWebDeploymentTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                               CommandLine="copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Debug32TargetFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Debug32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).lib&quot; &quot;$(GenericWin32LibraryFolder)\$(TargetName).lib&quot;&#x0D;&#x0A;"
+                       />
+               </Configuration>
+               <Configuration
+                       Name="Release 32|Win32"
+                       OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
+                       IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
+                       ConfigurationType="2"
+                       InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
+                       CharacterSet="2"
+                       WholeProgramOptimization="1"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
+                               Optimization="2"
+                               InlineFunctionExpansion="1"
+                               AdditionalIncludeDirectories="..;..\dummy;..\..\ardour;..\..\pbd;..\..\timecode;..\..\evoral;&quot;..\..\midi++2&quot;;&quot;$(GenericIncludeFolder)\ardourext&quot;;&quot;$(GenericLibraryFolder)\glib-2.0\include&quot;;&quot;$(GenericIncludeFolder)\libsndfile&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0&quot;;&quot;$(GenericIncludeFolder)\cairo&quot;;&quot;$(GenericIncludeFolder)\freetype2&quot;;&quot;$(GenericIncludeFolder)\pango-1.0&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0\gdk&quot;;&quot;$(GenericIncludeFolder)\atk-2.0&quot;;&quot;$(GenericIncludeFolder)\lrdf&quot;;&quot;$(GenericIncludeFolder)\raptor&quot;;&quot;$(GenericIncludeFolder)\boost&quot;;..\.."
+                               PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;_SECURE_SCL=0;BUILDING_DUMMY_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_WINDOWS;NDEBUG;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME=&quot;\&quot;Mixbus\&quot;&quot;;PACKAGE=&quot;\&quot;libardour_dummy\&quot;&quot;;_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\&quot;Mod4&gt;&lt;Super\&quot;;IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\&quot;1.2\&quot;;CURRENT_SESSION_FILE_VERSION=3001"
+                               StringPooling="false"
+                               RuntimeLibrary="2"
+                               EnableEnhancedInstructionSet="1"
+                               WarningLevel="3"
+                               CompileAs="2"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalDependencies="$(DllPrefix)glibmm32-2.4-0.lib $(DllPrefix)giomm32-2.4-0.lib boost-regex32.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0.lib $(DllPrefix)gobject32-2.0-0.lib $(DllPrefix)gmodule32-2.0-0.lib $(DllPrefix)glib32-2.0-0.lib $(DllPrefix)gio32-2.0-0.lib $(DllPrefix)sigc++32-2.0.lib $(DllPrefix)timecode32.lib $(DllPrefix)ardour32.lib $(DllPrefix)pbd32.lib intl.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib"
+                               OutputFile="$(OutDir)\$(ProjectName).dll"
+                               AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
+                               IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;dsound.lib"
+                               GenerateDebugInformation="false"
+                               SubSystem="2"
+                               OptimizeReferences="1"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                               UseFAT32Workaround="true"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCWebDeploymentTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                               CommandLine="copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Release32TargetFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Release32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).lib&quot; &quot;$(GenericWin32LibraryFolder)\$(TargetName).lib&quot;&#x0D;&#x0A;"
+                       />
+               </Configuration>
+               <Configuration
+                       Name="Release 32 with Debugging Capability|Win32"
+                       OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
+                       IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
+                       ConfigurationType="2"
+                       InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
+                       CharacterSet="2"
+                       WholeProgramOptimization="0"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
+                               Optimization="0"
+                               AdditionalIncludeDirectories="..;..\dummy;..\..\ardour;..\..\pbd;..\..\timecode;..\..\evoral;&quot;..\..\midi++2&quot;;&quot;$(GenericIncludeFolder)\ardourext&quot;;&quot;$(GenericLibraryFolder)\glib-2.0\include&quot;;&quot;$(GenericIncludeFolder)\libsndfile&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0&quot;;&quot;$(GenericIncludeFolder)\cairo&quot;;&quot;$(GenericIncludeFolder)\freetype2&quot;;&quot;$(GenericIncludeFolder)\pango-1.0&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0\gdk&quot;;&quot;$(GenericIncludeFolder)\atk-2.0&quot;;&quot;$(GenericIncludeFolder)\lrdf&quot;;&quot;$(GenericIncludeFolder)\raptor&quot;;&quot;$(GenericIncludeFolder)\boost&quot;;..\.."
+                               PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;_SECURE_SCL=0;BUILDING_DUMMY_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_WINDOWS;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME=&quot;\&quot;Mixbus\&quot;&quot;;PACKAGE=&quot;\&quot;libardour_dummy\&quot;&quot;;_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\&quot;Mod4&gt;&lt;Super\&quot;;IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\&quot;1.2\&quot;;CURRENT_SESSION_FILE_VERSION=3001"
+                               StringPooling="false"
+                               RuntimeLibrary="2"
+                               EnableEnhancedInstructionSet="1"
+                               WarningLevel="3"
+                               DebugInformationFormat="3"
+                               CompileAs="2"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalDependencies="$(DllPrefix)glibmm32-2.4-0RDC.lib $(DllPrefix)giomm32-2.4-0RDC.lib boost-regex32RDC.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0RDC.lib $(DllPrefix)gobject32-2.0-0RDC.lib $(DllPrefix)gmodule32-2.0-0RDC.lib $(DllPrefix)glib32-2.0-0RDC.lib $(DllPrefix)gio32-2.0-0RDC.lib $(DllPrefix)sigc++32-2.0RDC.lib $(DllPrefix)timecode32RDC.lib $(DllPrefix)ardour32RDC.lib $(DllPrefix)pbd32RDC.lib intlRDC.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib"
+                               OutputFile="$(OutDir)\$(ProjectName)RDC.dll"
+                               AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
+                               IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;dsound.lib"
+                               GenerateDebugInformation="true"
+                               SubSystem="2"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                               UseFAT32Workaround="true"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCWebDeploymentTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                               CommandLine="copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Release32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).lib&quot; &quot;$(GenericWin32LibraryFolder)\$(TargetName).lib&quot;&#x0D;&#x0A;"
+                       />
+               </Configuration>
+       </Configurations>
+       <References>
+       </References>
+       <Files>
+               <Filter
+                       Name="Source Files"
+                       Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+                       >
+                       <File
+                               RelativePath="..\dummy\dummy_audiobackend.cc"
+                               >
+                       </File>
+               </Filter>
+               <Filter
+                       Name="Header Files"
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+                       >
+                       <File
+                               RelativePath="..\dummy\dummy_audiobackend.h"
+                               >
+                       </File>
+               </Filter>
+       </Files>
+       <Globals>
+       </Globals>
+</VisualStudioProject>
diff --git a/libs/backends/MSVCbackends/jack_audiobackend.vcproj b/libs/backends/MSVCbackends/jack_audiobackend.vcproj
new file mode 100644 (file)
index 0000000..5d4cac0
--- /dev/null
@@ -0,0 +1,316 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+       ProjectType="Visual C++"
+       Version="8.00"
+       Name="jack_audiobackend"
+       ProjectGUID="{4A58CE49-541E-43D9-92CD-7E85EA7C96AF}"
+       RootNamespace="jack_audiobackend"
+       >
+       <Platforms>
+               <Platform
+                       Name="Win32"
+               />
+       </Platforms>
+       <ToolFiles>
+       </ToolFiles>
+       <Configurations>
+               <Configuration
+                       Name="Debug 32|Win32"
+                       OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
+                       IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
+                       ConfigurationType="2"
+                       InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
+                       CharacterSet="2"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
+                               Optimization="0"
+                               AdditionalIncludeDirectories="..;..\..\ardour;..\..\pbd;..\..\fst;&quot;$(GenericIncludeFolder)\ardourext&quot;;..\..\surfaces\control_protocol;..\..\evoral;..\..\libltc;..\..\timecode;..\..\rubberband;&quot;..\..\vamp-sdk&quot;;&quot;..\..\midi++2&quot;;..\..\taglib;..\..\taglib\taglib;..\..\taglib\taglib\toolkit;..\..\audiographer;&quot;$(GenericLibraryFolder)\glib-2.0\include&quot;;&quot;$(GenericIncludeFolder)\libsndfile&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0&quot;;&quot;$(GenericIncludeFolder)\cairo&quot;;&quot;$(GenericIncludeFolder)\freetype2&quot;;&quot;$(GenericIncludeFolder)\pango-1.0&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0\gdk&quot;;&quot;$(GenericIncludeFolder)\atk-2.0&quot;;&quot;$(GenericIncludeFolder)\lrdf&quot;;&quot;$(GenericIncludeFolder)\raptor&quot;;..\..\..\..\support\MB3JackPortaudio\include"
+                               PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;BUILDING_JACK_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_DEBUG;DEBUG=&quot;Debug&quot;;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME=&quot;\&quot;Mixbus\&quot;&quot;;PACKAGE=&quot;\&quot;libardour_jack\&quot;&quot;;_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_PORTAUDIO=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\&quot;Mod4&gt;&lt;Super\&quot;;IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\&quot;1.2\&quot;;CURRENT_SESSION_FILE_VERSION=3001"
+                               MinimalRebuild="true"
+                               RuntimeLibrary="3"
+                               WarningLevel="3"
+                               DebugInformationFormat="3"
+                               CompileAs="2"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalDependencies="libjackD.lib $(DllPrefix)glibmm32-2.4-0D.lib $(DllPrefix)giomm32-2.4-0D.lib jack_portaudio_x86D.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0D.lib $(DllPrefix)gobject32-2.0-0D.lib $(DllPrefix)gmodule32-2.0-0D.lib $(DllPrefix)glib32-2.0-0D.lib $(DllPrefix)gio32-2.0-0D.lib $(DllPrefix)sigc++32-2.0D.lib $(DllPrefix)timecode32D.lib $(DllPrefix)pbd32D.lib intlD.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib setupapi.lib"
+                               OutputFile="$(OutDir)\$(ProjectName)D.dll"
+                               AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
+                               IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;msvcrt.lib;dsound.lib"
+                               GenerateDebugInformation="true"
+                               SubSystem="2"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                               UseFAT32Workaround="true"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCWebDeploymentTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                               CommandLine="copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Debug32TargetFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Debug32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).lib&quot; &quot;$(GenericWin32LibraryFolder)\$(TargetName).lib&quot;&#x0D;&#x0A;"
+                       />
+               </Configuration>
+               <Configuration
+                       Name="Release 32|Win32"
+                       OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
+                       IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
+                       ConfigurationType="2"
+                       InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
+                       CharacterSet="2"
+                       WholeProgramOptimization="1"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
+                               Optimization="2"
+                               InlineFunctionExpansion="1"
+                               AdditionalIncludeDirectories="..;..\..\ardour;..\..\pbd;..\..\fst;&quot;$(GenericIncludeFolder)\ardourext&quot;;..\..\surfaces\control_protocol;..\..\evoral;..\..\libltc;..\..\timecode;..\..\rubberband;&quot;..\..\vamp-sdk&quot;;&quot;..\..\midi++2&quot;;..\..\taglib;..\..\taglib\taglib;..\..\taglib\taglib\toolkit;..\..\audiographer;&quot;$(GenericLibraryFolder)\glib-2.0\include&quot;;&quot;$(GenericIncludeFolder)\libsndfile&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0&quot;;&quot;$(GenericIncludeFolder)\cairo&quot;;&quot;$(GenericIncludeFolder)\freetype2&quot;;&quot;$(GenericIncludeFolder)\pango-1.0&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0\gdk&quot;;&quot;$(GenericIncludeFolder)\atk-2.0&quot;;&quot;$(GenericIncludeFolder)\lrdf&quot;;&quot;$(GenericIncludeFolder)\raptor&quot;;..\..\..\..\support\MB3JackPortaudio\include"
+                               PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;_SECURE_SCL=0;BUILDING_JACK_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;NDEBUG;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME=&quot;\&quot;Mixbus\&quot;&quot;;PACKAGE=&quot;\&quot;libardour_jack\&quot;&quot;;_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_PORTAUDIO=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\&quot;Mod4&gt;&lt;Super\&quot;;IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\&quot;1.2\&quot;;CURRENT_SESSION_FILE_VERSION=3001"
+                               StringPooling="false"
+                               RuntimeLibrary="2"
+                               EnableEnhancedInstructionSet="1"
+                               WarningLevel="3"
+                               CompileAs="2"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalDependencies="libjack.lib $(DllPrefix)glibmm32-2.4-0.lib $(DllPrefix)giomm32-2.4-0.lib jack_portaudio_x86.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0.lib $(DllPrefix)gobject32-2.0-0.lib $(DllPrefix)gmodule32-2.0-0.lib $(DllPrefix)glib32-2.0-0.lib $(DllPrefix)gio32-2.0-0.lib $(DllPrefix)sigc++32-2.0.lib $(DllPrefix)timecode32.lib $(DllPrefix)pbd32.lib intl.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib setupapi.lib"
+                               OutputFile="$(OutDir)\$(ProjectName).dll"
+                               AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
+                               IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;dsound.lib"
+                               SubSystem="2"
+                               OptimizeReferences="2"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                               UseFAT32Workaround="true"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCWebDeploymentTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                               CommandLine="copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Release32TargetFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Release32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).lib&quot; &quot;$(GenericWin32LibraryFolder)\$(TargetName).lib&quot;&#x0D;&#x0A;"
+                       />
+               </Configuration>
+               <Configuration
+                       Name="Release 32 with Debugging Capability|Win32"
+                       OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
+                       IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
+                       ConfigurationType="2"
+                       InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
+                       CharacterSet="2"
+                       WholeProgramOptimization="0"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
+                               Optimization="0"
+                               AdditionalIncludeDirectories="..;..\..\ardour;..\..\pbd;..\..\fst;&quot;$(GenericIncludeFolder)\ardourext&quot;;..\..\surfaces\control_protocol;..\..\evoral;..\..\libltc;..\..\timecode;..\..\rubberband;&quot;..\..\vamp-sdk&quot;;&quot;..\..\midi++2&quot;;..\..\taglib;..\..\taglib\taglib;..\..\taglib\taglib\toolkit;..\..\audiographer;&quot;$(GenericLibraryFolder)\glib-2.0\include&quot;;&quot;$(GenericIncludeFolder)\libsndfile&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0&quot;;&quot;$(GenericIncludeFolder)\cairo&quot;;&quot;$(GenericIncludeFolder)\freetype2&quot;;&quot;$(GenericIncludeFolder)\pango-1.0&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0\gdk&quot;;&quot;$(GenericIncludeFolder)\atk-2.0&quot;;&quot;$(GenericIncludeFolder)\lrdf&quot;;&quot;$(GenericIncludeFolder)\raptor&quot;;..\..\..\..\support\MB3JackPortaudio\include"
+                               PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;_SECURE_SCL=0;BUILDING_JACK_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME=&quot;\&quot;Mixbus\&quot;&quot;;PACKAGE=&quot;\&quot;libardour_jack\&quot;&quot;;_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_PORTAUDIO=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\&quot;Mod4&gt;&lt;Super\&quot;;IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\&quot;1.2\&quot;;CURRENT_SESSION_FILE_VERSION=3001"
+                               StringPooling="false"
+                               RuntimeLibrary="2"
+                               EnableEnhancedInstructionSet="1"
+                               WarningLevel="3"
+                               DebugInformationFormat="3"
+                               CompileAs="2"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalDependencies="libjack.lib $(DllPrefix)glibmm32-2.4-0RDC.lib $(DllPrefix)giomm32-2.4-0RDC.lib jack_portaudio_x86RDC.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0RDC.lib $(DllPrefix)gobject32-2.0-0RDC.lib $(DllPrefix)gmodule32-2.0-0RDC.lib $(DllPrefix)glib32-2.0-0RDC.lib $(DllPrefix)gio32-2.0-0RDC.lib $(DllPrefix)sigc++32-2.0RDC.lib $(DllPrefix)timecode32RDC.lib $(DllPrefix)pbd32RDC.lib intlRDC.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib setupapi.lib"
+                               OutputFile="$(OutDir)\$(ProjectName)RDC.dll"
+                               AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
+                               IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;dsound.lib"
+                               GenerateDebugInformation="true"
+                               SubSystem="2"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                               UseFAT32Workaround="true"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCWebDeploymentTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                               CommandLine="copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Release32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).lib&quot; &quot;$(GenericWin32LibraryFolder)\$(TargetName).lib&quot;&#x0D;&#x0A;"
+                       />
+               </Configuration>
+       </Configurations>
+       <References>
+       </References>
+       <Files>
+               <Filter
+                       Name="Source Files"
+                       Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+                       >
+                       <File
+                               RelativePath="..\jack\jack_api.cc"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\jack\jack_audiobackend.cc"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\jack\jack_connection.cc"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\jack\jack_portengine.cc"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\jack\jack_session.cc"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\jack\jack_utils.cc"
+                               >
+                       </File>
+               </Filter>
+               <Filter
+                       Name="Header Files"
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+                       >
+                       <File
+                               RelativePath="..\jack\jack_audiobackend.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\jack\jack_connection.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\jack\jack_utils.h"
+                               >
+                       </File>
+               </Filter>
+       </Files>
+       <Globals>
+       </Globals>
+</VisualStudioProject>
diff --git a/libs/backends/MSVCbackends/jack_backend.vcproj b/libs/backends/MSVCbackends/jack_backend.vcproj
deleted file mode 100644 (file)
index ef428b0..0000000
+++ /dev/null
@@ -1,316 +0,0 @@
-<?xml version="1.0" encoding="Windows-1252"?>
-<VisualStudioProject
-       ProjectType="Visual C++"
-       Version="8.00"
-       Name="jack_backend"
-       ProjectGUID="{4A58CE49-541E-43D9-92CD-7E85EA7C96AF}"
-       RootNamespace="jack_backend"
-       >
-       <Platforms>
-               <Platform
-                       Name="Win32"
-               />
-       </Platforms>
-       <ToolFiles>
-       </ToolFiles>
-       <Configurations>
-               <Configuration
-                       Name="Debug 32|Win32"
-                       OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
-                       IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
-                       ConfigurationType="2"
-                       InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
-                       CharacterSet="2"
-                       >
-                       <Tool
-                               Name="VCPreBuildEventTool"
-                       />
-                       <Tool
-                               Name="VCCustomBuildTool"
-                       />
-                       <Tool
-                               Name="VCXMLDataGeneratorTool"
-                       />
-                       <Tool
-                               Name="VCWebServiceProxyGeneratorTool"
-                       />
-                       <Tool
-                               Name="VCMIDLTool"
-                       />
-                       <Tool
-                               Name="VCCLCompilerTool"
-                               AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
-                               Optimization="0"
-                               AdditionalIncludeDirectories="..;..\..\ardour;..\..\pbd;..\..\fst;&quot;$(GenericIncludeFolder)\ardourext&quot;;..\..\surfaces\control_protocol;..\..\evoral;..\..\libltc;..\..\timecode;..\..\rubberband;&quot;..\..\vamp-sdk&quot;;&quot;..\..\midi++2&quot;;..\..\taglib;..\..\taglib\taglib;..\..\taglib\taglib\toolkit;..\..\audiographer;&quot;$(GenericLibraryFolder)\glib-2.0\include&quot;;&quot;$(GenericIncludeFolder)\libsndfile&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0&quot;;&quot;$(GenericIncludeFolder)\cairo&quot;;&quot;$(GenericIncludeFolder)\freetype2&quot;;&quot;$(GenericIncludeFolder)\pango-1.0&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0\gdk&quot;;&quot;$(GenericIncludeFolder)\atk-2.0&quot;;&quot;$(GenericIncludeFolder)\lrdf&quot;;&quot;$(GenericIncludeFolder)\raptor&quot;"
-                               PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;BUILDING_JACK_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_DEBUG;DEBUG=&quot;Debug&quot;;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME=&quot;\&quot;Mixbus\&quot;&quot;;PACKAGE=&quot;\&quot;libardour_jack\&quot;&quot;;_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\&quot;Mod4&gt;&lt;Super\&quot;;IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\&quot;1.2\&quot;;CURRENT_SESSION_FILE_VERSION=3001"
-                               MinimalRebuild="true"
-                               RuntimeLibrary="3"
-                               WarningLevel="3"
-                               DebugInformationFormat="3"
-                               CompileAs="2"
-                       />
-                       <Tool
-                               Name="VCManagedResourceCompilerTool"
-                       />
-                       <Tool
-                               Name="VCResourceCompilerTool"
-                       />
-                       <Tool
-                               Name="VCPreLinkEventTool"
-                       />
-                       <Tool
-                               Name="VCLinkerTool"
-                               AdditionalDependencies="libjackD.lib $(DllPrefix)glibmm32-2.4-0D.lib $(DllPrefix)giomm32-2.4-0D.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0D.lib $(DllPrefix)gobject32-2.0-0D.lib $(DllPrefix)gmodule32-2.0-0D.lib $(DllPrefix)glib32-2.0-0D.lib $(DllPrefix)gio32-2.0-0D.lib $(DllPrefix)sigc++32-2.0D.lib $(DllPrefix)timecode32D.lib $(DllPrefix)pbd32D.lib intlD.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib lilv-0D.lib serd-0D.lib sord-0D.lib sratom-0D.lib suil-0D.lib suil_win_in_gtk2.lib"
-                               OutputFile="$(OutDir)\$(ProjectName)D.dll"
-                               AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
-                               IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;msvcrt.lib;dsound.lib"
-                               GenerateDebugInformation="true"
-                               SubSystem="2"
-                       />
-                       <Tool
-                               Name="VCALinkTool"
-                       />
-                       <Tool
-                               Name="VCManifestTool"
-                               UseFAT32Workaround="true"
-                       />
-                       <Tool
-                               Name="VCXDCMakeTool"
-                       />
-                       <Tool
-                               Name="VCBscMakeTool"
-                       />
-                       <Tool
-                               Name="VCFxCopTool"
-                       />
-                       <Tool
-                               Name="VCAppVerifierTool"
-                       />
-                       <Tool
-                               Name="VCWebDeploymentTool"
-                       />
-                       <Tool
-                               Name="VCPostBuildEventTool"
-                               CommandLine="copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Debug32TargetFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Debug32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).lib&quot; &quot;$(GenericWin32LibraryFolder)\$(TargetName).lib&quot;&#x0D;&#x0A;"
-                       />
-               </Configuration>
-               <Configuration
-                       Name="Release 32|Win32"
-                       OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
-                       IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
-                       ConfigurationType="2"
-                       InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
-                       CharacterSet="2"
-                       WholeProgramOptimization="1"
-                       >
-                       <Tool
-                               Name="VCPreBuildEventTool"
-                       />
-                       <Tool
-                               Name="VCCustomBuildTool"
-                       />
-                       <Tool
-                               Name="VCXMLDataGeneratorTool"
-                       />
-                       <Tool
-                               Name="VCWebServiceProxyGeneratorTool"
-                       />
-                       <Tool
-                               Name="VCMIDLTool"
-                       />
-                       <Tool
-                               Name="VCCLCompilerTool"
-                               AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
-                               Optimization="2"
-                               InlineFunctionExpansion="1"
-                               AdditionalIncludeDirectories="..;..\..\ardour;..\..\pbd;..\..\fst;&quot;$(GenericIncludeFolder)\ardourext&quot;;..\..\surfaces\control_protocol;..\..\evoral;..\..\libltc;..\..\timecode;..\..\rubberband;&quot;..\..\vamp-sdk&quot;;&quot;..\..\midi++2&quot;;..\..\taglib;..\..\taglib\taglib;..\..\taglib\taglib\toolkit;..\..\audiographer;&quot;$(GenericLibraryFolder)\glib-2.0\include&quot;;&quot;$(GenericIncludeFolder)\libsndfile&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0&quot;;&quot;$(GenericIncludeFolder)\cairo&quot;;&quot;$(GenericIncludeFolder)\freetype2&quot;;&quot;$(GenericIncludeFolder)\pango-1.0&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0\gdk&quot;;&quot;$(GenericIncludeFolder)\atk-2.0&quot;;&quot;$(GenericIncludeFolder)\lrdf&quot;;&quot;$(GenericIncludeFolder)\raptor&quot;"
-                               PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;_SECURE_SCL=0;BUILDING_JACK_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;NDEBUG;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME=&quot;\&quot;Mixbus\&quot;&quot;;PACKAGE=&quot;\&quot;libardour_jack\&quot;&quot;;_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\&quot;Mod4&gt;&lt;Super\&quot;;IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\&quot;1.2\&quot;;CURRENT_SESSION_FILE_VERSION=3001"
-                               StringPooling="false"
-                               RuntimeLibrary="2"
-                               EnableEnhancedInstructionSet="1"
-                               WarningLevel="3"
-                               CompileAs="2"
-                       />
-                       <Tool
-                               Name="VCManagedResourceCompilerTool"
-                       />
-                       <Tool
-                               Name="VCResourceCompilerTool"
-                       />
-                       <Tool
-                               Name="VCPreLinkEventTool"
-                       />
-                       <Tool
-                               Name="VCLinkerTool"
-                               AdditionalDependencies="libjack.lib $(DllPrefix)glibmm32-2.4-0.lib $(DllPrefix)giomm32-2.4-0.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0.lib $(DllPrefix)gobject32-2.0-0.lib $(DllPrefix)gmodule32-2.0-0.lib $(DllPrefix)glib32-2.0-0.lib $(DllPrefix)gio32-2.0-0.lib $(DllPrefix)sigc++32-2.0.lib $(DllPrefix)timecode32.lib $(DllPrefix)pbd32.lib intl.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib lilv-0.lib serd-0.lib sord-0.lib sratom-0.lib suil-0.lib suil_win_in_gtk2.lib"
-                               OutputFile="$(OutDir)\$(ProjectName).dll"
-                               AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
-                               IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;dsound.lib"
-                               SubSystem="2"
-                               OptimizeReferences="2"
-                       />
-                       <Tool
-                               Name="VCALinkTool"
-                       />
-                       <Tool
-                               Name="VCManifestTool"
-                               UseFAT32Workaround="true"
-                       />
-                       <Tool
-                               Name="VCXDCMakeTool"
-                       />
-                       <Tool
-                               Name="VCBscMakeTool"
-                       />
-                       <Tool
-                               Name="VCFxCopTool"
-                       />
-                       <Tool
-                               Name="VCAppVerifierTool"
-                       />
-                       <Tool
-                               Name="VCWebDeploymentTool"
-                       />
-                       <Tool
-                               Name="VCPostBuildEventTool"
-                               CommandLine="copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Release32TargetFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Release32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).lib&quot; &quot;$(GenericWin32LibraryFolder)\$(TargetName).lib&quot;&#x0D;&#x0A;"
-                       />
-               </Configuration>
-               <Configuration
-                       Name="Release 32 with Debugging Capability|Win32"
-                       OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
-                       IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
-                       ConfigurationType="2"
-                       InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
-                       CharacterSet="2"
-                       WholeProgramOptimization="0"
-                       >
-                       <Tool
-                               Name="VCPreBuildEventTool"
-                       />
-                       <Tool
-                               Name="VCCustomBuildTool"
-                       />
-                       <Tool
-                               Name="VCXMLDataGeneratorTool"
-                       />
-                       <Tool
-                               Name="VCWebServiceProxyGeneratorTool"
-                       />
-                       <Tool
-                               Name="VCMIDLTool"
-                       />
-                       <Tool
-                               Name="VCCLCompilerTool"
-                               AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
-                               Optimization="0"
-                               AdditionalIncludeDirectories="..;..\..\ardour;..\..\pbd;..\..\fst;&quot;$(GenericIncludeFolder)\ardourext&quot;;..\..\surfaces\control_protocol;..\..\evoral;..\..\libltc;..\..\timecode;..\..\rubberband;&quot;..\..\vamp-sdk&quot;;&quot;..\..\midi++2&quot;;..\..\taglib;..\..\taglib\taglib;..\..\taglib\taglib\toolkit;..\..\audiographer;&quot;$(GenericLibraryFolder)\glib-2.0\include&quot;;&quot;$(GenericIncludeFolder)\libsndfile&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0&quot;;&quot;$(GenericIncludeFolder)\cairo&quot;;&quot;$(GenericIncludeFolder)\freetype2&quot;;&quot;$(GenericIncludeFolder)\pango-1.0&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0\gdk&quot;;&quot;$(GenericIncludeFolder)\atk-2.0&quot;;&quot;$(GenericIncludeFolder)\lrdf&quot;;&quot;$(GenericIncludeFolder)\raptor&quot;"
-                               PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;_SECURE_SCL=0;BUILDING_JACK_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME=&quot;\&quot;Mixbus\&quot;&quot;;PACKAGE=&quot;\&quot;libardour_jack\&quot;&quot;;_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\&quot;Mod4&gt;&lt;Super\&quot;;IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\&quot;1.2\&quot;;CURRENT_SESSION_FILE_VERSION=3001"
-                               StringPooling="false"
-                               RuntimeLibrary="2"
-                               EnableEnhancedInstructionSet="1"
-                               WarningLevel="3"
-                               DebugInformationFormat="3"
-                               CompileAs="2"
-                       />
-                       <Tool
-                               Name="VCManagedResourceCompilerTool"
-                       />
-                       <Tool
-                               Name="VCResourceCompilerTool"
-                       />
-                       <Tool
-                               Name="VCPreLinkEventTool"
-                       />
-                       <Tool
-                               Name="VCLinkerTool"
-                               AdditionalDependencies="libjack.lib $(DllPrefix)glibmm32-2.4-0RDC.lib $(DllPrefix)giomm32-2.4-0RDC.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0RDC.lib $(DllPrefix)gobject32-2.0-0RDC.lib $(DllPrefix)gmodule32-2.0-0RDC.lib $(DllPrefix)glib32-2.0-0RDC.lib $(DllPrefix)gio32-2.0-0RDC.lib $(DllPrefix)sigc++32-2.0RDC.lib $(DllPrefix)timecode32RDC.lib $(DllPrefix)pbd32RDC.lib intlRDC.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib"
-                               OutputFile="$(OutDir)\$(ProjectName)RDC.dll"
-                               AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
-                               IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;dsound.lib"
-                               GenerateDebugInformation="true"
-                               SubSystem="2"
-                       />
-                       <Tool
-                               Name="VCALinkTool"
-                       />
-                       <Tool
-                               Name="VCManifestTool"
-                               UseFAT32Workaround="true"
-                       />
-                       <Tool
-                               Name="VCXDCMakeTool"
-                       />
-                       <Tool
-                               Name="VCBscMakeTool"
-                       />
-                       <Tool
-                               Name="VCFxCopTool"
-                       />
-                       <Tool
-                               Name="VCAppVerifierTool"
-                       />
-                       <Tool
-                               Name="VCWebDeploymentTool"
-                       />
-                       <Tool
-                               Name="VCPostBuildEventTool"
-                               CommandLine="copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Release32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).lib&quot; &quot;$(GenericWin32LibraryFolder)\$(TargetName).lib&quot;&#x0D;&#x0A;"
-                       />
-               </Configuration>
-       </Configurations>
-       <References>
-       </References>
-       <Files>
-               <Filter
-                       Name="Source Files"
-                       Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
-                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
-                       >
-                       <File
-                               RelativePath="..\jack\jack_api.cc"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\jack\jack_audiobackend.cc"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\jack\jack_connection.cc"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\jack\jack_portengine.cc"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\jack\jack_session.cc"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\jack\jack_utils.cc"
-                               >
-                       </File>
-               </Filter>
-               <Filter
-                       Name="Header Files"
-                       Filter="h;hpp;hxx;hm;inl;inc;xsd"
-                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
-                       >
-                       <File
-                               RelativePath="..\jack\jack_audiobackend.h"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\jack\jack_connection.h"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\jack\jack_utils.h"
-                               >
-                       </File>
-               </Filter>
-       </Files>
-       <Globals>
-       </Globals>
-</VisualStudioProject>
diff --git a/libs/backends/MSVCbackends/waves_audiobackend.vcproj b/libs/backends/MSVCbackends/waves_audiobackend.vcproj
new file mode 100644 (file)
index 0000000..617422a
--- /dev/null
@@ -0,0 +1,524 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+       ProjectType="Visual C++"
+       Version="8.00"
+       Name="waves_audiobackend"
+       ProjectGUID="{D7B1537C-C244-4D86-BBBF-74A1801AB984}"
+       RootNamespace="waves_audiobackend"
+       >
+       <Platforms>
+               <Platform
+                       Name="Win32"
+               />
+       </Platforms>
+       <ToolFiles>
+       </ToolFiles>
+       <Configurations>
+               <Configuration
+                       Name="Debug 32|Win32"
+                       OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
+                       IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
+                       ConfigurationType="2"
+                       InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
+                       CharacterSet="2"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
+                               Optimization="0"
+                               AdditionalIncludeDirectories="..;..\wavesaudio;..\wavesaudio\wavesapi;..\wavesaudio\wavesapi\wavespublicapi;..\wavesaudio\wavesapi\devicemanager;..\wavesaudio\wavesapi\refmanager;..\wavesaudio\wavesapi\threads;..\wavesaudio\wavesapi\miscutils;..\wavesaudio\portmidi\src\pm_common;..\wavesaudio\portmidi;..\..\ardour;..\..\pbd;..\..\timecode;..\..\evoral;&quot;..\..\midi++2&quot;;&quot;$(GenericIncludeFolder)\ardourext&quot;;&quot;$(GenericLibraryFolder)\glib-2.0\include&quot;;&quot;$(GenericIncludeFolder)\libsndfile&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0&quot;;&quot;$(GenericIncludeFolder)\cairo&quot;;&quot;$(GenericIncludeFolder)\freetype2&quot;;&quot;$(GenericIncludeFolder)\pango-1.0&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0\gdk&quot;;&quot;$(GenericIncludeFolder)\atk-2.0&quot;;&quot;$(GenericIncludeFolder)\lrdf&quot;;&quot;$(GenericIncludeFolder)\raptor&quot;;..\..\..\..\support\MB3WavesPortaudio\include;..\..\..\..\support\MB3WavesPortaudio\src\hostapi\asio\ASIOSDK\common;..\.."
+                               PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;BUILDING_WAVES_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_WINDOWS;_DEBUG;DEBUG=&quot;Debug&quot;;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME=&quot;\&quot;Mixbus\&quot;&quot;;PACKAGE=&quot;\&quot;libardour_waves\&quot;&quot;;_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\&quot;Mod4&gt;&lt;Super\&quot;;IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\&quot;1.2\&quot;;CURRENT_SESSION_FILE_VERSION=3001"
+                               MinimalRebuild="true"
+                               RuntimeLibrary="3"
+                               WarningLevel="3"
+                               DebugInformationFormat="3"
+                               CompileAs="2"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalDependencies="$(DllPrefix)glibmm32-2.4-0D.lib $(DllPrefix)giomm32-2.4-0D.lib waves_portaudio_x86D.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0D.lib $(DllPrefix)gobject32-2.0-0D.lib $(DllPrefix)gmodule32-2.0-0D.lib $(DllPrefix)glib32-2.0-0D.lib $(DllPrefix)gio32-2.0-0D.lib $(DllPrefix)sigc++32-2.0D.lib $(DllPrefix)timecode32D.lib $(DllPrefix)ardour32D.lib $(DllPrefix)pbd32D.lib intlD.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib setupapi.lib"
+                               OutputFile="$(OutDir)\$(ProjectName)D.dll"
+                               AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
+                               IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;msvcrt.lib;dsound.lib"
+                               GenerateDebugInformation="true"
+                               SubSystem="2"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                               UseFAT32Workaround="true"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCWebDeploymentTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                               CommandLine="copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Debug32TargetFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Debug32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).lib&quot; &quot;$(GenericWin32LibraryFolder)\$(TargetName).lib&quot;&#x0D;&#x0A;"
+                       />
+               </Configuration>
+               <Configuration
+                       Name="Release 32|Win32"
+                       OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
+                       IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
+                       ConfigurationType="2"
+                       InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
+                       CharacterSet="2"
+                       WholeProgramOptimization="1"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
+                               Optimization="2"
+                               InlineFunctionExpansion="1"
+                               AdditionalIncludeDirectories="..;..\wavesaudio;..\wavesaudio\wavesapi;..\wavesaudio\wavesapi\wavespublicapi;..\wavesaudio\wavesapi\devicemanager;..\wavesaudio\wavesapi\refmanager;..\wavesaudio\wavesapi\threads;..\wavesaudio\wavesapi\miscutils;..\wavesaudio\portmidi\src\pm_common;..\wavesaudio\portmidi;..\..\ardour;..\..\pbd;..\..\timecode;..\..\evoral;&quot;..\..\midi++2&quot;;&quot;$(GenericIncludeFolder)\ardourext&quot;;&quot;$(GenericLibraryFolder)\glib-2.0\include&quot;;&quot;$(GenericIncludeFolder)\libsndfile&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0&quot;;&quot;$(GenericIncludeFolder)\cairo&quot;;&quot;$(GenericIncludeFolder)\freetype2&quot;;&quot;$(GenericIncludeFolder)\pango-1.0&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0\gdk&quot;;&quot;$(GenericIncludeFolder)\atk-2.0&quot;;&quot;$(GenericIncludeFolder)\lrdf&quot;;&quot;$(GenericIncludeFolder)\raptor&quot;;..\..\..\..\support\MB3WavesPortaudio\include;..\..\..\..\support\MB3WavesPortaudio\src\hostapi\asio\ASIOSDK\common;..\.."
+                               PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;_SECURE_SCL=0;BUILDING_WAVES_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_WINDOWS;NDEBUG;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME=&quot;\&quot;Mixbus\&quot;&quot;;PACKAGE=&quot;\&quot;libardour_waves\&quot;&quot;;_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\&quot;Mod4&gt;&lt;Super\&quot;;IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\&quot;1.2\&quot;;CURRENT_SESSION_FILE_VERSION=3001"
+                               StringPooling="false"
+                               RuntimeLibrary="2"
+                               EnableEnhancedInstructionSet="1"
+                               WarningLevel="3"
+                               CompileAs="2"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalDependencies="$(DllPrefix)glibmm32-2.4-0.lib $(DllPrefix)giomm32-2.4-0.lib waves_portaudio_x86.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0.lib $(DllPrefix)gobject32-2.0-0.lib $(DllPrefix)gmodule32-2.0-0.lib $(DllPrefix)glib32-2.0-0.lib $(DllPrefix)gio32-2.0-0.lib $(DllPrefix)sigc++32-2.0.lib $(DllPrefix)timecode32.lib $(DllPrefix)ardour32.lib $(DllPrefix)pbd32.lib intl.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib setupapi.lib"
+                               OutputFile="$(OutDir)\$(ProjectName).dll"
+                               AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
+                               IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;dsound.lib"
+                               SubSystem="2"
+                               OptimizeReferences="2"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                               UseFAT32Workaround="true"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCWebDeploymentTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                               CommandLine="copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Release32TargetFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Release32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).lib&quot; &quot;$(GenericWin32LibraryFolder)\$(TargetName).lib&quot;&#x0D;&#x0A;"
+                       />
+               </Configuration>
+               <Configuration
+                       Name="Release 32 with Debugging Capability|Win32"
+                       OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
+                       IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
+                       ConfigurationType="2"
+                       InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
+                       CharacterSet="2"
+                       WholeProgramOptimization="0"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
+                               Optimization="0"
+                               AdditionalIncludeDirectories="..;..\wavesaudio;..\wavesaudio\wavesapi;..\wavesaudio\wavesapi\wavespublicapi;..\wavesaudio\wavesapi\devicemanager;..\wavesaudio\wavesapi\refmanager;..\wavesaudio\wavesapi\threads;..\wavesaudio\wavesapi\miscutils;..\wavesaudio\portmidi\src\pm_common;..\wavesaudio\portmidi;..\..\ardour;..\..\pbd;..\..\timecode;..\..\evoral;&quot;..\..\midi++2&quot;;&quot;$(GenericIncludeFolder)\ardourext&quot;;&quot;$(GenericLibraryFolder)\glib-2.0\include&quot;;&quot;$(GenericIncludeFolder)\libsndfile&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0&quot;;&quot;$(GenericIncludeFolder)\cairo&quot;;&quot;$(GenericIncludeFolder)\freetype2&quot;;&quot;$(GenericIncludeFolder)\pango-1.0&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0\gdk&quot;;&quot;$(GenericIncludeFolder)\atk-2.0&quot;;&quot;$(GenericIncludeFolder)\lrdf&quot;;&quot;$(GenericIncludeFolder)\raptor&quot;;..\..\..\..\support\MB3WavesPortaudio\include;..\..\..\..\support\MB3WavesPortaudio\src\hostapi\asio\ASIOSDK\common;..\.."
+                               PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;_SECURE_SCL=0;BUILDING_WAVES_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_WINDOWS;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME=&quot;\&quot;Mixbus\&quot;&quot;;PACKAGE=&quot;\&quot;libardour_waves\&quot;&quot;;_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\&quot;Mod4&gt;&lt;Super\&quot;;IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\&quot;1.2\&quot;;CURRENT_SESSION_FILE_VERSION=3001"
+                               StringPooling="false"
+                               RuntimeLibrary="2"
+                               EnableEnhancedInstructionSet="1"
+                               WarningLevel="3"
+                               DebugInformationFormat="3"
+                               CompileAs="2"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalDependencies="$(DllPrefix)glibmm32-2.4-0RDC.lib $(DllPrefix)giomm32-2.4-0RDC.lib waves_portaudio_x86RDC.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0RDC.lib $(DllPrefix)gobject32-2.0-0RDC.lib $(DllPrefix)gmodule32-2.0-0RDC.lib $(DllPrefix)glib32-2.0-0RDC.lib $(DllPrefix)gio32-2.0-0RDC.lib $(DllPrefix)sigc++32-2.0RDC.lib $(DllPrefix)timecode32RDC.lib $(DllPrefix)ardour32RDC.lib $(DllPrefix)pbd32RDC.lib intlRDC.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib setupapi.lib"
+                               OutputFile="$(OutDir)\$(ProjectName)RDC.dll"
+                               AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
+                               IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;dsound.lib"
+                               GenerateDebugInformation="true"
+                               SubSystem="2"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                               UseFAT32Workaround="true"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCWebDeploymentTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                               CommandLine="copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Release32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).lib&quot; &quot;$(GenericWin32LibraryFolder)\$(TargetName).lib&quot;&#x0D;&#x0A;"
+                       />
+               </Configuration>
+       </Configurations>
+       <References>
+       </References>
+       <Files>
+               <Filter
+                       Name="Source Files"
+                       Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+                       >
+                       <File
+                               RelativePath="..\wavesaudio\waves_audiobackend.cc"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\waves_audiobackend.latency.cc"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\waves_audiobackend.midi.cc"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\waves_audiobackend.port_engine.cc"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\waves_audioport.cc"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\waves_dataport.cc"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\waves_midi_buffer.cc"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\waves_midi_device.cc"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\waves_midi_device_manager.cc"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\waves_midi_event.cc"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\waves_midiport.cc"
+                               >
+                       </File>
+                       <Filter
+                               Name="wavesapi"
+                               >
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\miscutils\UMicroseconds.cpp"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRAudioDeviceManager.cpp"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRNativeAudio.cpp"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRPortAudioDeviceManager.cpp"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\refmanager\WCRefManager.cpp"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\threads\WCThreadSafe.cpp"
+                                       >
+                               </File>
+                       </Filter>
+                       <Filter
+                               Name="portmidi"
+                               >
+                               <File
+                                       RelativePath="..\wavesaudio\portmidi\src\pm_common\pmutil.c"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\portmidi\src\pm_win\pmwin.c"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\portmidi\src\pm_win\pmwinmm.c"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\portmidi\src\pm_common\portmidi.c"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\portmidi\src\porttime\ptwinmm.c"
+                                       >
+                               </File>
+                       </Filter>
+               </Filter>
+               <Filter
+                       Name="Header Files"
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+                       >
+                       <File
+                               RelativePath="..\wavesaudio\portmidi\src\pm_common\pminternal.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\portmidi\pmutil.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\portmidi\src\pm_win\pmwinmm.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\portmidi\portmidi.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\portmidi\porttime.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\waves_audiobackend.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\waves_audioport.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\waves_dataport.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\waves_midi_buffer.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\waves_midi_device.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\waves_midi_device_manager.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\waves_midi_event.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\waves_midiport.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\wavesaudio\wavesapi\wavespublicapi\wstdint.h"
+                               >
+                       </File>
+                       <Filter
+                               Name="wavesapi"
+                               >
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\akupara\threading\atomic_ops.hpp"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\akupara\threading\atomic_ops_gcc_x86.hpp"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\akupara\basics.hpp"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\akupara\compiletime_functions.hpp"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\miscutils\MinMaxUtilities.h"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\miscutils\pthread_utils.h"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\miscutils\safe_delete.h"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\miscutils\UMicroseconds.h"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\miscutils\WCFixedString.h"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\BasicTypes\WCFourCC.h"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRAudioDeviceManager.h"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRNativeAudio.h"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRPortAudioDeviceManager.h"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\refmanager\WCRefManager.h"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\threads\WCThreadSafe.h"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\BasicTypes\WTByteOrder.h"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\wavespublicapi\WTErr.h"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\BasicTypes\WUComPtr.h"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\BasicTypes\WUDefines.h"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\miscutils\WUErrors.h"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\BasicTypes\WUMathConsts.h"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\wavesaudio\wavesapi\BasicTypes\WUTypes.h"
+                                       >
+                               </File>
+                       </Filter>
+               </Filter>
+       </Files>
+       <Globals>
+       </Globals>
+</VisualStudioProject>
diff --git a/libs/backends/MSVCbackends/waves_backend.vcproj b/libs/backends/MSVCbackends/waves_backend.vcproj
deleted file mode 100644 (file)
index 7436ced..0000000
+++ /dev/null
@@ -1,524 +0,0 @@
-<?xml version="1.0" encoding="Windows-1252"?>
-<VisualStudioProject
-       ProjectType="Visual C++"
-       Version="8.00"
-       Name="waves_backend"
-       ProjectGUID="{D7B1537C-C244-4D86-BBBF-74A1801AB984}"
-       RootNamespace="waves_backend"
-       >
-       <Platforms>
-               <Platform
-                       Name="Win32"
-               />
-       </Platforms>
-       <ToolFiles>
-       </ToolFiles>
-       <Configurations>
-               <Configuration
-                       Name="Debug 32|Win32"
-                       OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
-                       IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
-                       ConfigurationType="2"
-                       InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
-                       CharacterSet="2"
-                       >
-                       <Tool
-                               Name="VCPreBuildEventTool"
-                       />
-                       <Tool
-                               Name="VCCustomBuildTool"
-                       />
-                       <Tool
-                               Name="VCXMLDataGeneratorTool"
-                       />
-                       <Tool
-                               Name="VCWebServiceProxyGeneratorTool"
-                       />
-                       <Tool
-                               Name="VCMIDLTool"
-                       />
-                       <Tool
-                               Name="VCCLCompilerTool"
-                               AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
-                               Optimization="0"
-                               AdditionalIncludeDirectories="..;..\wavesaudio;..\wavesaudio\wavesapi;..\wavesaudio\wavesapi\wavespublicapi;..\wavesaudio\wavesapi\devicemanager;..\wavesaudio\wavesapi\refmanager;..\wavesaudio\wavesapi\threads;..\wavesaudio\wavesapi\miscutils;..\wavesaudio\portmidi\src\pm_common;..\wavesaudio\portmidi;..\..\ardour;..\..\pbd;..\..\timecode;..\..\evoral;&quot;..\..\midi++2&quot;;&quot;$(GenericIncludeFolder)\ardourext&quot;;&quot;$(GenericLibraryFolder)\glib-2.0\include&quot;;&quot;$(GenericIncludeFolder)\libsndfile&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0&quot;;&quot;$(GenericIncludeFolder)\cairo&quot;;&quot;$(GenericIncludeFolder)\freetype2&quot;;&quot;$(GenericIncludeFolder)\pango-1.0&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0\gdk&quot;;&quot;$(GenericIncludeFolder)\atk-2.0&quot;;&quot;$(GenericIncludeFolder)\lrdf&quot;;&quot;$(GenericIncludeFolder)\raptor&quot;;..\..\..\..\support\PortAudio\include;..\..\..\..\support\PortAudio\src\hostapi\asio\ASIOSDK\common;..\.."
-                               PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;BUILDING_WAVES_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_WINDOWS;_DEBUG;DEBUG=&quot;Debug&quot;;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME=&quot;\&quot;Mixbus\&quot;&quot;;PACKAGE=&quot;\&quot;libardour_waves\&quot;&quot;;_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\&quot;Mod4&gt;&lt;Super\&quot;;IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\&quot;1.2\&quot;;CURRENT_SESSION_FILE_VERSION=3001"
-                               MinimalRebuild="true"
-                               RuntimeLibrary="3"
-                               WarningLevel="3"
-                               DebugInformationFormat="3"
-                               CompileAs="2"
-                       />
-                       <Tool
-                               Name="VCManagedResourceCompilerTool"
-                       />
-                       <Tool
-                               Name="VCResourceCompilerTool"
-                       />
-                       <Tool
-                               Name="VCPreLinkEventTool"
-                       />
-                       <Tool
-                               Name="VCLinkerTool"
-                               AdditionalDependencies="$(DllPrefix)glibmm32-2.4-0D.lib $(DllPrefix)giomm32-2.4-0D.lib portaudio_x86D.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0D.lib $(DllPrefix)gobject32-2.0-0D.lib $(DllPrefix)gmodule32-2.0-0D.lib $(DllPrefix)glib32-2.0-0D.lib $(DllPrefix)gio32-2.0-0D.lib $(DllPrefix)sigc++32-2.0D.lib $(DllPrefix)timecode32D.lib $(DllPrefix)ardour32D.lib $(DllPrefix)pbd32D.lib intlD.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib lilv-0D.lib serd-0D.lib sord-0D.lib sratom-0D.lib suil-0D.lib suil_win_in_gtk2.lib"
-                               OutputFile="$(OutDir)\$(ProjectName)D.dll"
-                               AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
-                               IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;msvcrt.lib;dsound.lib"
-                               GenerateDebugInformation="true"
-                               SubSystem="2"
-                       />
-                       <Tool
-                               Name="VCALinkTool"
-                       />
-                       <Tool
-                               Name="VCManifestTool"
-                               UseFAT32Workaround="true"
-                       />
-                       <Tool
-                               Name="VCXDCMakeTool"
-                       />
-                       <Tool
-                               Name="VCBscMakeTool"
-                       />
-                       <Tool
-                               Name="VCFxCopTool"
-                       />
-                       <Tool
-                               Name="VCAppVerifierTool"
-                       />
-                       <Tool
-                               Name="VCWebDeploymentTool"
-                       />
-                       <Tool
-                               Name="VCPostBuildEventTool"
-                               CommandLine="copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Debug32TargetFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Debug32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).lib&quot; &quot;$(GenericWin32LibraryFolder)\$(TargetName).lib&quot;&#x0D;&#x0A;"
-                       />
-               </Configuration>
-               <Configuration
-                       Name="Release 32|Win32"
-                       OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
-                       IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
-                       ConfigurationType="2"
-                       InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
-                       CharacterSet="2"
-                       WholeProgramOptimization="1"
-                       >
-                       <Tool
-                               Name="VCPreBuildEventTool"
-                       />
-                       <Tool
-                               Name="VCCustomBuildTool"
-                       />
-                       <Tool
-                               Name="VCXMLDataGeneratorTool"
-                       />
-                       <Tool
-                               Name="VCWebServiceProxyGeneratorTool"
-                       />
-                       <Tool
-                               Name="VCMIDLTool"
-                       />
-                       <Tool
-                               Name="VCCLCompilerTool"
-                               AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
-                               Optimization="2"
-                               InlineFunctionExpansion="1"
-                               AdditionalIncludeDirectories="..;..\wavesaudio;..\wavesaudio\wavesapi;..\wavesaudio\wavesapi\wavespublicapi;..\wavesaudio\wavesapi\devicemanager;..\wavesaudio\wavesapi\refmanager;..\wavesaudio\wavesapi\threads;..\wavesaudio\wavesapi\miscutils;..\wavesaudio\portmidi\src\pm_common;..\wavesaudio\portmidi;..\..\ardour;..\..\pbd;..\..\timecode;..\..\evoral;&quot;..\..\midi++2&quot;;&quot;$(GenericIncludeFolder)\ardourext&quot;;&quot;$(GenericLibraryFolder)\glib-2.0\include&quot;;&quot;$(GenericIncludeFolder)\libsndfile&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0&quot;;&quot;$(GenericIncludeFolder)\cairo&quot;;&quot;$(GenericIncludeFolder)\freetype2&quot;;&quot;$(GenericIncludeFolder)\pango-1.0&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0\gdk&quot;;&quot;$(GenericIncludeFolder)\atk-2.0&quot;;&quot;$(GenericIncludeFolder)\lrdf&quot;;&quot;$(GenericIncludeFolder)\raptor&quot;;..\..\..\..\support\PortAudio\include;..\..\..\..\support\PortAudio\src\hostapi\asio\ASIOSDK\common;..\.."
-                               PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;_SECURE_SCL=0;BUILDING_WAVES_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_WINDOWS;NDEBUG;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME=&quot;\&quot;Mixbus\&quot;&quot;;PACKAGE=&quot;\&quot;libardour_waves\&quot;&quot;;_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\&quot;Mod4&gt;&lt;Super\&quot;;IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\&quot;1.2\&quot;;CURRENT_SESSION_FILE_VERSION=3001"
-                               StringPooling="false"
-                               RuntimeLibrary="2"
-                               EnableEnhancedInstructionSet="1"
-                               WarningLevel="3"
-                               CompileAs="2"
-                       />
-                       <Tool
-                               Name="VCManagedResourceCompilerTool"
-                       />
-                       <Tool
-                               Name="VCResourceCompilerTool"
-                       />
-                       <Tool
-                               Name="VCPreLinkEventTool"
-                       />
-                       <Tool
-                               Name="VCLinkerTool"
-                               AdditionalDependencies="$(DllPrefix)glibmm32-2.4-0.lib $(DllPrefix)giomm32-2.4-0.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0.lib $(DllPrefix)gobject32-2.0-0.lib $(DllPrefix)gmodule32-2.0-0.lib $(DllPrefix)glib32-2.0-0.lib $(DllPrefix)gio32-2.0-0.lib $(DllPrefix)sigc++32-2.0.lib $(DllPrefix)timecode32.lib $(DllPrefix)pbd32.lib intl.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib lilv-0.lib serd-0.lib sord-0.lib sratom-0.lib suil-0.lib suil_win_in_gtk2.lib"
-                               OutputFile="$(OutDir)\$(ProjectName).dll"
-                               AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
-                               IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;dsound.lib"
-                               SubSystem="2"
-                               OptimizeReferences="2"
-                       />
-                       <Tool
-                               Name="VCALinkTool"
-                       />
-                       <Tool
-                               Name="VCManifestTool"
-                               UseFAT32Workaround="true"
-                       />
-                       <Tool
-                               Name="VCXDCMakeTool"
-                       />
-                       <Tool
-                               Name="VCBscMakeTool"
-                       />
-                       <Tool
-                               Name="VCFxCopTool"
-                       />
-                       <Tool
-                               Name="VCAppVerifierTool"
-                       />
-                       <Tool
-                               Name="VCWebDeploymentTool"
-                       />
-                       <Tool
-                               Name="VCPostBuildEventTool"
-                               CommandLine="copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Release32TargetFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Release32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).lib&quot; &quot;$(GenericWin32LibraryFolder)\$(TargetName).lib&quot;&#x0D;&#x0A;"
-                       />
-               </Configuration>
-               <Configuration
-                       Name="Release 32 with Debugging Capability|Win32"
-                       OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
-                       IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
-                       ConfigurationType="2"
-                       InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
-                       CharacterSet="2"
-                       WholeProgramOptimization="0"
-                       >
-                       <Tool
-                               Name="VCPreBuildEventTool"
-                       />
-                       <Tool
-                               Name="VCCustomBuildTool"
-                       />
-                       <Tool
-                               Name="VCXMLDataGeneratorTool"
-                       />
-                       <Tool
-                               Name="VCWebServiceProxyGeneratorTool"
-                       />
-                       <Tool
-                               Name="VCMIDLTool"
-                       />
-                       <Tool
-                               Name="VCCLCompilerTool"
-                               AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
-                               Optimization="0"
-                               AdditionalIncludeDirectories="..;..\wavesaudio;..\wavesaudio\wavesapi;..\wavesaudio\wavesapi\wavespublicapi;..\wavesaudio\wavesapi\devicemanager;..\wavesaudio\wavesapi\refmanager;..\wavesaudio\wavesapi\threads;..\wavesaudio\wavesapi\miscutils;..\wavesaudio\portmidi\src\pm_common;..\wavesaudio\portmidi;..\..\ardour;..\..\pbd;..\..\timecode;..\..\evoral;&quot;..\..\midi++2&quot;;&quot;$(GenericIncludeFolder)\ardourext&quot;;&quot;$(GenericLibraryFolder)\glib-2.0\include&quot;;&quot;$(GenericIncludeFolder)\libsndfile&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0&quot;;&quot;$(GenericIncludeFolder)\cairo&quot;;&quot;$(GenericIncludeFolder)\freetype2&quot;;&quot;$(GenericIncludeFolder)\pango-1.0&quot;;&quot;$(GenericIncludeFolder)\gtk-2.0\gdk&quot;;&quot;$(GenericIncludeFolder)\atk-2.0&quot;;&quot;$(GenericIncludeFolder)\lrdf&quot;;&quot;$(GenericIncludeFolder)\raptor&quot;;..\..\..\..\support\PortAudio\include;..\..\..\..\support\PortAudio\src\hostapi\asio\ASIOSDK\common;..\.."
-                               PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;_SECURE_SCL=0;BUILDING_WAVES_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_WINDOWS;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME=&quot;\&quot;Mixbus\&quot;&quot;;PACKAGE=&quot;\&quot;libardour_waves\&quot;&quot;;_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\&quot;Mod4&gt;&lt;Super\&quot;;IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\&quot;1.2\&quot;;CURRENT_SESSION_FILE_VERSION=3001"
-                               StringPooling="false"
-                               RuntimeLibrary="2"
-                               EnableEnhancedInstructionSet="1"
-                               WarningLevel="3"
-                               DebugInformationFormat="3"
-                               CompileAs="2"
-                       />
-                       <Tool
-                               Name="VCManagedResourceCompilerTool"
-                       />
-                       <Tool
-                               Name="VCResourceCompilerTool"
-                       />
-                       <Tool
-                               Name="VCPreLinkEventTool"
-                       />
-                       <Tool
-                               Name="VCLinkerTool"
-                               AdditionalDependencies="$(DllPrefix)glibmm32-2.4-0RDC.lib $(DllPrefix)giomm32-2.4-0RDC.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0RDC.lib $(DllPrefix)gobject32-2.0-0RDC.lib $(DllPrefix)gmodule32-2.0-0RDC.lib $(DllPrefix)glib32-2.0-0RDC.lib $(DllPrefix)gio32-2.0-0RDC.lib $(DllPrefix)sigc++32-2.0RDC.lib $(DllPrefix)timecode32RDC.lib $(DllPrefix)pbd32RDC.lib intlRDC.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib"
-                               OutputFile="$(OutDir)\$(ProjectName)RDC.dll"
-                               AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
-                               IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;dsound.lib"
-                               GenerateDebugInformation="true"
-                               SubSystem="2"
-                       />
-                       <Tool
-                               Name="VCALinkTool"
-                       />
-                       <Tool
-                               Name="VCManifestTool"
-                               UseFAT32Workaround="true"
-                       />
-                       <Tool
-                               Name="VCXDCMakeTool"
-                       />
-                       <Tool
-                               Name="VCBscMakeTool"
-                       />
-                       <Tool
-                               Name="VCFxCopTool"
-                       />
-                       <Tool
-                               Name="VCAppVerifierTool"
-                       />
-                       <Tool
-                               Name="VCWebDeploymentTool"
-                       />
-                       <Tool
-                               Name="VCPostBuildEventTool"
-                               CommandLine="copy /Y &quot;$(OutDir)\$(TargetName).dll&quot; &quot;$(Release32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll&quot;&#x0D;&#x0A;copy /Y &quot;$(OutDir)\$(TargetName).lib&quot; &quot;$(GenericWin32LibraryFolder)\$(TargetName).lib&quot;&#x0D;&#x0A;"
-                       />
-               </Configuration>
-       </Configurations>
-       <References>
-       </References>
-       <Files>
-               <Filter
-                       Name="Source Files"
-                       Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
-                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
-                       >
-                       <File
-                               RelativePath="..\wavesaudio\waves_audiobackend.cc"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\waves_audiobackend.latency.cc"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\waves_audiobackend.midi.cc"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\waves_audiobackend.port_engine.cc"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\waves_audioport.cc"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\waves_dataport.cc"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\waves_midi_buffer.cc"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\waves_midi_device.cc"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\waves_midi_device_manager.cc"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\waves_midi_event.cc"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\waves_midiport.cc"
-                               >
-                       </File>
-                       <Filter
-                               Name="wavesapi"
-                               >
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\miscutils\UMicroseconds.cpp"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRAudioDeviceManager.cpp"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRNativeAudio.cpp"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRPortAudioDeviceManager.cpp"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\refmanager\WCRefManager.cpp"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\threads\WCThreadSafe.cpp"
-                                       >
-                               </File>
-                       </Filter>
-                       <Filter
-                               Name="portmidi"
-                               >
-                               <File
-                                       RelativePath="..\wavesaudio\portmidi\src\pm_common\pmutil.c"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\portmidi\src\pm_win\pmwin.c"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\portmidi\src\pm_win\pmwinmm.c"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\portmidi\src\pm_common\portmidi.c"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\portmidi\src\porttime\ptwinmm.c"
-                                       >
-                               </File>
-                       </Filter>
-               </Filter>
-               <Filter
-                       Name="Header Files"
-                       Filter="h;hpp;hxx;hm;inl;inc;xsd"
-                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
-                       >
-                       <File
-                               RelativePath="..\wavesaudio\portmidi\src\pm_common\pminternal.h"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\portmidi\pmutil.h"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\portmidi\src\pm_win\pmwinmm.h"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\portmidi\portmidi.h"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\portmidi\porttime.h"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\waves_audiobackend.h"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\waves_audioport.h"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\waves_dataport.h"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\waves_midi_buffer.h"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\waves_midi_device.h"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\waves_midi_device_manager.h"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\waves_midi_event.h"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\waves_midiport.h"
-                               >
-                       </File>
-                       <File
-                               RelativePath="..\wavesaudio\wavesapi\wavespublicapi\wstdint.h"
-                               >
-                       </File>
-                       <Filter
-                               Name="wavesapi"
-                               >
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\akupara\threading\atomic_ops.hpp"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\akupara\threading\atomic_ops_gcc_x86.hpp"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\akupara\basics.hpp"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\akupara\compiletime_functions.hpp"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\miscutils\MinMaxUtilities.h"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\miscutils\pthread_utils.h"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\miscutils\safe_delete.h"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\miscutils\UMicroseconds.h"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\miscutils\WCFixedString.h"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\BasicTypes\WCFourCC.h"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRAudioDeviceManager.h"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRNativeAudio.h"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRPortAudioDeviceManager.h"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\refmanager\WCRefManager.h"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\threads\WCThreadSafe.h"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\BasicTypes\WTByteOrder.h"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\wavespublicapi\WTErr.h"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\BasicTypes\WUComPtr.h"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\BasicTypes\WUDefines.h"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\miscutils\WUErrors.h"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\BasicTypes\WUMathConsts.h"
-                                       >
-                               </File>
-                               <File
-                                       RelativePath="..\wavesaudio\wavesapi\BasicTypes\WUTypes.h"
-                                       >
-                               </File>
-                       </Filter>
-               </Filter>
-       </Files>
-       <Globals>
-       </Globals>
-</VisualStudioProject>
diff --git a/libs/backends/alsa/alsa_audiobackend.cc b/libs/backends/alsa/alsa_audiobackend.cc
new file mode 100644 (file)
index 0000000..5afa637
--- /dev/null
@@ -0,0 +1,1826 @@
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2013 Paul Davis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <regex.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+
+#include <glibmm.h>
+
+#include "alsa_audiobackend.h"
+#include "rt_thread.h"
+
+#include "pbd/compose.h"
+#include "pbd/error.h"
+#include "pbd/file_utils.h"
+#include "ardour/filesystem_paths.h"
+#include "ardour/port_manager.h"
+#include "ardouralsautil/devicelist.h"
+#include "i18n.h"
+
+using namespace ARDOUR;
+
+static std::string s_instance_name;
+size_t AlsaAudioBackend::_max_buffer_size = 8192;
+std::vector<std::string> AlsaAudioBackend::_midi_options;
+std::vector<AudioBackend::DeviceStatus> AlsaAudioBackend::_audio_device_status;
+std::vector<AudioBackend::DeviceStatus> AlsaAudioBackend::_midi_device_status;
+
+AlsaAudioBackend::AlsaAudioBackend (AudioEngine& e, AudioBackendInfo& info)
+       : AudioBackend (e, info)
+       , _pcmi (0)
+       , _run (false)
+       , _active (false)
+       , _freewheeling (false)
+       , _measure_latency (false)
+       , _audio_device("")
+       , _midi_driver_option(_("None"))
+       , _device_reservation(0)
+       , _samplerate (48000)
+       , _samples_per_period (1024)
+       , _periods_per_cycle (2)
+       , _n_inputs (0)
+       , _n_outputs (0)
+       , _systemic_audio_input_latency (0)
+       , _systemic_audio_output_latency (0)
+       , _dsp_load (0)
+       , _processed_samples (0)
+       , _port_change_flag (false)
+{
+       _instance_name = s_instance_name;
+       pthread_mutex_init (&_port_callback_mutex, 0);
+}
+
+AlsaAudioBackend::~AlsaAudioBackend ()
+{
+       pthread_mutex_destroy (&_port_callback_mutex);
+}
+
+/* AUDIOBACKEND API */
+
+std::string
+AlsaAudioBackend::name () const
+{
+       return X_("ALSA");
+}
+
+bool
+AlsaAudioBackend::is_realtime () const
+{
+       return true;
+}
+
+std::vector<AudioBackend::DeviceStatus>
+AlsaAudioBackend::enumerate_devices () const
+{
+       _audio_device_status.clear();
+       std::map<std::string, std::string> devices;
+       get_alsa_audio_device_names(devices);
+       for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
+               _audio_device_status.push_back (DeviceStatus (i->first, true));
+       }
+       return _audio_device_status;
+}
+
+void
+AlsaAudioBackend::reservation_stdout (std::string d, size_t /* s */)
+{
+  if (d.substr(0, 19) == "Acquired audio-card") {
+               _reservation_succeeded = true;
+       }
+}
+
+void
+AlsaAudioBackend::release_device()
+{
+       _reservation_connection.drop_connections();
+       ARDOUR::SystemExec * tmp = _device_reservation;
+       _device_reservation = 0;
+       delete tmp;
+}
+
+bool
+AlsaAudioBackend::acquire_device(const char* device_name)
+{
+       /* This is  quick hack, ideally we'll link against libdbus and implement a dbus-listener
+        * that owns the device. here we try to get away by just requesting it and then block it...
+        * (pulseaudio periodically checks anyway)
+        *
+        * dbus-send --session --print-reply --type=method_call --dest=org.freedesktop.ReserveDevice1.Audio2 /org/freedesktop/ReserveDevice1/Audio2 org.freedesktop.ReserveDevice1.RequestRelease int32:4
+        * -> should not return  'boolean false'
+        */
+       int device_number = card_to_num(device_name);
+       if (device_number < 0) return false;
+
+       assert(_device_reservation == 0);
+       _reservation_succeeded = false;
+
+       std::string request_device_exe;
+       if (!PBD::find_file (
+                               PBD::Searchpath(Glib::build_filename(ARDOUR::ardour_dll_directory(), "ardouralsautil")
+                                       + G_SEARCHPATH_SEPARATOR_S + ARDOUR::ardour_dll_directory()),
+                               "ardour-request-device", request_device_exe))
+       {
+               PBD::warning << "ardour-request-device binary was not found..'" << endmsg;
+               return false;
+       }
+       else
+       {
+               char **argp;
+               char tmp[128];
+               argp=(char**) calloc(5,sizeof(char*));
+               argp[0] = strdup(request_device_exe.c_str());
+               argp[1] = strdup("-P");
+               snprintf(tmp, sizeof(tmp), "%d", getpid());
+               argp[2] = strdup(tmp);
+               snprintf(tmp, sizeof(tmp), "Audio%d", device_number);
+               argp[3] = strdup(tmp);
+               argp[4] = 0;
+
+               _device_reservation = new ARDOUR::SystemExec(request_device_exe, argp);
+               _device_reservation->ReadStdout.connect_same_thread (_reservation_connection, boost::bind (&AlsaAudioBackend::reservation_stdout, this, _1 ,_2));
+               _device_reservation->Terminated.connect_same_thread (_reservation_connection, boost::bind (&AlsaAudioBackend::release_device, this));
+               if (_device_reservation->start(0)) {
+                       PBD::warning << _("AlsaAudioBackend: Device Request failed.") << endmsg;
+                       release_device();
+                       return false;
+               }
+       }
+       // wait to check if reservation suceeded.
+       int timeout = 500; // 5 sec
+       while (_device_reservation && !_reservation_succeeded && --timeout > 0) {
+               Glib::usleep(10000);
+       }
+       if (timeout == 0 || !_reservation_succeeded) {
+               PBD::warning << _("AlsaAudioBackend: Device Reservation failed.") << endmsg;
+               release_device();
+               return false;
+       }
+       return true;
+}
+
+std::vector<float>
+AlsaAudioBackend::available_sample_rates (const std::string&) const
+{
+       std::vector<float> sr;
+       sr.push_back (8000.0);
+       sr.push_back (22050.0);
+       sr.push_back (24000.0);
+       sr.push_back (44100.0);
+       sr.push_back (48000.0);
+       sr.push_back (88200.0);
+       sr.push_back (96000.0);
+       sr.push_back (176400.0);
+       sr.push_back (192000.0);
+       return sr;
+}
+
+std::vector<uint32_t>
+AlsaAudioBackend::available_buffer_sizes (const std::string&) const
+{
+       std::vector<uint32_t> bs;
+       bs.push_back (32);
+       bs.push_back (64);
+       bs.push_back (128);
+       bs.push_back (256);
+       bs.push_back (512);
+       bs.push_back (1024);
+       bs.push_back (2048);
+       bs.push_back (4096);
+       bs.push_back (8192);
+       return bs;
+}
+
+uint32_t
+AlsaAudioBackend::available_input_channel_count (const std::string&) const
+{
+       return 128; // TODO query current device
+}
+
+uint32_t
+AlsaAudioBackend::available_output_channel_count (const std::string&) const
+{
+       return 128; // TODO query current device
+}
+
+bool
+AlsaAudioBackend::can_change_sample_rate_when_running () const
+{
+       return false;
+}
+
+bool
+AlsaAudioBackend::can_change_buffer_size_when_running () const
+{
+       return false;
+}
+
+int
+AlsaAudioBackend::set_device_name (const std::string& d)
+{
+       _audio_device = d;
+       return 0;
+}
+
+int
+AlsaAudioBackend::set_sample_rate (float sr)
+{
+       if (sr <= 0) { return -1; }
+       _samplerate = sr;
+       engine.sample_rate_change (sr);
+       return 0;
+}
+
+int
+AlsaAudioBackend::set_buffer_size (uint32_t bs)
+{
+       if (bs <= 0 || bs >= _max_buffer_size) {
+               return -1;
+       }
+       _samples_per_period = bs;
+       engine.buffer_size_change (bs);
+       return 0;
+}
+
+int
+AlsaAudioBackend::set_interleaved (bool yn)
+{
+       if (!yn) { return 0; }
+       return -1;
+}
+
+int
+AlsaAudioBackend::set_input_channels (uint32_t cc)
+{
+       _n_inputs = cc;
+       return 0;
+}
+
+int
+AlsaAudioBackend::set_output_channels (uint32_t cc)
+{
+       _n_outputs = cc;
+       return 0;
+}
+
+int
+AlsaAudioBackend::set_systemic_input_latency (uint32_t sl)
+{
+       _systemic_audio_input_latency = sl;
+       return 0;
+}
+
+int
+AlsaAudioBackend::set_systemic_output_latency (uint32_t sl)
+{
+       _systemic_audio_output_latency = sl;
+       return 0;
+}
+
+int
+AlsaAudioBackend::set_systemic_midi_input_latency (std::string const device, uint32_t sl)
+{
+       struct AlsaMidiDeviceInfo * nfo = midi_device_info(device);
+       if (!nfo) return -1;
+       nfo->systemic_input_latency = sl;
+       return 0;
+}
+
+int
+AlsaAudioBackend::set_systemic_midi_output_latency (std::string const device, uint32_t sl)
+{
+       struct AlsaMidiDeviceInfo * nfo = midi_device_info(device);
+       if (!nfo) return -1;
+       nfo->systemic_output_latency = sl;
+       return 0;
+}
+
+/* Retrieving parameters */
+std::string
+AlsaAudioBackend::device_name () const
+{
+       return _audio_device;
+}
+
+float
+AlsaAudioBackend::sample_rate () const
+{
+       return _samplerate;
+}
+
+uint32_t
+AlsaAudioBackend::buffer_size () const
+{
+       return _samples_per_period;
+}
+
+bool
+AlsaAudioBackend::interleaved () const
+{
+       return false;
+}
+
+uint32_t
+AlsaAudioBackend::input_channels () const
+{
+       return _n_inputs;
+}
+
+uint32_t
+AlsaAudioBackend::output_channels () const
+{
+       return _n_outputs;
+}
+
+uint32_t
+AlsaAudioBackend::systemic_input_latency () const
+{
+       return _systemic_audio_input_latency;
+}
+
+uint32_t
+AlsaAudioBackend::systemic_output_latency () const
+{
+       return _systemic_audio_output_latency;
+}
+
+uint32_t
+AlsaAudioBackend::systemic_midi_input_latency (std::string const device) const
+{
+       struct AlsaMidiDeviceInfo * nfo = midi_device_info(device);
+       if (!nfo) return 0;
+       return nfo->systemic_input_latency;
+}
+
+uint32_t
+AlsaAudioBackend::systemic_midi_output_latency (std::string const device) const
+{
+       struct AlsaMidiDeviceInfo * nfo = midi_device_info(device);
+       if (!nfo) return 0;
+       return nfo->systemic_output_latency;
+}
+
+/* MIDI */
+struct AlsaAudioBackend::AlsaMidiDeviceInfo *
+AlsaAudioBackend::midi_device_info(std::string const name) const {
+       for (std::map<std::string, struct AlsaMidiDeviceInfo*>::const_iterator i = _midi_devices.begin (); i != _midi_devices.end(); ++i) {
+               if (i->first == name) {
+                       return (i->second);
+               }
+       }
+
+       assert(_midi_driver_option != _("None"));
+
+       std::map<std::string, std::string> devices;
+       if (_midi_driver_option == _("ALSA raw devices")) {
+               get_alsa_rawmidi_device_names(devices);
+       } else {
+               get_alsa_sequencer_names (devices);
+       }
+
+       for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
+               if (i->first == name) {
+                       _midi_devices[name] = new AlsaMidiDeviceInfo();
+                       return _midi_devices[name];
+               }
+       }
+       return 0;
+}
+
+std::vector<std::string>
+AlsaAudioBackend::enumerate_midi_options () const
+{
+       if (_midi_options.empty()) {
+               _midi_options.push_back (_("None"));
+               _midi_options.push_back (_("ALSA raw devices"));
+               _midi_options.push_back (_("ALSA sequencer"));
+       }
+       return _midi_options;
+}
+
+std::vector<AudioBackend::DeviceStatus>
+AlsaAudioBackend::enumerate_midi_devices () const
+{
+       _midi_device_status.clear();
+       std::map<std::string, std::string> devices;
+
+       if (_midi_driver_option == _("ALSA raw devices")) {
+               get_alsa_rawmidi_device_names (devices);
+       }
+       else if (_midi_driver_option == _("ALSA sequencer")) {
+               get_alsa_sequencer_names (devices);
+       }
+
+       for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
+               _midi_device_status.push_back (DeviceStatus (i->first, true));
+       }
+       return _midi_device_status;
+}
+
+int
+AlsaAudioBackend::set_midi_option (const std::string& opt)
+{
+       if (opt != _("None") && opt != _("ALSA raw devices") && opt != _("ALSA sequencer")) {
+               return -1;
+       }
+       _midi_driver_option = opt;
+       return 0;
+}
+
+std::string
+AlsaAudioBackend::midi_option () const
+{
+       return _midi_driver_option;
+}
+
+int
+AlsaAudioBackend::set_midi_device_enabled (std::string const device, bool enable)
+{
+       struct AlsaMidiDeviceInfo * nfo = midi_device_info(device);
+       if (!nfo) return -1;
+       nfo->enabled = enable;
+       return 0;
+}
+
+bool
+AlsaAudioBackend::midi_device_enabled (std::string const device) const
+{
+       struct AlsaMidiDeviceInfo * nfo = midi_device_info(device);
+       if (!nfo) return false;
+       return nfo->enabled;
+}
+
+/* State Control */
+
+static void * pthread_process (void *arg)
+{
+       AlsaAudioBackend *d = static_cast<AlsaAudioBackend *>(arg);
+       d->main_process_thread ();
+       pthread_exit (0);
+       return 0;
+}
+
+int
+AlsaAudioBackend::_start (bool for_latency_measurement)
+{
+       if (!_active && _run) {
+               // recover from 'halted', reap threads
+               stop();
+       }
+
+       if (_active || _run) {
+               PBD::error << _("AlsaAudioBackend: already active.") << endmsg;
+               return -1;
+       }
+
+       if (_ports.size()) {
+               PBD::warning << _("AlsaAudioBackend: recovering from unclean shutdown, port registry is not empty.") << endmsg;
+               _system_inputs.clear();
+               _system_outputs.clear();
+               _system_midi_in.clear();
+               _system_midi_out.clear();
+               _ports.clear();
+       }
+
+       release_device();
+
+       assert(_rmidi_in.size() == 0);
+       assert(_rmidi_out.size() == 0);
+       assert(_pcmi == 0);
+
+       std::string alsa_device;
+       std::map<std::string, std::string> devices;
+       get_alsa_audio_device_names(devices);
+       for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
+               if (i->first == _audio_device) {
+                       alsa_device = i->second;
+                       break;
+               }
+       }
+
+       acquire_device(alsa_device.c_str());
+       _pcmi = new Alsa_pcmi (alsa_device.c_str(), alsa_device.c_str(), 0, _samplerate, _samples_per_period, _periods_per_cycle, 0);
+       switch (_pcmi->state ()) {
+               case 0: /* OK */ break;
+               case -1: PBD::error << _("AlsaAudioBackend: failed to open device.") << endmsg; break;
+               case -2: PBD::error << _("AlsaAudioBackend: failed to allocate parameters.") << endmsg; break;
+               case -3: PBD::error << _("AlsaAudioBackend: cannot set requested sample rate.") << endmsg; break;
+               case -4: PBD::error << _("AlsaAudioBackend: cannot set requested period size.") << endmsg; break;
+               case -5: PBD::error << _("AlsaAudioBackend: cannot set requested number of periods.") << endmsg; break;
+               case -6: PBD::error << _("AlsaAudioBackend: unsupported sample format.") << endmsg; break;
+               default: PBD::error << _("AlsaAudioBackend: initialization failed.") << endmsg; break;
+       }
+       if (_pcmi->state ()) {
+               delete _pcmi; _pcmi = 0;
+               release_device();
+               return -1;
+       }
+
+#ifndef NDEBUG
+       _pcmi->printinfo ();
+#endif
+
+       if (_n_outputs != _pcmi->nplay ()) {
+               if (_n_outputs == 0) {
+                _n_outputs = _pcmi->nplay ();
+               } else {
+                _n_outputs = std::min (_n_outputs, _pcmi->nplay ());
+               }
+               PBD::warning << _("AlsaAudioBackend: adjusted output channel count to match device.") << endmsg;
+       }
+
+       if (_n_inputs != _pcmi->ncapt ()) {
+               if (_n_inputs == 0) {
+                _n_inputs = _pcmi->ncapt ();
+               } else {
+                _n_inputs = std::min (_n_inputs, _pcmi->ncapt ());
+               }
+               PBD::warning << _("AlsaAudioBackend: adjusted input channel count to match device.") << endmsg;
+       }
+
+       if (_pcmi->fsize() != _samples_per_period) {
+               _samples_per_period = _pcmi->fsize();
+               PBD::warning << _("AlsaAudioBackend: samples per period does not match.") << endmsg;
+       }
+
+       if (_pcmi->fsamp() != _samplerate) {
+               _samplerate = _pcmi->fsamp();
+               engine.sample_rate_change (_samplerate);
+               PBD::warning << _("AlsaAudioBackend: sample rate does not match.") << endmsg;
+       }
+
+       _measure_latency = for_latency_measurement;
+
+       register_system_midi_ports();
+
+       if (register_system_audio_ports()) {
+               PBD::error << _("AlsaAudioBackend: failed to register system ports.") << endmsg;
+               delete _pcmi; _pcmi = 0;
+               release_device();
+               return -1;
+       }
+
+       engine.sample_rate_change (_samplerate);
+       engine.buffer_size_change (_samples_per_period);
+
+       if (engine.reestablish_ports ()) {
+               PBD::error << _("AlsaAudioBackend: Could not re-establish ports.") << endmsg;
+               delete _pcmi; _pcmi = 0;
+               release_device();
+               return -1;
+       }
+
+       engine.reconnect_ports ();
+       _run = true;
+       _port_change_flag = false;
+
+       if (_realtime_pthread_create (SCHED_FIFO, -20, 100000,
+                               &_main_thread, pthread_process, this))
+       {
+               if (pthread_create (&_main_thread, NULL, pthread_process, this))
+               {
+                       PBD::error << _("AlsaAudioBackend: failed to create process thread.") << endmsg;
+                       delete _pcmi; _pcmi = 0;
+                       release_device();
+                       _run = false;
+                       return -1;
+               } else {
+                       PBD::warning << _("AlsaAudioBackend: cannot acquire realtime permissions.") << endmsg;
+               }
+       }
+
+       int timeout = 5000;
+       while (!_active && --timeout > 0) { Glib::usleep (1000); }
+
+       if (timeout == 0 || !_active) {
+               PBD::error << _("AlsaAudioBackend: failed to start process thread.") << endmsg;
+               delete _pcmi; _pcmi = 0;
+               release_device();
+               _run = false;
+               return -1;
+       }
+
+       return 0;
+}
+
+int
+AlsaAudioBackend::stop ()
+{
+       void *status;
+       if (!_run) {
+               return 0;
+       }
+
+       _run = false;
+       if (pthread_join (_main_thread, &status)) {
+               PBD::error << _("AlsaAudioBackend: failed to terminate.") << endmsg;
+               return -1;
+       }
+
+       while (!_rmidi_out.empty ()) {
+               AlsaMidiIO *m = _rmidi_out.back ();
+               m->stop();
+               _rmidi_out.pop_back ();
+               delete m;
+       }
+       while (!_rmidi_in.empty ()) {
+               AlsaMidiIO *m = _rmidi_in.back ();
+               m->stop();
+               _rmidi_in.pop_back ();
+               delete m;
+       }
+
+       unregister_system_ports();
+       delete _pcmi; _pcmi = 0;
+       release_device();
+
+       return (_active == false) ? 0 : -1;
+}
+
+int
+AlsaAudioBackend::freewheel (bool onoff)
+{
+       if (onoff == _freewheeling) {
+               return 0;
+       }
+       _freewheeling = onoff;
+       engine.freewheel_callback (onoff);
+       return 0;
+}
+
+float
+AlsaAudioBackend::dsp_load () const
+{
+       return 100.f * _dsp_load;
+}
+
+size_t
+AlsaAudioBackend::raw_buffer_size (DataType t)
+{
+       switch (t) {
+               case DataType::AUDIO:
+                       return _samples_per_period * sizeof(Sample);
+               case DataType::MIDI:
+                       return _max_buffer_size; // XXX not really limited
+       }
+       return 0;
+}
+
+/* Process time */
+pframes_t
+AlsaAudioBackend::sample_time ()
+{
+       return _processed_samples;
+}
+
+pframes_t
+AlsaAudioBackend::sample_time_at_cycle_start ()
+{
+       return _processed_samples;
+}
+
+pframes_t
+AlsaAudioBackend::samples_since_cycle_start ()
+{
+       return 0;
+}
+
+
+void *
+AlsaAudioBackend::alsa_process_thread (void *arg)
+{
+       ThreadData* td = reinterpret_cast<ThreadData*> (arg);
+       boost::function<void ()> f = td->f;
+       delete td;
+       f ();
+       return 0;
+}
+
+int
+AlsaAudioBackend::create_process_thread (boost::function<void()> func)
+{
+       pthread_t thread_id;
+       pthread_attr_t attr;
+       size_t stacksize = 100000;
+
+       ThreadData* td = new ThreadData (this, func, stacksize);
+
+       if (_realtime_pthread_create (SCHED_FIFO, -21, stacksize,
+                               &thread_id, alsa_process_thread, td)) {
+               pthread_attr_init (&attr);
+               pthread_attr_setstacksize (&attr, stacksize);
+               if (pthread_create (&thread_id, &attr, alsa_process_thread, td)) {
+                       PBD::error << _("AudioEngine: cannot create process thread.") << endmsg;
+                       pthread_attr_destroy (&attr);
+                       return -1;
+               }
+               pthread_attr_destroy (&attr);
+       }
+
+       _threads.push_back (thread_id);
+       return 0;
+}
+
+int
+AlsaAudioBackend::join_process_threads ()
+{
+       int rv = 0;
+
+       for (std::vector<pthread_t>::const_iterator i = _threads.begin (); i != _threads.end (); ++i)
+       {
+               void *status;
+               if (pthread_join (*i, &status)) {
+                       PBD::error << _("AudioEngine: cannot terminate process thread.") << endmsg;
+                       rv -= 1;
+               }
+       }
+       _threads.clear ();
+       return rv;
+}
+
+bool
+AlsaAudioBackend::in_process_thread ()
+{
+       for (std::vector<pthread_t>::const_iterator i = _threads.begin (); i != _threads.end (); ++i)
+       {
+               if (pthread_equal (*i, pthread_self ()) != 0) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+uint32_t
+AlsaAudioBackend::process_thread_count ()
+{
+       return _threads.size ();
+}
+
+void
+AlsaAudioBackend::update_latencies ()
+{
+       // trigger latency callback in RT thread (locked graph)
+       port_connect_add_remove_callback();
+}
+
+/* PORTENGINE API */
+
+void*
+AlsaAudioBackend::private_handle () const
+{
+       return NULL;
+}
+
+const std::string&
+AlsaAudioBackend::my_name () const
+{
+       return _instance_name;
+}
+
+bool
+AlsaAudioBackend::available () const
+{
+       return _run && _active;
+}
+
+uint32_t
+AlsaAudioBackend::port_name_size () const
+{
+       return 256;
+}
+
+int
+AlsaAudioBackend::set_port_name (PortEngine::PortHandle port, const std::string& name)
+{
+       if (!valid_port (port)) {
+               PBD::error << _("AlsaBackend::set_port_name: Invalid Port(s)") << endmsg;
+               return -1;
+       }
+       return static_cast<AlsaPort*>(port)->set_name (_instance_name + ":" + name);
+}
+
+std::string
+AlsaAudioBackend::get_port_name (PortEngine::PortHandle port) const
+{
+       if (!valid_port (port)) {
+               PBD::error << _("AlsaBackend::get_port_name: Invalid Port(s)") << endmsg;
+               return std::string ();
+       }
+       return static_cast<AlsaPort*>(port)->name ();
+}
+
+PortEngine::PortHandle
+AlsaAudioBackend::get_port_by_name (const std::string& name) const
+{
+       PortHandle port = (PortHandle) find_port (name);
+       return port;
+}
+
+int
+AlsaAudioBackend::get_ports (
+               const std::string& port_name_pattern,
+               DataType type, PortFlags flags,
+               std::vector<std::string>& port_names) const
+{
+       int rv = 0;
+       regex_t port_regex;
+       bool use_regexp = false;
+       if (port_name_pattern.size () > 0) {
+               if (!regcomp (&port_regex, port_name_pattern.c_str (), REG_EXTENDED|REG_NOSUB)) {
+                       use_regexp = true;
+               }
+       }
+       for (size_t i = 0; i < _ports.size (); ++i) {
+               AlsaPort* port = _ports[i];
+               if ((port->type () == type) && (port->flags () & flags)) {
+                       if (!use_regexp || !regexec (&port_regex, port->name ().c_str (), 0, NULL, 0)) {
+                               port_names.push_back (port->name ());
+                               ++rv;
+                       }
+               }
+       }
+       if (use_regexp) {
+               regfree (&port_regex);
+       }
+       return rv;
+}
+
+DataType
+AlsaAudioBackend::port_data_type (PortEngine::PortHandle port) const
+{
+       if (!valid_port (port)) {
+               return DataType::NIL;
+       }
+       return static_cast<AlsaPort*>(port)->type ();
+}
+
+PortEngine::PortHandle
+AlsaAudioBackend::register_port (
+               const std::string& name,
+               ARDOUR::DataType type,
+               ARDOUR::PortFlags flags)
+{
+       if (name.size () == 0) { return 0; }
+       if (flags & IsPhysical) { return 0; }
+       return add_port (_instance_name + ":" + name, type, flags);
+}
+
+PortEngine::PortHandle
+AlsaAudioBackend::add_port (
+               const std::string& name,
+               ARDOUR::DataType type,
+               ARDOUR::PortFlags flags)
+{
+       assert(name.size ());
+       if (find_port (name)) {
+               PBD::error << _("AlsaBackend::register_port: Port already exists:")
+                               << " (" << name << ")" << endmsg;
+               return 0;
+       }
+       AlsaPort* port = NULL;
+       switch (type) {
+               case DataType::AUDIO:
+                       port = new AlsaAudioPort (*this, name, flags);
+                       break;
+               case DataType::MIDI:
+                       port = new AlsaMidiPort (*this, name, flags);
+                       break;
+               default:
+                       PBD::error << _("AlsaBackend::register_port: Invalid Data Type.") << endmsg;
+                       return 0;
+       }
+
+       _ports.push_back (port);
+
+       return port;
+}
+
+void
+AlsaAudioBackend::unregister_port (PortEngine::PortHandle port_handle)
+{
+       if (!valid_port (port_handle)) {
+               PBD::error << _("AlsaBackend::unregister_port: Invalid Port.") << endmsg;
+       }
+       AlsaPort* port = static_cast<AlsaPort*>(port_handle);
+       std::vector<AlsaPort*>::iterator i = std::find (_ports.begin (), _ports.end (), static_cast<AlsaPort*>(port_handle));
+       if (i == _ports.end ()) {
+               PBD::error << _("AlsaBackend::unregister_port: Failed to find port") << endmsg;
+               return;
+       }
+       disconnect_all(port_handle);
+       _ports.erase (i);
+       delete port;
+}
+
+int
+AlsaAudioBackend::register_system_audio_ports()
+{
+       LatencyRange lr;
+
+       const int a_ins = _n_inputs > 0 ? _n_inputs : 2;
+       const int a_out = _n_outputs > 0 ? _n_outputs : 2;
+
+       /* audio ports */
+       lr.min = lr.max = _samples_per_period + (_measure_latency ? 0 : _systemic_audio_input_latency);
+       for (int i = 1; i <= a_ins; ++i) {
+               char tmp[64];
+               snprintf(tmp, sizeof(tmp), "system:capture_%d", i);
+               PortHandle p = add_port(std::string(tmp), DataType::AUDIO, static_cast<PortFlags>(IsOutput | IsPhysical | IsTerminal));
+               if (!p) return -1;
+               set_latency_range (p, false, lr);
+               _system_inputs.push_back(static_cast<AlsaPort*>(p));
+       }
+
+       lr.min = lr.max = _samples_per_period + (_measure_latency ? 0 : _systemic_audio_output_latency);
+       for (int i = 1; i <= a_out; ++i) {
+               char tmp[64];
+               snprintf(tmp, sizeof(tmp), "system:playback_%d", i);
+               PortHandle p = add_port(std::string(tmp), DataType::AUDIO, static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal));
+               if (!p) return -1;
+               set_latency_range (p, true, lr);
+               _system_outputs.push_back(static_cast<AlsaPort*>(p));
+       }
+       return 0;
+}
+
+int
+AlsaAudioBackend::register_system_midi_ports()
+{
+       std::map<std::string, std::string> devices;
+       int midi_ins = 0;
+       int midi_outs = 0;
+
+       if (_midi_driver_option == _("None")) {
+               return 0;
+       } else if (_midi_driver_option == _("ALSA raw devices")) {
+               get_alsa_rawmidi_device_names(devices);
+       } else {
+               get_alsa_sequencer_names (devices);
+       }
+
+       for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
+               struct AlsaMidiDeviceInfo * nfo = midi_device_info(i->first);
+               if (!nfo) continue;
+               if (!nfo->enabled) continue;
+
+               AlsaMidiOut *mout;
+               if (_midi_driver_option == _("ALSA raw devices")) {
+                       mout = new AlsaRawMidiOut (i->second.c_str());
+               } else {
+                       mout = new AlsaSeqMidiOut (i->second.c_str());
+               }
+
+               if (mout->state ()) {
+                       PBD::warning << string_compose (
+                                       _("AlsaMidiOut: failed to open midi device '%1'."), i->second)
+                               << endmsg;
+                       delete mout;
+               } else {
+                       mout->setup_timing(_samples_per_period, _samplerate);
+                       mout->sync_time (g_get_monotonic_time());
+                       if (mout->start ()) {
+                               PBD::warning << string_compose (
+                                               _("AlsaMidiOut: failed to start midi device '%1'."), i->second)
+                                       << endmsg;
+                               delete mout;
+                       } else {
+                               char tmp[64];
+                               snprintf(tmp, sizeof(tmp), "system:midi_playback_%d", ++midi_ins);
+                               PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal));
+                               if (!p) {
+                                       mout->stop();
+                                       delete mout;
+                               }
+                               LatencyRange lr;
+                               lr.min = lr.max = _samples_per_period + (_measure_latency ? 0 : nfo->systemic_output_latency);
+                               set_latency_range (p, false, lr);
+                               static_cast<AlsaMidiPort*>(p)->set_n_periods(2);
+                               _system_midi_out.push_back(static_cast<AlsaPort*>(p));
+                               _rmidi_out.push_back (mout);
+                       }
+               }
+
+               AlsaMidiIn *midin;
+               if (_midi_driver_option == _("ALSA raw devices")) {
+                       midin = new AlsaRawMidiIn (i->second.c_str());
+               } else {
+                       midin = new AlsaSeqMidiIn (i->second.c_str());
+               }
+
+               if (midin->state ()) {
+                       PBD::warning << string_compose (
+                                       _("AlsaMidiIn: failed to open midi device '%1'."), i->second)
+                               << endmsg;
+                       delete midin;
+               } else {
+                       midin->setup_timing(_samples_per_period, _samplerate);
+                       midin->sync_time (g_get_monotonic_time());
+                       if (midin->start ()) {
+                               PBD::warning << string_compose (
+                                               _("AlsaMidiIn: failed to start midi device '%1'."), i->second)
+                                       << endmsg;
+                               delete midin;
+                       } else {
+                               char tmp[64];
+                               snprintf(tmp, sizeof(tmp), "system:midi_capture_%d", ++midi_outs);
+                               PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast<PortFlags>(IsOutput | IsPhysical | IsTerminal));
+                               if (!p) {
+                                       midin->stop();
+                                       delete midin;
+                                       continue;
+                               }
+                               LatencyRange lr;
+                               lr.min = lr.max = _samples_per_period + (_measure_latency ? 0 : nfo->systemic_input_latency);
+                               set_latency_range (p, false, lr);
+                               _system_midi_in.push_back(static_cast<AlsaPort*>(p));
+                               _rmidi_in.push_back (midin);
+                       }
+               }
+       }
+       return 0;
+}
+
+void
+AlsaAudioBackend::unregister_system_ports()
+{
+       size_t i = 0;
+       _system_inputs.clear();
+       _system_outputs.clear();
+       _system_midi_in.clear();
+       _system_midi_out.clear();
+       while (i <  _ports.size ()) {
+               AlsaPort* port = _ports[i];
+               if (port->is_physical () && port->is_terminal ()) {
+                       port->disconnect_all ();
+                       _ports.erase (_ports.begin() + i);
+               } else {
+                       ++i;
+               }
+       }
+}
+
+int
+AlsaAudioBackend::connect (const std::string& src, const std::string& dst)
+{
+       AlsaPort* src_port = find_port (src);
+       AlsaPort* dst_port = find_port (dst);
+
+       if (!src_port) {
+               PBD::error << _("AlsaBackend::connect: Invalid Source port:")
+                               << " (" << src <<")" << endmsg;
+               return -1;
+       }
+       if (!dst_port) {
+               PBD::error << _("AlsaBackend::connect: Invalid Destination port:")
+                       << " (" << dst <<")" << endmsg;
+               return -1;
+       }
+       return src_port->connect (dst_port);
+}
+
+int
+AlsaAudioBackend::disconnect (const std::string& src, const std::string& dst)
+{
+       AlsaPort* src_port = find_port (src);
+       AlsaPort* dst_port = find_port (dst);
+
+       if (!src_port || !dst_port) {
+               PBD::error << _("AlsaBackend::disconnect: Invalid Port(s)") << endmsg;
+               return -1;
+       }
+       return src_port->disconnect (dst_port);
+}
+
+int
+AlsaAudioBackend::connect (PortEngine::PortHandle src, const std::string& dst)
+{
+       AlsaPort* dst_port = find_port (dst);
+       if (!valid_port (src)) {
+               PBD::error << _("AlsaBackend::connect: Invalid Source Port Handle") << endmsg;
+               return -1;
+       }
+       if (!dst_port) {
+               PBD::error << _("AlsaBackend::connect: Invalid Destination Port")
+                       << " (" << dst << ")" << endmsg;
+               return -1;
+       }
+       return static_cast<AlsaPort*>(src)->connect (dst_port);
+}
+
+int
+AlsaAudioBackend::disconnect (PortEngine::PortHandle src, const std::string& dst)
+{
+       AlsaPort* dst_port = find_port (dst);
+       if (!valid_port (src) || !dst_port) {
+               PBD::error << _("AlsaBackend::disconnect: Invalid Port(s)") << endmsg;
+               return -1;
+       }
+       return static_cast<AlsaPort*>(src)->disconnect (dst_port);
+}
+
+int
+AlsaAudioBackend::disconnect_all (PortEngine::PortHandle port)
+{
+       if (!valid_port (port)) {
+               PBD::error << _("AlsaBackend::disconnect_all: Invalid Port") << endmsg;
+               return -1;
+       }
+       static_cast<AlsaPort*>(port)->disconnect_all ();
+       return 0;
+}
+
+bool
+AlsaAudioBackend::connected (PortEngine::PortHandle port, bool /* process_callback_safe*/)
+{
+       if (!valid_port (port)) {
+               PBD::error << _("AlsaBackend::disconnect_all: Invalid Port") << endmsg;
+               return false;
+       }
+       return static_cast<AlsaPort*>(port)->is_connected ();
+}
+
+bool
+AlsaAudioBackend::connected_to (PortEngine::PortHandle src, const std::string& dst, bool /*process_callback_safe*/)
+{
+       AlsaPort* dst_port = find_port (dst);
+       if (!valid_port (src) || !dst_port) {
+               PBD::error << _("AlsaBackend::connected_to: Invalid Port") << endmsg;
+               return false;
+       }
+       return static_cast<AlsaPort*>(src)->is_connected (dst_port);
+}
+
+bool
+AlsaAudioBackend::physically_connected (PortEngine::PortHandle port, bool /*process_callback_safe*/)
+{
+       if (!valid_port (port)) {
+               PBD::error << _("AlsaBackend::physically_connected: Invalid Port") << endmsg;
+               return false;
+       }
+       return static_cast<AlsaPort*>(port)->is_physically_connected ();
+}
+
+int
+AlsaAudioBackend::get_connections (PortEngine::PortHandle port, std::vector<std::string>& names, bool /*process_callback_safe*/)
+{
+       if (!valid_port (port)) {
+               PBD::error << _("AlsaBackend::get_connections: Invalid Port") << endmsg;
+               return -1;
+       }
+
+       assert (0 == names.size ());
+
+       const std::vector<AlsaPort*>& connected_ports = static_cast<AlsaPort*>(port)->get_connections ();
+
+       for (std::vector<AlsaPort*>::const_iterator i = connected_ports.begin (); i != connected_ports.end (); ++i) {
+               names.push_back ((*i)->name ());
+       }
+
+       return (int)names.size ();
+}
+
+/* MIDI */
+int
+AlsaAudioBackend::midi_event_get (
+               pframes_t& timestamp,
+               size_t& size, uint8_t** buf, void* port_buffer,
+               uint32_t event_index)
+{
+       assert (buf && port_buffer);
+       AlsaMidiBuffer& source = * static_cast<AlsaMidiBuffer*>(port_buffer);
+       if (event_index >= source.size ()) {
+               return -1;
+       }
+       AlsaMidiEvent * const event = source[event_index].get ();
+
+       timestamp = event->timestamp ();
+       size = event->size ();
+       *buf = event->data ();
+       return 0;
+}
+
+int
+AlsaAudioBackend::midi_event_put (
+               void* port_buffer,
+               pframes_t timestamp,
+               const uint8_t* buffer, size_t size)
+{
+       assert (buffer && port_buffer);
+       AlsaMidiBuffer& dst = * static_cast<AlsaMidiBuffer*>(port_buffer);
+       if (dst.size () && (pframes_t)dst.back ()->timestamp () > timestamp) {
+               fprintf (stderr, "AlsaMidiBuffer: it's too late for this event. %d > %d\n",
+                               (pframes_t)dst.back ()->timestamp (), timestamp);
+               return -1;
+       }
+       dst.push_back (boost::shared_ptr<AlsaMidiEvent>(new AlsaMidiEvent (timestamp, buffer, size)));
+       return 0;
+}
+
+uint32_t
+AlsaAudioBackend::get_midi_event_count (void* port_buffer)
+{
+       assert (port_buffer);
+       return static_cast<AlsaMidiBuffer*>(port_buffer)->size ();
+}
+
+void
+AlsaAudioBackend::midi_clear (void* port_buffer)
+{
+       assert (port_buffer);
+       AlsaMidiBuffer * buf = static_cast<AlsaMidiBuffer*>(port_buffer);
+       assert (buf);
+       buf->clear ();
+}
+
+/* Monitoring */
+
+bool
+AlsaAudioBackend::can_monitor_input () const
+{
+       return false;
+}
+
+int
+AlsaAudioBackend::request_input_monitoring (PortEngine::PortHandle, bool)
+{
+       return -1;
+}
+
+int
+AlsaAudioBackend::ensure_input_monitoring (PortEngine::PortHandle, bool)
+{
+       return -1;
+}
+
+bool
+AlsaAudioBackend::monitoring_input (PortEngine::PortHandle)
+{
+       return false;
+}
+
+/* Latency management */
+
+void
+AlsaAudioBackend::set_latency_range (PortEngine::PortHandle port, bool for_playback, LatencyRange latency_range)
+{
+       if (!valid_port (port)) {
+               PBD::error << _("AlsaPort::set_latency_range (): invalid port.") << endmsg;
+       }
+       static_cast<AlsaPort*>(port)->set_latency_range (latency_range, for_playback);
+}
+
+LatencyRange
+AlsaAudioBackend::get_latency_range (PortEngine::PortHandle port, bool for_playback)
+{
+       if (!valid_port (port)) {
+               PBD::error << _("AlsaPort::get_latency_range (): invalid port.") << endmsg;
+               LatencyRange r;
+               r.min = 0;
+               r.max = 0;
+               return r;
+       }
+       return static_cast<AlsaPort*>(port)->latency_range (for_playback);
+}
+
+/* Discovering physical ports */
+
+bool
+AlsaAudioBackend::port_is_physical (PortEngine::PortHandle port) const
+{
+       if (!valid_port (port)) {
+               PBD::error << _("AlsaPort::port_is_physical (): invalid port.") << endmsg;
+               return false;
+       }
+       return static_cast<AlsaPort*>(port)->is_physical ();
+}
+
+void
+AlsaAudioBackend::get_physical_outputs (DataType type, std::vector<std::string>& port_names)
+{
+       for (size_t i = 0; i < _ports.size (); ++i) {
+               AlsaPort* port = _ports[i];
+               if ((port->type () == type) && port->is_input () && port->is_physical ()) {
+                       port_names.push_back (port->name ());
+               }
+       }
+}
+
+void
+AlsaAudioBackend::get_physical_inputs (DataType type, std::vector<std::string>& port_names)
+{
+       for (size_t i = 0; i < _ports.size (); ++i) {
+               AlsaPort* port = _ports[i];
+               if ((port->type () == type) && port->is_output () && port->is_physical ()) {
+                       port_names.push_back (port->name ());
+               }
+       }
+}
+
+ChanCount
+AlsaAudioBackend::n_physical_outputs () const
+{
+       int n_midi = 0;
+       int n_audio = 0;
+       for (size_t i = 0; i < _ports.size (); ++i) {
+               AlsaPort* port = _ports[i];
+               if (port->is_output () && port->is_physical ()) {
+                       switch (port->type ()) {
+                               case DataType::AUDIO: ++n_audio; break;
+                               case DataType::MIDI: ++n_midi; break;
+                               default: break;
+                       }
+               }
+       }
+       ChanCount cc;
+       cc.set (DataType::AUDIO, n_audio);
+       cc.set (DataType::MIDI, n_midi);
+       return cc;
+}
+
+ChanCount
+AlsaAudioBackend::n_physical_inputs () const
+{
+       int n_midi = 0;
+       int n_audio = 0;
+       for (size_t i = 0; i < _ports.size (); ++i) {
+               AlsaPort* port = _ports[i];
+               if (port->is_input () && port->is_physical ()) {
+                       switch (port->type ()) {
+                               case DataType::AUDIO: ++n_audio; break;
+                               case DataType::MIDI: ++n_midi; break;
+                               default: break;
+                       }
+               }
+       }
+       ChanCount cc;
+       cc.set (DataType::AUDIO, n_audio);
+       cc.set (DataType::MIDI, n_midi);
+       return cc;
+}
+
+/* Getting access to the data buffer for a port */
+
+void*
+AlsaAudioBackend::get_buffer (PortEngine::PortHandle port, pframes_t nframes)
+{
+       assert (port);
+       assert (valid_port (port));
+       return static_cast<AlsaPort*>(port)->get_buffer (nframes);
+}
+
+/* Engine Process */
+void *
+AlsaAudioBackend::main_process_thread ()
+{
+       AudioEngine::thread_init_callback (this);
+       _active = true;
+       _processed_samples = 0;
+
+       uint64_t clock1, clock2;
+       clock1 = g_get_monotonic_time();
+       _pcmi->pcm_start ();
+       int no_proc_errors = 0;
+       const int bailout = 2 * _samplerate / _samples_per_period;
+       const int64_t nomial_time = 1e6 * _samples_per_period / _samplerate;
+
+       manager.registration_callback();
+       manager.graph_order_callback();
+
+       while (_run) {
+               long nr;
+               bool xrun = false;
+               if (!_freewheeling) {
+                       nr = _pcmi->pcm_wait ();
+
+                       if (_pcmi->state () > 0) {
+                               ++no_proc_errors;
+                               xrun = true;
+                       }
+                       if (_pcmi->state () < 0 || no_proc_errors > bailout) {
+                               PBD::error << _("AlsaAudioBackend: I/O error. Audio Process Terminated.") << endmsg;
+                               break;
+                       }
+                       while (nr >= (long)_samples_per_period) {
+                               uint32_t i = 0;
+                               clock1 = g_get_monotonic_time();
+                               no_proc_errors = 0;
+
+                               _pcmi->capt_init (_samples_per_period);
+                               for (std::vector<AlsaPort*>::const_iterator it = _system_inputs.begin (); it != _system_inputs.end (); ++it, ++i) {
+                                       _pcmi->capt_chan (i, (float*)((*it)->get_buffer(_samples_per_period)), _samples_per_period);
+                               }
+                               _pcmi->capt_done (_samples_per_period);
+
+                               /* de-queue midi*/
+                               i = 0;
+                               for (std::vector<AlsaPort*>::const_iterator it = _system_midi_in.begin (); it != _system_midi_in.end (); ++it, ++i) {
+                                       assert (_rmidi_in.size() > i);
+                                       AlsaMidiIn *rm = _rmidi_in.at(i);
+                                       void *bptr = (*it)->get_buffer(0);
+                                       pframes_t time;
+                                       uint8_t data[64]; // match MaxAlsaEventSize in alsa_rawmidi.cc
+                                       size_t size = sizeof(data);
+                                       midi_clear(bptr);
+                                       while (rm->recv_event (time, data, size)) {
+                                               midi_event_put(bptr, time, data, size);
+                                               size = sizeof(data);
+                                       }
+                                       rm->sync_time (clock1);
+                               }
+
+                               for (std::vector<AlsaPort*>::const_iterator it = _system_outputs.begin (); it != _system_outputs.end (); ++it) {
+                                       memset ((*it)->get_buffer (_samples_per_period), 0, _samples_per_period * sizeof (Sample));
+                               }
+
+                               if (engine.process_callback (_samples_per_period)) {
+                                       _pcmi->pcm_stop ();
+                                       _active = false;
+                                       return 0;
+                               }
+
+                               i = 0;
+                               for (std::vector<AlsaPort*>::iterator it = _system_midi_out.begin (); it != _system_midi_out.end (); ++it, ++i) {
+                                       static_cast<AlsaMidiPort*>(*it)->next_period();
+                               }
+
+                               /* queue midi */
+                               i = 0;
+                               for (std::vector<AlsaPort*>::const_iterator it = _system_midi_out.begin (); it != _system_midi_out.end (); ++it, ++i) {
+                                       assert (_rmidi_out.size() > i);
+                                       const AlsaMidiBuffer src = static_cast<const AlsaMidiPort*>(*it)->const_buffer();
+                                       AlsaMidiOut *rm = _rmidi_out.at(i);
+                                       rm->sync_time (clock1);
+                                       for (AlsaMidiBuffer::const_iterator mit = src.begin (); mit != src.end (); ++mit) {
+                                               rm->send_event ((*mit)->timestamp(), (*mit)->data(), (*mit)->size());
+                                       }
+                               }
+
+                               /* write back audio */
+                               i = 0;
+                               _pcmi->play_init (_samples_per_period);
+                               for (std::vector<AlsaPort*>::const_iterator it = _system_outputs.begin (); it != _system_outputs.end (); ++it, ++i) {
+                                       _pcmi->play_chan (i, (const float*)(*it)->get_buffer (_samples_per_period), _samples_per_period);
+                               }
+                               for (; i < _pcmi->nplay (); ++i) {
+                                       _pcmi->clear_chan (i, _samples_per_period);
+                               }
+                               _pcmi->play_done (_samples_per_period);
+                               nr -= _samples_per_period;
+                               _processed_samples += _samples_per_period;
+
+                               /* calculate DSP load */
+                               clock2 = g_get_monotonic_time();
+                               const int64_t elapsed_time = clock2 - clock1;
+                               _dsp_load = elapsed_time / (float) nomial_time;
+                       }
+
+                       if (xrun && (_pcmi->capt_xrun() > 0 || _pcmi->play_xrun() > 0)) {
+                               engine.Xrun ();
+#if 0
+                               fprintf(stderr, "ALSA x-run read: %.1f ms, write: %.1f ms\n",
+                                               _pcmi->capt_xrun() * 1000.0, _pcmi->play_xrun() * 1000.0);
+#endif
+                       }
+               } else {
+                       // Freewheelin'
+                       for (std::vector<AlsaPort*>::const_iterator it = _system_inputs.begin (); it != _system_inputs.end (); ++it) {
+                               memset ((*it)->get_buffer (_samples_per_period), 0, _samples_per_period * sizeof (Sample));
+                       }
+                       for (std::vector<AlsaPort*>::const_iterator it = _system_midi_in.begin (); it != _system_midi_in.end (); ++it) {
+                               static_cast<AlsaMidiBuffer*>((*it)->get_buffer(0))->clear ();
+                       }
+
+                       if (engine.process_callback (_samples_per_period)) {
+                               _pcmi->pcm_stop ();
+                               return 0;
+                       }
+                       _dsp_load = 1.0;
+                       Glib::usleep (100); // don't hog cpu
+               }
+
+               bool connections_changed = false;
+               bool ports_changed = false;
+               if (!pthread_mutex_trylock (&_port_callback_mutex)) {
+                       if (_port_change_flag) {
+                               ports_changed = true;
+                               _port_change_flag = false;
+                       }
+                       if (!_port_connection_queue.empty ()) {
+                               connections_changed = true;
+                       }
+                       while (!_port_connection_queue.empty ()) {
+                               PortConnectData *c = _port_connection_queue.back ();
+                               manager.connect_callback (c->a, c->b, c->c);
+                               _port_connection_queue.pop_back ();
+                               delete c;
+                       }
+                       pthread_mutex_unlock (&_port_callback_mutex);
+               }
+               if (ports_changed) {
+                       manager.registration_callback();
+               }
+               if (connections_changed) {
+                       manager.graph_order_callback();
+               }
+               if (connections_changed || ports_changed) {
+                       engine.latency_callback(false);
+                       engine.latency_callback(true);
+               }
+
+       }
+       _pcmi->pcm_stop ();
+       _active = false;
+       if (_run) {
+               engine.halted_callback("ALSA I/O error.");
+       }
+       return 0;
+}
+
+
+/******************************************************************************/
+
+static boost::shared_ptr<AlsaAudioBackend> _instance;
+
+static boost::shared_ptr<AudioBackend> backend_factory (AudioEngine& e);
+static int instantiate (const std::string& arg1, const std::string& /* arg2 */);
+static int deinstantiate ();
+static bool already_configured ();
+
+static ARDOUR::AudioBackendInfo _descriptor = {
+       "ALSA",
+       instantiate,
+       deinstantiate,
+       backend_factory,
+       already_configured,
+};
+
+static boost::shared_ptr<AudioBackend>
+backend_factory (AudioEngine& e)
+{
+       if (!_instance) {
+               _instance.reset (new AlsaAudioBackend (e, _descriptor));
+       }
+       return _instance;
+}
+
+static int
+instantiate (const std::string& arg1, const std::string& /* arg2 */)
+{
+       s_instance_name = arg1;
+       return 0;
+}
+
+static int
+deinstantiate ()
+{
+       _instance.reset ();
+       return 0;
+}
+
+static bool
+already_configured ()
+{
+       return false;
+}
+
+extern "C" ARDOURBACKEND_API ARDOUR::AudioBackendInfo* descriptor ()
+{
+       return &_descriptor;
+}
+
+
+/******************************************************************************/
+AlsaPort::AlsaPort (AlsaAudioBackend &b, const std::string& name, PortFlags flags)
+       : _alsa_backend (b)
+       , _name  (name)
+       , _flags (flags)
+{
+       _capture_latency_range.min = 0;
+       _capture_latency_range.max = 0;
+       _playback_latency_range.min = 0;
+       _playback_latency_range.max = 0;
+}
+
+AlsaPort::~AlsaPort () {
+       disconnect_all ();
+}
+
+
+int AlsaPort::connect (AlsaPort *port)
+{
+       if (!port) {
+               PBD::error << _("AlsaPort::connect (): invalid (null) port") << endmsg;
+               return -1;
+       }
+
+       if (type () != port->type ()) {
+               PBD::error << _("AlsaPort::connect (): wrong port-type") << endmsg;
+               return -1;
+       }
+
+       if (is_output () && port->is_output ()) {
+               PBD::error << _("AlsaPort::connect (): cannot inter-connect output ports.") << endmsg;
+               return -1;
+       }
+
+       if (is_input () && port->is_input ()) {
+               PBD::error << _("AlsaPort::connect (): cannot inter-connect input ports.") << endmsg;
+               return -1;
+       }
+
+       if (this == port) {
+               PBD::error << _("AlsaPort::connect (): cannot self-connect ports.") << endmsg;
+               return -1;
+       }
+
+       if (is_connected (port)) {
+#if 0 // don't bother to warn about this for now. just ignore it
+               PBD::error << _("AlsaPort::connect (): ports are already connected:")
+                       << " (" << name () << ") -> (" << port->name () << ")"
+                       << endmsg;
+#endif
+               return -1;
+       }
+
+       _connect (port, true);
+       return 0;
+}
+
+
+void AlsaPort::_connect (AlsaPort *port, bool callback)
+{
+       _connections.push_back (port);
+       if (callback) {
+               port->_connect (this, false);
+               _alsa_backend.port_connect_callback (name(),  port->name(), true);
+       }
+}
+
+int AlsaPort::disconnect (AlsaPort *port)
+{
+       if (!port) {
+               PBD::error << _("AlsaPort::disconnect (): invalid (null) port") << endmsg;
+               return -1;
+       }
+
+       if (!is_connected (port)) {
+               PBD::error << _("AlsaPort::disconnect (): ports are not connected:")
+                       << " (" << name () << ") -> (" << port->name () << ")"
+                       << endmsg;
+               return -1;
+       }
+       _disconnect (port, true);
+       return 0;
+}
+
+void AlsaPort::_disconnect (AlsaPort *port, bool callback)
+{
+       std::vector<AlsaPort*>::iterator it = std::find (_connections.begin (), _connections.end (), port);
+
+       assert (it != _connections.end ());
+
+       _connections.erase (it);
+
+       if (callback) {
+               port->_disconnect (this, false);
+               _alsa_backend.port_connect_callback (name(),  port->name(), false);
+       }
+}
+
+
+void AlsaPort::disconnect_all ()
+{
+       while (!_connections.empty ()) {
+               _connections.back ()->_disconnect (this, false);
+               _alsa_backend.port_connect_callback (name(),  _connections.back ()->name(), false);
+               _connections.pop_back ();
+       }
+}
+
+bool
+AlsaPort::is_connected (const AlsaPort *port) const
+{
+       return std::find (_connections.begin (), _connections.end (), port) != _connections.end ();
+}
+
+bool AlsaPort::is_physically_connected () const
+{
+       for (std::vector<AlsaPort*>::const_iterator it = _connections.begin (); it != _connections.end (); ++it) {
+               if ((*it)->is_physical ()) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+/******************************************************************************/
+
+AlsaAudioPort::AlsaAudioPort (AlsaAudioBackend &b, const std::string& name, PortFlags flags)
+       : AlsaPort (b, name, flags)
+{
+       memset (_buffer, 0, sizeof (_buffer));
+       mlock(_buffer, sizeof (_buffer));
+}
+
+AlsaAudioPort::~AlsaAudioPort () { }
+
+void* AlsaAudioPort::get_buffer (pframes_t n_samples)
+{
+       if (is_input ()) {
+               std::vector<AlsaPort*>::const_iterator it = get_connections ().begin ();
+               if (it == get_connections ().end ()) {
+                       memset (_buffer, 0, n_samples * sizeof (Sample));
+               } else {
+                       AlsaAudioPort const * source = static_cast<const AlsaAudioPort*>(*it);
+                       assert (source && source->is_output ());
+                       memcpy (_buffer, source->const_buffer (), n_samples * sizeof (Sample));
+                       while (++it != get_connections ().end ()) {
+                               source = static_cast<const AlsaAudioPort*>(*it);
+                               assert (source && source->is_output ());
+                               Sample* dst = buffer ();
+                               const Sample* src = source->const_buffer ();
+                               for (uint32_t s = 0; s < n_samples; ++s, ++dst, ++src) {
+                                       *dst += *src;
+                               }
+                       }
+               }
+       }
+       return _buffer;
+}
+
+
+AlsaMidiPort::AlsaMidiPort (AlsaAudioBackend &b, const std::string& name, PortFlags flags)
+       : AlsaPort (b, name, flags)
+       , _n_periods (1)
+       , _bufperiod (0)
+{
+       _buffer[0].clear ();
+       _buffer[1].clear ();
+}
+
+AlsaMidiPort::~AlsaMidiPort () { }
+
+struct MidiEventSorter {
+       bool operator() (const boost::shared_ptr<AlsaMidiEvent>& a, const boost::shared_ptr<AlsaMidiEvent>& b) {
+               return *a < *b;
+       }
+};
+
+void* AlsaMidiPort::get_buffer (pframes_t /* nframes */)
+{
+       if (is_input ()) {
+               (_buffer[_bufperiod]).clear ();
+               for (std::vector<AlsaPort*>::const_iterator i = get_connections ().begin ();
+                               i != get_connections ().end ();
+                               ++i) {
+                       const AlsaMidiBuffer src = static_cast<const AlsaMidiPort*>(*i)->const_buffer ();
+                       for (AlsaMidiBuffer::const_iterator it = src.begin (); it != src.end (); ++it) {
+                               (_buffer[_bufperiod]).push_back (boost::shared_ptr<AlsaMidiEvent>(new AlsaMidiEvent (**it)));
+                       }
+               }
+               std::sort ((_buffer[_bufperiod]).begin (), (_buffer[_bufperiod]).end (), MidiEventSorter());
+       }
+       return &(_buffer[_bufperiod]);
+}
+
+AlsaMidiEvent::AlsaMidiEvent (const pframes_t timestamp, const uint8_t* data, size_t size)
+       : _size (size)
+       , _timestamp (timestamp)
+       , _data (0)
+{
+       if (size > 0) {
+               _data = (uint8_t*) malloc (size);
+               memcpy (_data, data, size);
+       }
+}
+
+AlsaMidiEvent::AlsaMidiEvent (const AlsaMidiEvent& other)
+       : _size (other.size ())
+       , _timestamp (other.timestamp ())
+       , _data (0)
+{
+       if (other.size () && other.const_data ()) {
+               _data = (uint8_t*) malloc (other.size ());
+               memcpy (_data, other.const_data (), other.size ());
+       }
+};
+
+AlsaMidiEvent::~AlsaMidiEvent () {
+       free (_data);
+};
diff --git a/libs/backends/alsa/alsa_audiobackend.h b/libs/backends/alsa/alsa_audiobackend.h
new file mode 100644 (file)
index 0000000..6f54ba2
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2013 Paul Davis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __libbackend_alsa_audiobackend_h__
+#define __libbackend_alsa_audiobackend_h__
+
+#include <string>
+#include <vector>
+#include <map>
+#include <set>
+
+#include <stdint.h>
+#include <pthread.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include "ardour/audio_backend.h"
+#include "ardour/system_exec.h"
+#include "ardour/types.h"
+
+#include "zita-alsa-pcmi.h"
+#include "alsa_rawmidi.h"
+#include "alsa_sequencer.h"
+
+namespace ARDOUR {
+
+class AlsaAudioBackend;
+
+class AlsaMidiEvent {
+       public:
+               AlsaMidiEvent (const pframes_t timestamp, const uint8_t* data, size_t size);
+               AlsaMidiEvent (const AlsaMidiEvent& other);
+               ~AlsaMidiEvent ();
+               size_t size () const { return _size; };
+               pframes_t timestamp () const { return _timestamp; };
+               const unsigned char* const_data () const { return _data; };
+               unsigned char* data () { return _data; };
+               bool operator< (const AlsaMidiEvent &other) const { return timestamp () < other.timestamp (); };
+       private:
+               size_t _size;
+               pframes_t _timestamp;
+               uint8_t *_data;
+};
+
+typedef std::vector<boost::shared_ptr<AlsaMidiEvent> > AlsaMidiBuffer;
+
+class AlsaPort {
+       protected:
+               AlsaPort (AlsaAudioBackend &b, const std::string&, PortFlags);
+       public:
+               virtual ~AlsaPort ();
+
+               const std::string& name () const { return _name; }
+               PortFlags flags () const { return _flags; }
+
+               int set_name (const std::string &name) { _name = name; return 0; }
+
+               virtual DataType type () const = 0;
+
+               bool is_input ()     const { return flags () & IsInput; }
+               bool is_output ()    const { return flags () & IsOutput; }
+               bool is_physical ()  const { return flags () & IsPhysical; }
+               bool is_terminal ()  const { return flags () & IsTerminal; }
+               bool is_connected () const { return _connections.size () != 0; }
+               bool is_connected (const AlsaPort *port) const;
+               bool is_physically_connected () const;
+
+               const std::vector<AlsaPort *>& get_connections () const { return _connections; }
+
+               int connect (AlsaPort *port);
+               int disconnect (AlsaPort *port);
+               void disconnect_all ();
+
+               virtual void* get_buffer (pframes_t nframes) = 0;
+
+               const LatencyRange& latency_range (bool for_playback) const
+               {
+                       return for_playback ? _playback_latency_range : _capture_latency_range;
+               }
+
+               void set_latency_range (const LatencyRange &latency_range, bool for_playback)
+               {
+                       if (for_playback)
+                       {
+                               _playback_latency_range = latency_range;
+                       }
+                       else
+                       {
+                               _capture_latency_range = latency_range;
+                       }
+               }
+
+       private:
+               AlsaAudioBackend &_alsa_backend;
+               std::string _name;
+               const PortFlags _flags;
+               LatencyRange _capture_latency_range;
+               LatencyRange _playback_latency_range;
+               std::vector<AlsaPort*> _connections;
+
+               void _connect (AlsaPort* , bool);
+               void _disconnect (AlsaPort* , bool);
+
+}; // class AlsaPort
+
+class AlsaAudioPort : public AlsaPort {
+       public:
+               AlsaAudioPort (AlsaAudioBackend &b, const std::string&, PortFlags);
+               ~AlsaAudioPort ();
+
+               DataType type () const { return DataType::AUDIO; };
+
+               Sample* buffer () { return _buffer; }
+               const Sample* const_buffer () const { return _buffer; }
+               void* get_buffer (pframes_t nframes);
+
+       private:
+               Sample _buffer[8192];
+}; // class AlsaAudioPort
+
+class AlsaMidiPort : public AlsaPort {
+       public:
+               AlsaMidiPort (AlsaAudioBackend &b, const std::string&, PortFlags);
+               ~AlsaMidiPort ();
+
+               DataType type () const { return DataType::MIDI; };
+
+               void* get_buffer (pframes_t nframes);
+               const AlsaMidiBuffer const_buffer () const { return _buffer[_bufperiod]; }
+
+               void next_period() { if (_n_periods > 1) { get_buffer(0); _bufperiod = (_bufperiod + 1) % _n_periods; } }
+               void set_n_periods(int n) { if (n > 0 && n < 3) { _n_periods = n; } }
+
+       private:
+               AlsaMidiBuffer _buffer[2];
+               int _n_periods;
+               int _bufperiod;
+}; // class AlsaMidiPort
+
+class AlsaAudioBackend : public AudioBackend {
+       friend class AlsaPort;
+       public:
+               AlsaAudioBackend (AudioEngine& e, AudioBackendInfo& info);
+               ~AlsaAudioBackend ();
+
+               /* AUDIOBACKEND API */
+
+               std::string name () const;
+               bool is_realtime () const;
+
+               std::vector<DeviceStatus> enumerate_devices () const;
+               std::vector<float> available_sample_rates (const std::string& device) const;
+               std::vector<uint32_t> available_buffer_sizes (const std::string& device) const;
+               uint32_t available_input_channel_count (const std::string& device) const;
+               uint32_t available_output_channel_count (const std::string& device) const;
+
+               bool can_change_sample_rate_when_running () const;
+               bool can_change_buffer_size_when_running () const;
+
+               int set_device_name (const std::string&);
+               int set_sample_rate (float);
+               int set_buffer_size (uint32_t);
+               int set_interleaved (bool yn);
+               int set_input_channels (uint32_t);
+               int set_output_channels (uint32_t);
+               int set_systemic_input_latency (uint32_t);
+               int set_systemic_output_latency (uint32_t);
+               int set_systemic_midi_input_latency (std::string const, uint32_t);
+               int set_systemic_midi_output_latency (std::string const, uint32_t);
+
+               /* Retrieving parameters */
+               std::string  device_name () const;
+               float        sample_rate () const;
+               uint32_t     buffer_size () const;
+               bool         interleaved () const;
+               uint32_t     input_channels () const;
+               uint32_t     output_channels () const;
+               uint32_t     systemic_input_latency () const;
+               uint32_t     systemic_output_latency () const;
+               uint32_t     systemic_midi_input_latency (std::string const) const;
+               uint32_t     systemic_midi_output_latency (std::string const) const;
+
+               bool can_set_systemic_midi_latencies () const { return true; }
+
+               /* External control app */
+               std::string control_app_name () const { return std::string (); }
+               void launch_control_app () {}
+
+               /* MIDI */
+               std::vector<std::string> enumerate_midi_options () const;
+               int set_midi_option (const std::string&);
+               std::string midi_option () const;
+
+               std::vector<DeviceStatus> enumerate_midi_devices () const;
+               int set_midi_device_enabled (std::string const, bool);
+               bool midi_device_enabled (std::string const) const;
+
+               /* State Control */
+       protected:
+               int _start (bool for_latency_measurement);
+       public:
+               int stop ();
+               int freewheel (bool);
+               float dsp_load () const;
+               size_t raw_buffer_size (DataType t);
+
+               /* Process time */
+               pframes_t sample_time ();
+               pframes_t sample_time_at_cycle_start ();
+               pframes_t samples_since_cycle_start ();
+
+               int create_process_thread (boost::function<void()> func);
+               int join_process_threads ();
+               bool in_process_thread ();
+               uint32_t process_thread_count ();
+
+               void update_latencies ();
+
+               /* PORTENGINE API */
+
+               void* private_handle () const;
+               const std::string& my_name () const;
+               bool available () const;
+               uint32_t port_name_size () const;
+
+               int         set_port_name (PortHandle, const std::string&);
+               std::string get_port_name (PortHandle) const;
+               PortHandle  get_port_by_name (const std::string&) const;
+
+               int get_ports (const std::string& port_name_pattern, DataType type, PortFlags flags, std::vector<std::string>&) const;
+
+               DataType port_data_type (PortHandle) const;
+
+               PortHandle register_port (const std::string& shortname, ARDOUR::DataType, ARDOUR::PortFlags);
+               void unregister_port (PortHandle);
+
+               int  connect (const std::string& src, const std::string& dst);
+               int  disconnect (const std::string& src, const std::string& dst);
+               int  connect (PortHandle, const std::string&);
+               int  disconnect (PortHandle, const std::string&);
+               int  disconnect_all (PortHandle);
+
+               bool connected (PortHandle, bool process_callback_safe);
+               bool connected_to (PortHandle, const std::string&, bool process_callback_safe);
+               bool physically_connected (PortHandle, bool process_callback_safe);
+               int  get_connections (PortHandle, std::vector<std::string>&, bool process_callback_safe);
+
+               /* MIDI */
+               int midi_event_get (pframes_t& timestamp, size_t& size, uint8_t** buf, void* port_buffer, uint32_t event_index);
+               int midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size);
+               uint32_t get_midi_event_count (void* port_buffer);
+               void     midi_clear (void* port_buffer);
+
+               /* Monitoring */
+
+               bool can_monitor_input () const;
+               int  request_input_monitoring (PortHandle, bool);
+               int  ensure_input_monitoring (PortHandle, bool);
+               bool monitoring_input (PortHandle);
+
+               /* Latency management */
+
+               void         set_latency_range (PortHandle, bool for_playback, LatencyRange);
+               LatencyRange get_latency_range (PortHandle, bool for_playback);
+
+               /* Discovering physical ports */
+
+               bool      port_is_physical (PortHandle) const;
+               void      get_physical_outputs (DataType type, std::vector<std::string>&);
+               void      get_physical_inputs (DataType type, std::vector<std::string>&);
+               ChanCount n_physical_outputs () const;
+               ChanCount n_physical_inputs () const;
+
+               /* Getting access to the data buffer for a port */
+
+               void* get_buffer (PortHandle, pframes_t);
+
+               void* main_process_thread ();
+
+       private:
+               std::string _instance_name;
+               Alsa_pcmi *_pcmi;
+
+               bool  _run; /* keep going or stop, ardour thread */
+               bool  _active; /* is running, process thread */
+               bool  _freewheeling;
+               bool  _measure_latency;
+
+               static std::vector<std::string> _midi_options;
+               static std::vector<AudioBackend::DeviceStatus> _audio_device_status;
+               static std::vector<AudioBackend::DeviceStatus> _midi_device_status;
+
+               std::string _audio_device;
+               std::string _midi_driver_option;
+
+               /* audio device reservation */
+               ARDOUR::SystemExec *_device_reservation;
+               PBD::ScopedConnectionList _reservation_connection;
+               void reservation_stdout (std::string, size_t);
+               bool acquire_device(const char* device_name);
+               void release_device();
+               bool _reservation_succeeded;
+
+               /* audio settings */
+               float  _samplerate;
+               size_t _samples_per_period;
+               size_t _periods_per_cycle;
+               static size_t _max_buffer_size;
+
+               uint32_t _n_inputs;
+               uint32_t _n_outputs;
+
+               uint32_t _systemic_audio_input_latency;
+               uint32_t _systemic_audio_output_latency;
+
+               /* midi settings */
+               struct AlsaMidiDeviceInfo {
+                       bool     enabled;
+                       uint32_t systemic_input_latency;
+                       uint32_t systemic_output_latency;
+                       AlsaMidiDeviceInfo()
+                               : enabled (true)
+                               , systemic_input_latency (0)
+                               , systemic_output_latency (0)
+                       {}
+               };
+
+               mutable std::map<std::string, struct AlsaMidiDeviceInfo *> _midi_devices;
+               struct AlsaMidiDeviceInfo * midi_device_info(std::string const) const;
+
+               /* processing */
+               float  _dsp_load;
+               uint64_t _processed_samples;
+               pthread_t _main_thread;
+
+               /* process threads */
+               static void* alsa_process_thread (void *);
+               std::vector<pthread_t> _threads;
+
+               struct ThreadData {
+                       AlsaAudioBackend* engine;
+                       boost::function<void ()> f;
+                       size_t stacksize;
+
+                       ThreadData (AlsaAudioBackend* e, boost::function<void ()> fp, size_t stacksz)
+                               : engine (e) , f (fp) , stacksize (stacksz) {}
+               };
+
+               /* port engine */
+               PortHandle add_port (const std::string& shortname, ARDOUR::DataType, ARDOUR::PortFlags);
+               int register_system_audio_ports ();
+               int register_system_midi_ports ();
+               void unregister_system_ports ();
+
+               std::vector<AlsaPort *> _ports;
+               std::vector<AlsaPort *> _system_inputs;
+               std::vector<AlsaPort *> _system_outputs;
+               std::vector<AlsaPort *> _system_midi_in;
+               std::vector<AlsaPort *> _system_midi_out;
+
+               std::vector<AlsaMidiOut *> _rmidi_out;
+               std::vector<AlsaMidiIn  *> _rmidi_in;
+
+               struct PortConnectData {
+                       std::string a;
+                       std::string b;
+                       bool c;
+
+                       PortConnectData (const std::string& a, const std::string& b, bool c)
+                               : a (a) , b (b) , c (c) {}
+               };
+
+               std::vector<PortConnectData *> _port_connection_queue;
+               pthread_mutex_t _port_callback_mutex;
+               bool _port_change_flag;
+
+               void port_connect_callback (const std::string& a, const std::string& b, bool conn) {
+                       pthread_mutex_lock (&_port_callback_mutex);
+                       _port_connection_queue.push_back(new PortConnectData(a, b, conn));
+                       pthread_mutex_unlock (&_port_callback_mutex);
+               }
+
+               void port_connect_add_remove_callback () {
+                       pthread_mutex_lock (&_port_callback_mutex);
+                       _port_change_flag = true;
+                       pthread_mutex_unlock (&_port_callback_mutex);
+               }
+
+               bool valid_port (PortHandle port) const {
+                       return std::find (_ports.begin (), _ports.end (), (AlsaPort*)port) != _ports.end ();
+               }
+
+               AlsaPort * find_port (const std::string& port_name) const {
+                       for (std::vector<AlsaPort*>::const_iterator it = _ports.begin (); it != _ports.end (); ++it) {
+                               if ((*it)->name () == port_name) {
+                                       return *it;
+                               }
+                       }
+                       return NULL;
+               }
+
+}; // class AlsaAudioBackend
+
+} // namespace
+
+#endif /* __libbackend_alsa_audiobackend_h__ */
diff --git a/libs/backends/alsa/alsa_midi.cc b/libs/backends/alsa/alsa_midi.cc
new file mode 100644 (file)
index 0000000..dce8478
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <unistd.h>
+
+#include <glibmm.h>
+
+#include "alsa_midi.h"
+#include "rt_thread.h"
+
+#include "pbd/error.h"
+#include "i18n.h"
+
+using namespace ARDOUR;
+
+#ifndef NDEBUG
+#define _DEBUGPRINT(STR) fprintf(stderr, STR);
+#else
+#define _DEBUGPRINT(STR) ;
+#endif
+
+AlsaMidiIO::AlsaMidiIO ()
+       : _state (-1)
+       , _running (false)
+       , _pfds (0)
+       , _sample_length_us (1e6 / 48000.0)
+       , _period_length_us (1.024e6 / 48000.0)
+       , _samples_per_period (1024)
+       , _rb (0)
+{
+       pthread_mutex_init (&_notify_mutex, 0);
+       pthread_cond_init (&_notify_ready, 0);
+
+       // MIDI (hw port) 31.25 kbaud
+       // worst case here is  8192 SPP and 8KSPS for which we'd need
+       // 4000 bytes sans MidiEventHeader.
+       // since we're not always in sync, let's use 4096.
+       _rb = new RingBuffer<uint8_t>(4096 + 4096 * sizeof(MidiEventHeader));
+}
+
+AlsaMidiIO::~AlsaMidiIO ()
+{
+       delete _rb;
+       pthread_mutex_destroy (&_notify_mutex);
+       pthread_cond_destroy (&_notify_ready);
+       free (_pfds);
+}
+
+static void * pthread_process (void *arg)
+{
+       AlsaMidiIO *d = static_cast<AlsaMidiIO *>(arg);
+       d->main_process_thread ();
+       pthread_exit (0);
+       return 0;
+}
+
+int
+AlsaMidiIO::start ()
+{
+       if (_realtime_pthread_create (SCHED_FIFO, -21, 100000,
+                               &_main_thread, pthread_process, this))
+       {
+               if (pthread_create (&_main_thread, NULL, pthread_process, this)) {
+                       PBD::error << _("AlsaMidiIO: Failed to create process thread.") << endmsg;
+                       return -1;
+               } else {
+                       PBD::warning << _("AlsaMidiIO: Cannot acquire realtime permissions.") << endmsg;
+               }
+       }
+       int timeout = 5000;
+       while (!_running && --timeout > 0) { Glib::usleep (1000); }
+       if (timeout == 0 || !_running) {
+               return -1;
+       }
+       return 0;
+}
+
+int
+AlsaMidiIO::stop ()
+{
+       void *status;
+       if (!_running) {
+               return 0;
+       }
+
+       _running = false;
+
+       pthread_mutex_lock (&_notify_mutex);
+       pthread_cond_signal (&_notify_ready);
+       pthread_mutex_unlock (&_notify_mutex);
+
+       if (pthread_join (_main_thread, &status)) {
+               PBD::error << _("AlsaMidiIO: Failed to terminate.") << endmsg;
+               return -1;
+       }
+       return 0;
+}
+
+void
+AlsaMidiIO::setup_timing (const size_t samples_per_period, const float samplerate)
+{
+       _period_length_us = (double) samples_per_period * 1e6 / samplerate;
+       _sample_length_us = 1e6 / samplerate;
+       _samples_per_period = samples_per_period;
+}
+
+void
+AlsaMidiIO::sync_time (const uint64_t tme)
+{
+       // TODO consider a PLL, if this turns out to be the bottleneck for jitter
+       // also think about using
+       // snd_pcm_status_get_tstamp() and snd_rawmidi_status_get_tstamp()
+       // instead of monotonic clock.
+#ifdef DEBUG_TIMING
+       double tdiff = (_clock_monotonic + _period_length_us - tme) / 1000.0;
+       if (abs(tdiff) >= .05) {
+               printf("AlsaMidiIO MJ: %.1f ms\n", tdiff);
+       }
+#endif
+       _clock_monotonic = tme;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AlsaMidiOut::AlsaMidiOut ()
+       : AlsaMidiIO ()
+{
+}
+
+int
+AlsaMidiOut::send_event (const pframes_t time, const uint8_t *data, const size_t size)
+{
+       const uint32_t  buf_size = sizeof (MidiEventHeader) + size;
+       if (_rb->write_space() < buf_size) {
+               _DEBUGPRINT("AlsaMidiOut: ring buffer overflow\n");
+               return -1;
+       }
+       struct MidiEventHeader h (_clock_monotonic + time * _sample_length_us, size);
+       _rb->write ((uint8_t*) &h, sizeof(MidiEventHeader));
+       _rb->write (data, size);
+
+       if (pthread_mutex_trylock (&_notify_mutex) == 0) {
+               pthread_cond_signal (&_notify_ready);
+               pthread_mutex_unlock (&_notify_mutex);
+       }
+       return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AlsaMidiIn::AlsaMidiIn ()
+       : AlsaMidiIO ()
+{
+}
+
+size_t
+AlsaMidiIn::recv_event (pframes_t &time, uint8_t *data, size_t &size)
+{
+       const uint32_t read_space = _rb->read_space();
+       struct MidiEventHeader h(0,0);
+
+       if (read_space <= sizeof(MidiEventHeader)) {
+               return 0;
+       }
+
+       RingBuffer<uint8_t>::rw_vector vector;
+       _rb->get_read_vector(&vector);
+       if (vector.len[0] >= sizeof(MidiEventHeader)) {
+               memcpy((uint8_t*)&h, vector.buf[0], sizeof(MidiEventHeader));
+       } else {
+               if (vector.len[0] > 0) {
+                       memcpy ((uint8_t*)&h, vector.buf[0], vector.len[0]);
+               }
+               memcpy (((uint8_t*)&h) + vector.len[0], vector.buf[1], sizeof(MidiEventHeader) - vector.len[0]);
+       }
+
+       if (h.time >= _clock_monotonic + _period_length_us ) {
+#ifdef DEBUG_TIMING
+               printf("AlsaMidiIn DEBUG: POSTPONE EVENT TO NEXT CYCLE: %.1f spl\n", ((h.time - _clock_monotonic) / _sample_length_us));
+#endif
+               return 0;
+       }
+       _rb->increment_read_idx (sizeof(MidiEventHeader));
+
+       assert (h.size > 0);
+       if (h.size > size) {
+               _DEBUGPRINT("AlsaMidiIn::recv_event MIDI event too large!\n");
+               _rb->increment_read_idx (h.size);
+               return 0;
+       }
+       if (_rb->read (&data[0], h.size) != h.size) {
+               _DEBUGPRINT("AlsaMidiIn::recv_event Garbled MIDI EVENT DATA!!\n");
+               return 0;
+       }
+       if (h.time < _clock_monotonic) {
+#ifdef DEBUG_TIMING
+               printf("AlsaMidiIn DEBUG: MIDI TIME < 0 %.1f spl\n", ((_clock_monotonic - h.time) / -_sample_length_us));
+#endif
+               time = 0;
+       } else if (h.time >= _clock_monotonic + _period_length_us ) {
+#ifdef DEBUG_TIMING
+               printf("AlsaMidiIn DEBUG: MIDI TIME > PERIOD %.1f spl\n", ((h.time - _clock_monotonic) / _sample_length_us));
+#endif
+               time = _samples_per_period - 1;
+       } else {
+               time = floor ((h.time - _clock_monotonic) / _sample_length_us);
+       }
+       assert(time < _samples_per_period);
+       size = h.size;
+       return h.size;
+}
+
+int
+AlsaMidiIn::queue_event (const uint64_t time, const uint8_t *data, const size_t size) {
+       const uint32_t  buf_size = sizeof(MidiEventHeader) + size;
+
+       if (size == 0) {
+               return -1;
+       }
+       if (_rb->write_space() < buf_size) {
+               _DEBUGPRINT("AlsaMidiIn: ring buffer overflow\n");
+               return -1;
+       }
+       struct MidiEventHeader h (time, size);
+       _rb->write ((uint8_t*) &h, sizeof(MidiEventHeader));
+       _rb->write (data, size);
+       return 0;
+}
diff --git a/libs/backends/alsa/alsa_midi.h b/libs/backends/alsa/alsa_midi.h
new file mode 100644 (file)
index 0000000..7da991d
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __libbackend_alsa_midi_h__
+#define __libbackend_alsa_midi_h__
+
+#include <stdint.h>
+#include <poll.h>
+#include <pthread.h>
+
+#include "pbd/ringbuffer.h"
+#include "ardour/types.h"
+
+namespace ARDOUR {
+
+class AlsaMidiIO {
+public:
+       AlsaMidiIO ();
+       virtual ~AlsaMidiIO ();
+
+       int state (void) const { return _state; }
+       int start ();
+       int stop ();
+
+       void setup_timing (const size_t samples_per_period, const float samplerate);
+       void sync_time(uint64_t);
+
+       virtual void* main_process_thread () = 0;
+
+protected:
+       pthread_t _main_thread;
+       pthread_mutex_t _notify_mutex;
+       pthread_cond_t _notify_ready;
+
+       int  _state;
+       bool  _running;
+
+       int _npfds;
+       struct pollfd *_pfds;
+
+       double _sample_length_us;
+       double _period_length_us;
+       size_t _samples_per_period;
+       uint64_t _clock_monotonic;
+
+       struct MidiEventHeader {
+               uint64_t time;
+               size_t size;
+               MidiEventHeader(const uint64_t t, const size_t s)
+                       : time(t)
+                       , size(s) {}
+       };
+
+       RingBuffer<uint8_t>* _rb;
+
+protected:
+       virtual void init (const char *device_name, const bool input) = 0;
+
+};
+
+class AlsaMidiOut : virtual public AlsaMidiIO
+{
+public:
+       AlsaMidiOut ();
+
+       int send_event (const pframes_t, const uint8_t *, const size_t);
+};
+
+class AlsaMidiIn : virtual public AlsaMidiIO
+{
+public:
+       AlsaMidiIn ();
+
+       size_t recv_event (pframes_t &, uint8_t *, size_t &);
+
+protected:
+       int queue_event (const uint64_t, const uint8_t *, const size_t);
+};
+
+} // namespace
+
+#endif
diff --git a/libs/backends/alsa/alsa_rawmidi.cc b/libs/backends/alsa/alsa_rawmidi.cc
new file mode 100644 (file)
index 0000000..e4678ba
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2010 Devin Anderson
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <unistd.h>
+#include <glibmm.h>
+
+#include "select_sleep.h"
+#include "alsa_rawmidi.h"
+
+#include "pbd/error.h"
+#include "i18n.h"
+
+using namespace ARDOUR;
+
+/* max bytes per individual midi-event
+ * events larger than this are ignored */
+#define MaxAlsaRawEventSize (64)
+
+#ifndef NDEBUG
+#define _DEBUGPRINT(STR) fprintf(stderr, STR);
+#else
+#define _DEBUGPRINT(STR) ;
+#endif
+
+AlsaRawMidiIO::AlsaRawMidiIO (const char *device, const bool input)
+       : AlsaMidiIO()
+       , _device (0)
+{
+       init (device, input);
+}
+
+AlsaRawMidiIO::~AlsaRawMidiIO ()
+{
+       if (_device) {
+               snd_rawmidi_drain (_device);
+               snd_rawmidi_close (_device);
+               _device = 0;
+       }
+}
+
+void
+AlsaRawMidiIO::init (const char *device_name, const bool input)
+{
+       if (snd_rawmidi_open (
+                               input ? &_device : NULL,
+                               input ? NULL : &_device,
+                               device_name, SND_RAWMIDI_NONBLOCK) < 0) {
+               return;
+       }
+
+       _npfds = snd_rawmidi_poll_descriptors_count (_device);
+       if (_npfds < 1) {
+               _DEBUGPRINT("AlsaRawMidiIO: no poll descriptor(s).\n");
+               snd_rawmidi_close (_device);
+               _device = 0;
+               return;
+       }
+       _pfds = (struct pollfd*) malloc (_npfds * sizeof(struct pollfd));
+       snd_rawmidi_poll_descriptors (_device, _pfds, _npfds);
+
+#if 0
+       _state = 0;
+#else
+       snd_rawmidi_params_t *params;
+       if (snd_rawmidi_params_malloc (&params)) {
+               goto initerr;
+       }
+       if (snd_rawmidi_params_current (_device, params)) {
+               goto initerr;
+       }
+       if (snd_rawmidi_params_set_avail_min (_device, params, 1)) {
+               goto initerr;
+       }
+       if ( snd_rawmidi_params_set_buffer_size (_device, params, 64)) {
+               goto initerr;
+       }
+       if (snd_rawmidi_params_set_no_active_sensing (_device, params, 1)) {
+               goto initerr;
+       }
+
+       _state = 0;
+       return;
+
+initerr:
+       _DEBUGPRINT("AlsaRawMidiIO: parameter setup error\n");
+       snd_rawmidi_close (_device);
+       _device = 0;
+#endif
+       return;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AlsaRawMidiOut::AlsaRawMidiOut (const char *device)
+               : AlsaRawMidiIO (device, false)
+               , AlsaMidiOut ()
+{
+}
+
+void *
+AlsaRawMidiOut::main_process_thread ()
+{
+       _running = true;
+       pthread_mutex_lock (&_notify_mutex);
+       bool need_drain = false;
+       while (_running) {
+               bool have_data = false;
+               struct MidiEventHeader h(0,0);
+               uint8_t data[MaxAlsaRawEventSize];
+
+               const uint32_t read_space = _rb->read_space();
+
+               if (read_space > sizeof(MidiEventHeader)) {
+                       if (_rb->read ((uint8_t*)&h, sizeof(MidiEventHeader)) != sizeof(MidiEventHeader)) {
+                               _DEBUGPRINT("AlsaRawMidiOut: Garbled MIDI EVENT HEADER!!\n");
+                               break;
+                       }
+                       assert (read_space >= h.size);
+                       if (h.size > MaxAlsaRawEventSize) {
+                               _rb->increment_read_idx (h.size);
+                               _DEBUGPRINT("AlsaRawMidiOut: MIDI event too large!\n");
+                               continue;
+                       }
+                       if (_rb->read (&data[0], h.size) != h.size) {
+                               _DEBUGPRINT("AlsaRawMidiOut: Garbled MIDI EVENT DATA!!\n");
+                               break;
+                       }
+                       have_data = true;
+               }
+
+               if (!have_data) {
+                       if (need_drain) {
+                               snd_rawmidi_drain (_device);
+                               need_drain = false;
+                       }
+                       pthread_cond_wait (&_notify_ready, &_notify_mutex);
+                       continue;
+               }
+
+               uint64_t now = g_get_monotonic_time();
+               while (h.time > now + 500) {
+                       if (need_drain) {
+                               snd_rawmidi_drain (_device);
+                               need_drain = false;
+                       } else {
+                               select_sleep(h.time - now);
+                       }
+                       now = g_get_monotonic_time();
+               }
+
+retry:
+               int perr = poll (_pfds, _npfds, 10 /* ms */);
+               if (perr < 0) {
+                       PBD::error << _("AlsaRawMidiOut: Error polling device. Terminating Midi Thread.") << endmsg;
+                       break;
+               }
+               if (perr == 0) {
+                       _DEBUGPRINT("AlsaRawMidiOut: poll() timed out.\n");
+                       goto retry;
+               }
+
+               unsigned short revents = 0;
+               if (snd_rawmidi_poll_descriptors_revents (_device, _pfds, _npfds, &revents)) {
+                       PBD::error << _("AlsaRawMidiOut: Failed to poll device. Terminating Midi Thread.") << endmsg;
+                       break;
+               }
+
+               if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
+                       PBD::error << _("AlsaRawMidiOut: poll error. Terminating Midi Thread.") << endmsg;
+                       break;
+               }
+
+               if (!(revents & POLLOUT)) {
+                       _DEBUGPRINT("AlsaRawMidiOut: POLLOUT not ready.\n");
+                       select_sleep (1000);
+                       goto retry;
+               }
+
+               ssize_t err = snd_rawmidi_write (_device, data, h.size);
+
+               if ((err == -EAGAIN)) {
+                       snd_rawmidi_drain (_device);
+                       goto retry;
+               }
+               if (err == -EWOULDBLOCK) {
+                       select_sleep (1000);
+                       goto retry;
+               }
+               if (err < 0) {
+                       PBD::error << _("AlsaRawMidiOut: write failed. Terminating Midi Thread.") << endmsg;
+                       break;
+               }
+               if ((size_t) err < h.size) {
+                       _DEBUGPRINT("AlsaRawMidiOut: short write\n");
+                       memmove(&data[0], &data[err], err);
+                       h.size -= err;
+                       goto retry;
+               }
+               need_drain = true;
+       }
+
+       pthread_mutex_unlock (&_notify_mutex);
+       _DEBUGPRINT("AlsaRawMidiOut: MIDI OUT THREAD STOPPED\n");
+       return 0;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+AlsaRawMidiIn::AlsaRawMidiIn (const char *device)
+               : AlsaRawMidiIO (device, true)
+               , AlsaMidiIn ()
+               , _event(0,0)
+               , _first_time(true)
+               , _unbuffered_bytes(0)
+               , _total_bytes(0)
+               , _expected_bytes(0)
+               , _status_byte(0)
+{
+}
+
+void *
+AlsaRawMidiIn::main_process_thread ()
+{
+       _running = true;
+       while (_running) {
+               unsigned short revents = 0;
+
+               int perr = poll (_pfds, _npfds, 100 /* ms */);
+               if (perr < 0) {
+                       PBD::error << _("AlsaRawMidiIn: Error polling device. Terminating Midi Thread.") << endmsg;
+                       break;
+               }
+               if (perr == 0) {
+                       continue;
+               }
+
+               if (snd_rawmidi_poll_descriptors_revents (_device, _pfds, _npfds, &revents)) {
+                       PBD::error << _("AlsaRawMidiIn: Failed to poll device. Terminating Midi Thread.") << endmsg;
+                       break;
+               }
+
+               if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
+                       PBD::error << _("AlsaRawMidiIn: poll error. Terminating Midi Thread.") << endmsg;
+                       break;
+               }
+
+               if (!(revents & POLLIN)) {
+                       _DEBUGPRINT("AlsaRawMidiOut: POLLIN not ready.\n");
+                       select_sleep (1000);
+                       continue;
+               }
+
+               uint8_t data[MaxAlsaRawEventSize];
+               uint64_t time = g_get_monotonic_time();
+               ssize_t err = snd_rawmidi_read (_device, data, sizeof(data));
+
+               if ((err == -EAGAIN) || (err == -EWOULDBLOCK)) {
+                       continue;
+               }
+               if (err < 0) {
+                       PBD::error << _("AlsaRawMidiIn: read error. Terminating Midi") << endmsg;
+                       break;
+               }
+               if (err == 0) {
+                       _DEBUGPRINT("AlsaRawMidiIn: zero read\n");
+                       continue;
+               }
+
+#if 0
+               queue_event (time, data, err);
+#else
+               parse_events (time, data, err);
+#endif
+       }
+
+       _DEBUGPRINT("AlsaRawMidiIn: MIDI IN THREAD STOPPED\n");
+       return 0;
+}
+
+int
+AlsaRawMidiIn::queue_event (const uint64_t time, const uint8_t *data, const size_t size) {
+       _event._pending = false;
+       return AlsaMidiIn::queue_event(time, data, size);
+}
+
+void
+AlsaRawMidiIn::parse_events (const uint64_t time, const uint8_t *data, const size_t size) {
+       if (_event._pending) {
+               _DEBUGPRINT("AlsaRawMidiIn: queue pending event\n");
+               if (queue_event (_event._time, _parser_buffer, _event._size)) {
+                       return;
+               }
+       }
+       for (size_t i = 0; i < size; ++i) {
+               if (_first_time && !(data[i] & 0x80)) {
+                       continue;
+               }
+               _first_time = false; /// TODO optimize e.g. use fn pointer to different parse_events()
+               if (process_byte(time, data[i])) {
+                       if (queue_event (_event._time, _parser_buffer, _event._size)) {
+                               return;
+                       }
+               }
+       }
+}
+
+// based on JackMidiRawInputWriteQueue by Devin Anderson //
+bool
+AlsaRawMidiIn::process_byte(const uint64_t time, const uint8_t byte)
+{
+       if (byte >= 0xf8) {
+               // Realtime
+               if (byte == 0xfd) {
+                       return false;
+               }
+               _parser_buffer[0] = byte;
+               prepare_byte_event(time, byte);
+               return true;
+       }
+       if (byte == 0xf7) {
+               // Sysex end
+               if (_status_byte == 0xf0) {
+                       record_byte(byte);
+                       return prepare_buffered_event(time);
+               }
+    _total_bytes = 0;
+    _unbuffered_bytes = 0;
+               _expected_bytes = 0;
+               _status_byte = 0;
+               return false;
+       }
+       if (byte >= 0x80) {
+               // Non-realtime status byte
+               if (_total_bytes) {
+                       _DEBUGPRINT("AlsaRawMidiIn: discarded bogus midi message\n");
+#if 0
+                       for (size_t i=0; i < _total_bytes; ++i) {
+                               printf("%02x ", _parser_buffer[i]);
+                       }
+                       printf("\n");
+#endif
+                       _total_bytes = 0;
+                       _unbuffered_bytes = 0;
+               }
+               _status_byte = byte;
+               switch (byte & 0xf0) {
+                       case 0x80:
+                       case 0x90:
+                       case 0xa0:
+                       case 0xb0:
+                       case 0xe0:
+                               // Note On, Note Off, Aftertouch, Control Change, Pitch Wheel
+                               _expected_bytes = 3;
+                               break;
+                       case 0xc0:
+                       case 0xd0:
+                               // Program Change, Channel Pressure
+                               _expected_bytes = 2;
+                               break;
+                       case 0xf0:
+                               switch (byte) {
+                                       case 0xf0:
+                                               // Sysex
+                                               _expected_bytes = 0;
+                                               break;
+                                       case 0xf1:
+                                       case 0xf3:
+                                               // MTC Quarter Frame, Song Select
+                                               _expected_bytes = 2;
+                                               break;
+                                       case 0xf2:
+                                               // Song Position
+                                               _expected_bytes = 3;
+                                               break;
+                                       case 0xf4:
+                                       case 0xf5:
+                                               // Undefined
+                                               _expected_bytes = 0;
+                                               _status_byte = 0;
+                                               return false;
+                                       case 0xf6:
+                                               // Tune Request
+                                               prepare_byte_event(time, byte);
+                                               _expected_bytes = 0;
+                                               _status_byte = 0;
+                                               return true;
+                               }
+               }
+               record_byte(byte);
+               return false;
+       }
+       // Data byte
+       if (! _status_byte) {
+               // Data bytes without a status will be discarded.
+               _total_bytes++;
+               _unbuffered_bytes++;
+               return false;
+       }
+       if (! _total_bytes) {
+               _DEBUGPRINT("AlsaRawMidiIn: apply running status\n");
+               record_byte(_status_byte);
+       }
+       record_byte(byte);
+       return (_total_bytes == _expected_bytes) ? prepare_buffered_event(time) : false;
+}
diff --git a/libs/backends/alsa/alsa_rawmidi.h b/libs/backends/alsa/alsa_rawmidi.h
new file mode 100644 (file)
index 0000000..5d9a86c
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __libbackend_alsa_rawmidi_h__
+#define __libbackend_alsa_rawmidi_h__
+
+#include <stdint.h>
+#include <poll.h>
+#include <pthread.h>
+
+#include <alsa/asoundlib.h>
+
+#include "pbd/ringbuffer.h"
+#include "ardour/types.h"
+#include "alsa_midi.h"
+
+namespace ARDOUR {
+
+class AlsaRawMidiIO : virtual public AlsaMidiIO {
+public:
+       AlsaRawMidiIO (const char *device, const bool input);
+       virtual ~AlsaRawMidiIO ();
+
+protected:
+       snd_rawmidi_t *_device;
+
+private:
+       void init (const char *device_name, const bool input);
+};
+
+class AlsaRawMidiOut : public AlsaRawMidiIO, public AlsaMidiOut
+{
+public:
+       AlsaRawMidiOut (const char *device);
+       void* main_process_thread ();
+};
+
+class AlsaRawMidiIn : public AlsaRawMidiIO, public AlsaMidiIn
+{
+public:
+       AlsaRawMidiIn (const char *device);
+
+       void* main_process_thread ();
+
+protected:
+       int queue_event (const uint64_t, const uint8_t *, const size_t);
+private:
+       void parse_events (const uint64_t, const uint8_t *, const size_t);
+       bool process_byte (const uint64_t, const uint8_t);
+
+       void record_byte(uint8_t byte) {
+               if (_total_bytes < sizeof(_parser_buffer)) {
+                       _parser_buffer[_total_bytes] = byte;
+               } else {
+                       ++_unbuffered_bytes;
+               }
+               ++_total_bytes;
+       }
+
+       void prepare_byte_event(const uint64_t time, const uint8_t byte) {
+               _parser_buffer[0] = byte;
+               _event.prepare(time, 1);
+       }
+
+       bool prepare_buffered_event(const uint64_t time) {
+               const bool result = _unbuffered_bytes == 0;
+               if (result) {
+                       _event.prepare(time, _total_bytes);
+               }
+               _total_bytes = 0;
+               _unbuffered_bytes = 0;
+               if (_status_byte >= 0xf0) {
+                       _expected_bytes = 0;
+                       _status_byte = 0;
+               }
+               return result;
+       }
+
+       struct ParserEvent {
+               uint64_t _time;
+               size_t _size;
+               bool _pending;
+               ParserEvent (const uint64_t time, const size_t size)
+                       : _time(time)
+                       , _size(size)
+                       , _pending(false) {}
+
+               void prepare(const uint64_t time, const size_t size) {
+                       _time = time;
+                       _size = size;
+                       _pending = true;
+               }
+       } _event;
+
+       bool    _first_time;
+       size_t  _unbuffered_bytes;
+       size_t  _total_bytes;
+       size_t  _expected_bytes;
+       uint8_t _status_byte;
+       uint8_t _parser_buffer[1024];
+};
+
+} // namespace
+
+#endif
diff --git a/libs/backends/alsa/alsa_sequencer.cc b/libs/backends/alsa/alsa_sequencer.cc
new file mode 100644 (file)
index 0000000..aa0aac0
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <unistd.h>
+#include <glibmm.h>
+
+#include "select_sleep.h"
+#include "alsa_sequencer.h"
+
+#include "pbd/error.h"
+#include "i18n.h"
+
+using namespace ARDOUR;
+
+/* max bytes per individual midi-event
+ * events larger than this are ignored */
+#define MaxAlsaSeqEventSize (64)
+
+#ifndef NDEBUG
+#define _DEBUGPRINT(STR) fprintf(stderr, STR);
+#else
+#define _DEBUGPRINT(STR) ;
+#endif
+
+AlsaSeqMidiIO::AlsaSeqMidiIO (const char *device, const bool input)
+       : AlsaMidiIO()
+       , _seq (0)
+{
+       init (device, input);
+}
+
+AlsaSeqMidiIO::~AlsaSeqMidiIO ()
+{
+       if (_seq) {
+               snd_seq_close (_seq);
+               _seq = 0;
+       }
+}
+
+void
+AlsaSeqMidiIO::init (const char *device_name, const bool input)
+{
+       if (snd_seq_open (&_seq, "hw",
+                               input ? SND_SEQ_OPEN_INPUT : SND_SEQ_OPEN_OUTPUT, 0) < 0)
+       {
+               _seq = 0;
+               return;
+       }
+
+       if (snd_seq_set_client_name (_seq, "Ardour")) {
+               _DEBUGPRINT("AlsaSeqMidiIO: cannot set client name.\n");
+               goto initerr;
+       }
+
+       _port = snd_seq_create_simple_port (_seq, "port", SND_SEQ_PORT_CAP_NO_EXPORT |
+                       (input ? SND_SEQ_PORT_CAP_WRITE : SND_SEQ_PORT_CAP_READ),
+                       SND_SEQ_PORT_TYPE_APPLICATION);
+
+       if (_port < 0) {
+               _DEBUGPRINT("AlsaSeqMidiIO: cannot create port.\n");
+               goto initerr;
+       }
+
+       _npfds = snd_seq_poll_descriptors_count (_seq, input ? POLLIN : POLLOUT);
+       if (_npfds < 1) {
+               _DEBUGPRINT("AlsaSeqMidiIO: no poll descriptor(s).\n");
+               goto initerr;
+       }
+       _pfds = (struct pollfd*) malloc (_npfds * sizeof(struct pollfd));
+       snd_seq_poll_descriptors (_seq, _pfds, _npfds, input ? POLLIN : POLLOUT);
+
+
+       snd_seq_addr_t port;
+       if (snd_seq_parse_address (_seq, &port, device_name) < 0) {
+               _DEBUGPRINT("AlsaSeqMidiIO: cannot resolve hardware port.\n");
+               goto initerr;
+       }
+
+       if (input) {
+               if (snd_seq_connect_from (_seq, _port, port.client, port.port) < 0) {
+                       _DEBUGPRINT("AlsaSeqMidiIO: cannot connect input port.\n");
+                       goto initerr;
+               }
+       } else {
+               if (snd_seq_connect_to (_seq, _port, port.client, port.port) < 0) {
+                       _DEBUGPRINT("AlsaSeqMidiIO: cannot connect output port.\n");
+                       goto initerr;
+               }
+       }
+
+       snd_seq_nonblock(_seq, 1);
+
+       _state = 0;
+       return;
+
+initerr:
+       PBD::error << _("AlsaSeqMidiIO: Device initialization failed.") << endmsg;
+       snd_seq_close (_seq);
+       _seq = 0;
+       return;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AlsaSeqMidiOut::AlsaSeqMidiOut (const char *device)
+               : AlsaSeqMidiIO (device, false)
+               , AlsaMidiOut ()
+{
+}
+
+void *
+AlsaSeqMidiOut::main_process_thread ()
+{
+       _running = true;
+       bool need_drain = false;
+       snd_midi_event_t *alsa_codec = NULL;
+       snd_midi_event_new (MaxAlsaSeqEventSize, &alsa_codec);
+       pthread_mutex_lock (&_notify_mutex);
+       while (_running) {
+               bool have_data = false;
+               struct MidiEventHeader h(0,0);
+               uint8_t data[MaxAlsaSeqEventSize];
+
+               const uint32_t read_space = _rb->read_space();
+
+               if (read_space > sizeof(MidiEventHeader)) {
+                       if (_rb->read ((uint8_t*)&h, sizeof(MidiEventHeader)) != sizeof(MidiEventHeader)) {
+                               _DEBUGPRINT("AlsaSeqMidiOut: Garbled MIDI EVENT HEADER!!\n");
+                               break;
+                       }
+                       assert (read_space >= h.size);
+                       if (h.size > MaxAlsaSeqEventSize) {
+                               _rb->increment_read_idx (h.size);
+                               _DEBUGPRINT("AlsaSeqMidiOut: MIDI event too large!\n");
+                               continue;
+                       }
+                       if (_rb->read (&data[0], h.size) != h.size) {
+                               _DEBUGPRINT("AlsaSeqMidiOut: Garbled MIDI EVENT DATA!!\n");
+                               break;
+                       }
+                       have_data = true;
+               }
+
+               if (!have_data) {
+                       if (need_drain) {
+                               snd_seq_drain_output (_seq);
+                               need_drain = false;
+                       }
+                       pthread_cond_wait (&_notify_ready, &_notify_mutex);
+                       continue;
+               }
+
+               snd_seq_event_t alsa_event;
+               snd_seq_ev_clear (&alsa_event);
+               snd_midi_event_reset_encode (alsa_codec);
+               if (!snd_midi_event_encode (alsa_codec, data, h.size, &alsa_event)) {
+                       PBD::error << _("AlsaSeqMidiOut: Invalid Midi Event.") << endmsg;
+                       continue;
+               }
+
+               snd_seq_ev_set_source (&alsa_event, _port);
+               snd_seq_ev_set_subs (&alsa_event);
+               snd_seq_ev_set_direct (&alsa_event);
+
+               uint64_t now = g_get_monotonic_time();
+               while (h.time > now + 500) {
+                       if (need_drain) {
+                               snd_seq_drain_output (_seq);
+                               need_drain = false;
+                       } else {
+                               select_sleep(h.time - now);
+                       }
+                       now = g_get_monotonic_time();
+               }
+
+retry:
+               int perr = poll (_pfds, _npfds, 10 /* ms */);
+               if (perr < 0) {
+                       PBD::error << _("AlsaSeqMidiOut: Error polling device. Terminating Midi Thread.") << endmsg;
+                       break;
+               }
+               if (perr == 0) {
+                       _DEBUGPRINT("AlsaSeqMidiOut: poll() timed out.\n");
+                       goto retry;
+               }
+
+               ssize_t err = snd_seq_event_output(_seq, &alsa_event);
+
+               if ((err == -EAGAIN)) {
+                       snd_seq_drain_output (_seq);
+                       goto retry;
+               }
+               if (err == -EWOULDBLOCK) {
+                       select_sleep (1000);
+                       goto retry;
+               }
+               if (err < 0) {
+                       PBD::error << _("AlsaSeqMidiOut: write failed. Terminating Midi Thread.") << endmsg;
+                       break;
+               }
+               need_drain = true;
+       }
+
+       pthread_mutex_unlock (&_notify_mutex);
+
+       if (alsa_codec) {
+               snd_midi_event_free(alsa_codec);
+       }
+       _DEBUGPRINT("AlsaSeqMidiOut: MIDI OUT THREAD STOPPED\n");
+       return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AlsaSeqMidiIn::AlsaSeqMidiIn (const char *device)
+               : AlsaSeqMidiIO (device, true)
+               , AlsaMidiIn ()
+{
+}
+
+void *
+AlsaSeqMidiIn::main_process_thread ()
+{
+       _running = true;
+       bool do_poll = true;
+       snd_midi_event_t *alsa_codec = NULL;
+       snd_midi_event_new (MaxAlsaSeqEventSize, &alsa_codec);
+
+       while (_running) {
+
+               if (do_poll) {
+                       snd_seq_poll_descriptors (_seq, _pfds, _npfds, POLLIN);
+                       int perr = poll (_pfds, _npfds, 100 /* ms */);
+
+                       if (perr < 0) {
+                               PBD::error << _("AlsaSeqMidiIn: Error polling device. Terminating Midi Thread.") << endmsg;
+                               break;
+                       }
+                       if (perr == 0) {
+                               continue;
+                       }
+               }
+
+               snd_seq_event_t *event;
+               uint64_t time = g_get_monotonic_time();
+               ssize_t err = snd_seq_event_input (_seq, &event);
+
+               if ((err == -EAGAIN) || (err == -EWOULDBLOCK)) {
+                       do_poll = true;
+                       continue;
+               }
+               if (err == -ENOSPC) {
+                       PBD::error << _("AlsaSeqMidiIn: FIFO overrun.") << endmsg;
+                       do_poll = true;
+                       continue;
+               }
+               if (err < 0) {
+                       PBD::error << _("AlsaSeqMidiIn: read error. Terminating Midi") << endmsg;
+                       break;
+               }
+
+               uint8_t data[MaxAlsaSeqEventSize];
+               snd_midi_event_reset_decode (alsa_codec);
+               ssize_t size = snd_midi_event_decode (alsa_codec, data, sizeof(data), event);
+
+               if (size > 0) {
+                       queue_event (time, data, size);
+               }
+               do_poll = (0 == err);
+       }
+
+       if (alsa_codec) {
+               snd_midi_event_free(alsa_codec);
+       }
+       _DEBUGPRINT("AlsaSeqMidiIn: MIDI IN THREAD STOPPED\n");
+       return 0;
+}
diff --git a/libs/backends/alsa/alsa_sequencer.h b/libs/backends/alsa/alsa_sequencer.h
new file mode 100644 (file)
index 0000000..bc00751
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __libbackend_alsa_sequencer_h__
+#define __libbackend_alsa_sequencer_h__
+
+#include <stdint.h>
+#include <poll.h>
+#include <pthread.h>
+
+#include <alsa/asoundlib.h>
+
+#include "pbd/ringbuffer.h"
+#include "ardour/types.h"
+#include "alsa_midi.h"
+
+namespace ARDOUR {
+
+class AlsaSeqMidiIO : virtual public AlsaMidiIO {
+public:
+       AlsaSeqMidiIO (const char *port_name, const bool input);
+       virtual ~AlsaSeqMidiIO ();
+
+protected:
+       snd_seq_t *_seq;
+       int _port;
+
+private:
+       void init (const char *device_name, const bool input);
+};
+
+class AlsaSeqMidiOut : public AlsaSeqMidiIO, public AlsaMidiOut
+{
+public:
+       AlsaSeqMidiOut (const char *port_name);
+       void* main_process_thread ();
+};
+
+class AlsaSeqMidiIn : public AlsaSeqMidiIO, public AlsaMidiIn
+{
+public:
+       AlsaSeqMidiIn (const char *port_name);
+
+       void* main_process_thread ();
+};
+
+} // namespace
+
+#endif
diff --git a/libs/backends/alsa/rt_thread.h b/libs/backends/alsa/rt_thread.h
new file mode 100644 (file)
index 0000000..3d2efe2
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __libbackend_alsa_rthread_h__
+#define __libbackend_alsa_rthread_h__
+
+#include <pthread.h>
+#include <sched.h>
+
+static int
+_realtime_pthread_create (
+               const int policy, int priority, const size_t stacksize,
+               pthread_t *thread,
+               void *(*start_routine) (void *),
+               void *arg)
+{
+       int rv;
+
+       pthread_attr_t attr;
+       struct sched_param parm;
+
+       const int p_min = sched_get_priority_min (policy);
+       const int p_max = sched_get_priority_max (policy);
+       priority += p_max;
+       if (priority > p_max) priority = p_max;
+       if (priority < p_min) priority = p_min;
+       parm.sched_priority = priority;
+
+       pthread_attr_init (&attr);
+       pthread_attr_setschedpolicy (&attr, policy);
+       pthread_attr_setschedparam (&attr, &parm);
+       pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
+       pthread_attr_setinheritsched (&attr, PTHREAD_EXPLICIT_SCHED);
+       pthread_attr_setstacksize (&attr, stacksize);
+       rv = pthread_create (thread, &attr, start_routine, arg);
+       pthread_attr_destroy (&attr);
+       return rv;
+}
+
+#endif
diff --git a/libs/backends/alsa/select_sleep.h b/libs/backends/alsa/select_sleep.h
new file mode 100644 (file)
index 0000000..ec6a93d
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2004,2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <sys/select.h>
+
+/* select() sleeps _at most_ a given time.
+ * (compared to usleep() or nanosleep() which sleep at least a given time)
+ */
+static void select_sleep (uint64_t usec) {
+       if (usec <= 10) return;
+       fd_set fd;
+       int max_fd=0;
+       struct timeval tv;
+       tv.tv_sec = usec / 1000000;
+       tv.tv_usec = usec % 1000000;
+       FD_ZERO (&fd);
+       select (max_fd, &fd, NULL, NULL, &tv);
+       // on Linux, tv reflects the actual time slept.
+}
diff --git a/libs/backends/alsa/wscript b/libs/backends/alsa/wscript
new file mode 100644 (file)
index 0000000..173b6e0
--- /dev/null
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+from waflib.extras import autowaf as autowaf
+import os
+import sys
+import re
+
+# Library version (UNIX style major, minor, micro)
+# major increment <=> incompatible changes
+# minor increment <=> compatible changes (additions)
+# micro increment <=> no interface changes
+ALSABACKEND_VERSION = '0.0.1'
+I18N_PACKAGE = 'alsa-backend'
+
+# Mandatory variables
+top = '.'
+out = 'build'
+
+def options(opt):
+    autowaf.set_options(opt)
+
+def configure(conf):
+    autowaf.configure(conf)
+
+def build(bld):
+    obj = bld(features = 'cxx cxxshlib')
+    obj.source = [
+            'alsa_audiobackend.cc',
+            'alsa_midi.cc',
+            'alsa_rawmidi.cc',
+            'alsa_sequencer.cc',
+            'zita-alsa-pcmi.cc',
+            ]
+    obj.includes = ['.']
+    obj.name     = 'alsa_audiobackend'
+    obj.target   = 'alsa_audiobackend'
+    obj.use      = 'libardour libpbd ardouralsautil'
+    obj.uselib   = 'ALSA'
+    obj.vnum     = ALSABACKEND_VERSION
+    obj.install_path  = os.path.join(bld.env['LIBDIR'], 'backends')
+    obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"',
+                   'ARDOURBACKEND_DLL_EXPORTS'
+                  ]
diff --git a/libs/backends/alsa/zita-alsa-pcmi.cc b/libs/backends/alsa/zita-alsa-pcmi.cc
new file mode 100644 (file)
index 0000000..5aba08c
--- /dev/null
@@ -0,0 +1,1138 @@
+// ----------------------------------------------------------------------------
+//
+//  Copyright (C) 2006-2012 Fons Adriaensen <fons@linuxaudio.org>
+//
+//  This program is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation; either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+// ----------------------------------------------------------------------------
+
+
+#include <endian.h>
+#include <sys/time.h>
+#include "zita-alsa-pcmi.h"
+
+
+// Public members ----------------------------------------------------------------------
+
+
+int zita_alsa_pcmi_major_version (void)
+{
+       return ZITA_ALSA_PCMI_MAJOR_VERSION;
+}
+
+
+int zita_alsa_pcmi_minor_version (void)
+{
+       return ZITA_ALSA_PCMI_MINOR_VERSION;
+}
+
+
+Alsa_pcmi::Alsa_pcmi (
+               const char        *play_name,
+               const char        *capt_name,
+               const char        *ctrl_name,
+               unsigned int       fsamp,
+               unsigned int       fsize,
+               unsigned int       nfrag,
+               unsigned int       debug)
+       : _fsamp (fsamp)
+       , _fsize (fsize)
+       , _nfrag (nfrag)
+       , _debug (debug)
+       , _state (-1)
+       , _play_handle (0)
+       , _capt_handle (0)
+       , _ctrl_handle (0)
+       , _play_hwpar (0)
+       , _play_swpar (0)
+       , _capt_hwpar (0)
+       , _capt_swpar (0)
+       , _play_nchan (0)
+       , _capt_nchan (0)
+       , _play_xrun (0)
+       , _capt_xrun (0)
+       , _synced (false)
+       , _play_npfd (0)
+       , _capt_npfd (0)
+{
+       const char *p;
+
+       p = getenv ("ZITA_ALSA_PCMI_DEBUG");
+       if (p && *p) _debug = atoi (p);
+       initialise (play_name, capt_name, ctrl_name);
+}
+
+
+Alsa_pcmi::~Alsa_pcmi (void)
+{
+       if (_play_handle) snd_pcm_close (_play_handle);
+       if (_capt_handle) snd_pcm_close (_capt_handle);
+       if (_ctrl_handle) snd_ctl_close (_ctrl_handle);
+
+       snd_pcm_sw_params_free (_capt_swpar);
+       snd_pcm_hw_params_free (_capt_hwpar);
+       snd_pcm_sw_params_free (_play_swpar);
+       snd_pcm_hw_params_free (_play_hwpar);
+}
+
+
+int Alsa_pcmi::pcm_start (void)
+{
+       unsigned int i, j, n;
+       int          err;
+
+       if (_play_handle)
+       {
+               n = snd_pcm_avail_update (_play_handle);
+               if (n != _fsize * _nfrag)
+               {
+                       if (_debug & DEBUG_STAT) fprintf  (stderr, "Alsa_pcmi: full buffer not available at start.\n");
+                       return -1;
+               }
+               for (i = 0; i < _nfrag; i++)
+               {
+                       play_init (_fsize);
+                       for (j = 0; j < _play_nchan; j++) clear_chan (j, _fsize);
+                       play_done (_fsize);
+               }
+               if ((err = snd_pcm_start (_play_handle)) < 0)
+               {
+                       if (_debug & DEBUG_STAT) fprintf (stderr, "Alsa_pcmi: pcm_start(play): %s.\n", snd_strerror (err));
+                       return -1;
+               }
+       }
+       if (_capt_handle && !_synced && ((err = snd_pcm_start (_capt_handle)) < 0))
+       {
+               if (_debug & DEBUG_STAT) fprintf (stderr, "Alsa_pcmi: pcm_start(capt): %s.\n", snd_strerror (err));
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int Alsa_pcmi::pcm_stop (void)
+{
+       int err;
+
+       if (_play_handle && ((err = snd_pcm_drop (_play_handle)) < 0))
+       {
+               if (_debug & DEBUG_STAT) fprintf (stderr, "Alsa_pcmi: pcm_drop(play): %s.\n", snd_strerror (err));
+               return -1;
+       }
+       if (_capt_handle && !_synced && ((err = snd_pcm_drop (_capt_handle)) < 0))
+       {
+               if (_debug & DEBUG_STAT) fprintf (stderr, "Alsa_pcmi: pcm_drop(capt): %s.\n", snd_strerror (err));
+               return -1;
+       }
+
+       return 0;
+}
+
+
+snd_pcm_sframes_t Alsa_pcmi::pcm_wait (void)
+{
+       bool              need_capt;
+       bool              need_play;
+       snd_pcm_sframes_t capt_av;
+       snd_pcm_sframes_t play_av;
+       unsigned short    rev;
+       int               i, r, n1, n2;
+
+       _state = 0;
+       need_capt = _capt_handle ? true : false;
+       need_play = _play_handle ? true : false;
+
+       while (need_play || need_capt)
+       {
+               n1 = 0;
+               if (need_play)
+               {
+                       snd_pcm_poll_descriptors (_play_handle, _poll_fd, _play_npfd);
+                       n1 += _play_npfd;
+               }
+               n2 = n1;
+               if (need_capt)
+               {
+                       snd_pcm_poll_descriptors (_capt_handle, _poll_fd + n1, _capt_npfd);
+                       n2 += _capt_npfd;
+               }
+               for (i = 0; i < n2; i++) _poll_fd [i].events |= POLLERR;
+
+               r = poll (_poll_fd, n2, 1000);
+               if (r < 0)
+               {
+                       if (errno == EINTR) return 0;
+                       if (_debug & DEBUG_WAIT) fprintf (stderr, "Alsa_pcmi: poll(): %s\n.", strerror (errno));
+                       _state = -1;
+                       return 0;
+               }
+               if (r == 0)
+               {
+                       if (_debug & DEBUG_WAIT) fprintf (stderr, "Alsa_pcmi: poll timed out.\n");
+                       _state = -1;
+                       return 0;
+               }
+
+               if (need_play)
+               {
+                       snd_pcm_poll_descriptors_revents (_play_handle, _poll_fd, n1, &rev);
+                       if (rev & POLLERR)
+                       {
+                               if (_debug & DEBUG_WAIT) fprintf (stderr, "Alsa_pcmi: error on playback pollfd.\n");
+                               _state = 1;
+                               recover ();
+                               return 0;
+                       }
+                       if (rev & POLLOUT) need_play = false;
+               }
+               if (need_capt)
+               {
+                       snd_pcm_poll_descriptors_revents (_capt_handle, _poll_fd + n1, n2 - n1, &rev);
+                       if (rev & POLLERR)
+                       {
+                               if (_debug & DEBUG_WAIT) fprintf (stderr, "Alsa_pcmi: error on capture pollfd.\n");
+                               _state = 1;
+                               recover ();
+                               return 0;
+                       }
+                       if (rev & POLLIN) need_capt = false;
+               }
+       }
+
+       play_av = 999999999;
+       if (_play_handle && (play_av = snd_pcm_avail_update (_play_handle)) < 0)
+       {
+               _state = -1;
+               recover ();
+               return 0;
+       }
+       capt_av = 999999999;
+       if (_capt_handle && (capt_av = snd_pcm_avail_update (_capt_handle)) < 0)
+       {
+               _state = -1;
+               recover ();
+               return 0;
+       }
+
+       return (capt_av < play_av) ? capt_av : play_av;
+}
+
+
+int Alsa_pcmi::pcm_idle (int len)
+{
+       unsigned int       i;
+       snd_pcm_uframes_t  n, k;
+
+       if (_capt_handle)
+       {
+               n = len;
+               while (n)
+               {
+                       k = capt_init (n);
+                       capt_done (k);
+                       n -= k;
+               }
+       }
+       if (_play_handle)
+       {
+               n = len;
+               while (n)
+               {
+                       k = play_init (n);
+                       for (i = 0; i < _play_nchan; i++) clear_chan (i, k);
+                       play_done (k);
+                       n -= k;
+               }
+       }
+       return 0;
+}
+
+
+int Alsa_pcmi::play_init (snd_pcm_uframes_t len)
+{
+       unsigned int                   i;
+       const snd_pcm_channel_area_t   *a;
+       int                            err;
+
+       if ((err = snd_pcm_mmap_begin (_play_handle, &a, &_play_offs, &len)) < 0)
+       {
+               if (_debug & DEBUG_DATA) fprintf (stderr, "Alsa_pcmi: snd_pcm_mmap_begin(play): %s.\n", snd_strerror (err));
+               return -1;
+       }
+       _play_step = (a->step) >> 3;
+       for (i = 0; i < _play_nchan; i++, a++)
+       {
+               _play_ptr [i] = (char *) a->addr + ((a->first + a->step * _play_offs) >> 3);
+       }
+
+       return len;
+}
+
+
+int Alsa_pcmi::capt_init (snd_pcm_uframes_t len)
+{
+       unsigned int                  i;
+       const snd_pcm_channel_area_t  *a;
+       int                           err;
+
+       if ((err = snd_pcm_mmap_begin (_capt_handle, &a, &_capt_offs, &len)) < 0)
+       {
+               if (_debug & DEBUG_DATA) fprintf (stderr, "Alsa_pcmi: snd_pcm_mmap_begin(capt): %s.\n", snd_strerror (err));
+               return -1;
+       }
+       _capt_step = (a->step) >> 3;
+       for (i = 0; i < _capt_nchan; i++, a++)
+       {
+               _capt_ptr [i] = (char *) a->addr + ((a->first + a->step * _capt_offs) >> 3);
+       }
+
+       return len;
+}
+
+
+void Alsa_pcmi::clear_chan (int chan, int len)
+{
+       _play_ptr [chan] = (this->*Alsa_pcmi::_clear_func)(_play_ptr [chan], len);
+}
+
+
+void Alsa_pcmi::play_chan (int chan, const float *src, int len, int step)
+{
+       _play_ptr [chan] = (this->*Alsa_pcmi::_play_func)(src, _play_ptr [chan], len, step);
+}
+
+
+void Alsa_pcmi::capt_chan  (int chan, float *dst, int len, int step)
+{
+       _capt_ptr [chan] = (this->*Alsa_pcmi::_capt_func)(_capt_ptr [chan], dst, len, step);
+}
+
+
+int Alsa_pcmi::play_done (int len)
+{
+       return snd_pcm_mmap_commit (_play_handle, _play_offs, len);
+}
+
+
+int Alsa_pcmi::capt_done (int len)
+{
+       return snd_pcm_mmap_commit (_capt_handle, _capt_offs, len);
+}
+
+
+void Alsa_pcmi::printinfo (void)
+{
+       fprintf (stdout, "playback :");
+       if (_play_handle)
+       {
+               fprintf (stdout, "\n  nchan  : %d\n", _play_nchan);
+               fprintf (stdout, "  fsamp  : %d\n", _fsamp);
+               fprintf (stdout, "  fsize  : %ld\n", _fsize);
+               fprintf (stdout, "  nfrag  : %d\n", _nfrag);
+               fprintf (stdout, "  format : %s\n", snd_pcm_format_name (_play_format));
+       }
+       else fprintf (stdout, " not enabled\n");
+       fprintf (stdout, "capture  :");
+       if (_capt_handle)
+       {
+               fprintf (stdout, "\n  nchan  : %d\n", _capt_nchan);
+               fprintf (stdout, "  fsamp  : %d\n", _fsamp);
+               fprintf (stdout, "  fsize  : %ld\n", _fsize);
+               fprintf (stdout, "  nfrag  : %d\n", _nfrag);
+               fprintf (stdout, "  format : %s\n", snd_pcm_format_name (_capt_format));
+               if (_play_handle) fprintf (stdout, "%s\n", _synced ? "synced" : "not synced");
+       }
+       else fprintf (stdout, " not enabled\n");
+}
+
+
+// Private members ---------------------------------------------------------------------
+
+
+void Alsa_pcmi::initialise (const char *play_name, const char *capt_name, const char *ctrl_name)
+{
+       unsigned int          fsamp;
+       snd_pcm_uframes_t     fsize;
+       unsigned int          nfrag;
+       int                   err;
+       int                   dir;
+       snd_ctl_card_info_t  *card;
+
+       if (play_name)
+       {
+               if (snd_pcm_open (&_play_handle, play_name, SND_PCM_STREAM_PLAYBACK, 0) < 0)
+               {
+                       _play_handle = 0;
+                       if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: Cannot open PCM device %s for playback.\n",
+                                       play_name);
+               }
+       }
+
+       if (capt_name)
+       {
+               if (snd_pcm_open (&_capt_handle, capt_name, SND_PCM_STREAM_CAPTURE, 0) < 0)
+               {
+                       _capt_handle = 0;
+                       if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: Cannot open PCM device %s for capture.\n",
+                                       capt_name);
+               }
+       }
+
+       if (! _play_handle || ! _capt_handle) return;
+
+       if (ctrl_name)
+       {
+               snd_ctl_card_info_alloca (&card);
+
+               if ((err = snd_ctl_open (&_ctrl_handle, ctrl_name, 0)) < 0)
+               {
+                       if (_debug & DEBUG_INIT) fprintf  (stderr, "Alse_driver: ctl_open(): %s\n",
+                                       snd_strerror (err));
+                       return;
+               }
+               if ((err = snd_ctl_card_info (_ctrl_handle, card)) < 0)
+               {
+                       if (_debug & DEBUG_INIT) fprintf  (stderr, "Alsa_pcmi: ctl_card_info(): %s\n",
+                                       snd_strerror (err));
+                       return;
+               }
+       }
+
+       _state = -2;
+       if (_play_handle)
+       {
+               if (snd_pcm_hw_params_malloc (&_play_hwpar) < 0)
+               {
+                       if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't allocate playback hw params\n");
+                       return;
+               }
+               if (snd_pcm_sw_params_malloc (&_play_swpar) < 0)
+               {
+                       if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't allocate playback sw params\n");
+                       return;
+               }
+               if (set_hwpar (_play_handle, _play_hwpar, "playback", &_play_nchan) < 0) return;
+               if (set_swpar (_play_handle, _play_swpar, "playback") < 0) return;
+       }
+
+       if (_capt_handle)
+       {
+               if (snd_pcm_hw_params_malloc (&_capt_hwpar) < 0)
+               {
+                       if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't allocate capture hw params\n");
+                       return;
+               }
+               if (snd_pcm_sw_params_malloc (&_capt_swpar) < 0)
+               {
+                       if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't allocate capture sw params\n");
+                       return;
+               }
+               if (set_hwpar (_capt_handle, _capt_hwpar, "capture", &_capt_nchan) < 0) return;
+               if (set_swpar (_capt_handle, _capt_swpar, "capture") < 0) return;
+       }
+
+       if (_play_handle)
+       {
+               if (snd_pcm_hw_params_get_rate (_play_hwpar, &fsamp, &dir) || (fsamp != _fsamp) || dir)
+               {
+                       if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't get requested sample rate for playback.\n");
+                       _state = -3;
+                       return;
+               }
+               if (snd_pcm_hw_params_get_period_size (_play_hwpar, &fsize, &dir) || (fsize != _fsize) || dir)
+               {
+                       if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't get requested period size for playback.\n");
+                       _state = -4;
+                       return;
+               }
+               if (snd_pcm_hw_params_get_periods (_play_hwpar, &nfrag, &dir) || (nfrag != _nfrag) || dir)
+               {
+                       if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't get requested number of periods for playback.\n");
+                       _state = -5;
+                       return;
+               }
+
+               snd_pcm_hw_params_get_format (_play_hwpar, &_play_format);
+               snd_pcm_hw_params_get_access (_play_hwpar, &_play_access);
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+               switch (_play_format)
+               {
+                       case SND_PCM_FORMAT_FLOAT_LE:
+                               _clear_func = &Alsa_pcmi::clear_32;
+                               _play_func  = &Alsa_pcmi::play_float;
+                               break;
+
+                       case SND_PCM_FORMAT_S32_LE:
+                               _clear_func = &Alsa_pcmi::clear_32;
+                               _play_func  = &Alsa_pcmi::play_32;
+                               break;
+
+                       case SND_PCM_FORMAT_S32_BE:
+                               _clear_func = &Alsa_pcmi::clear_32;
+                               _play_func  = &Alsa_pcmi::play_32swap;
+                               break;
+
+                       case SND_PCM_FORMAT_S24_3LE:
+                               _clear_func = &Alsa_pcmi::clear_24;
+                               _play_func  = &Alsa_pcmi::play_24;
+                               break;
+
+                       case SND_PCM_FORMAT_S24_3BE:
+                               _clear_func = &Alsa_pcmi::clear_24;
+                               _play_func  = &Alsa_pcmi::play_24swap;
+                               break;
+
+                       case SND_PCM_FORMAT_S16_LE:
+                               _clear_func = &Alsa_pcmi::clear_16;
+                               _play_func  = &Alsa_pcmi::play_16;
+                               break;
+
+                       case SND_PCM_FORMAT_S16_BE:
+                               _clear_func = &Alsa_pcmi::clear_16;
+                               _play_func  = &Alsa_pcmi::play_16swap;
+                               break;
+
+                       default:
+                               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't handle playback sample format.\n");
+                               _state = -6;
+                               return;
+               }
+#elif __BYTE_ORDER == __BIG_ENDIAN
+               switch (_play_format)
+               {
+                       case SND_PCM_FORMAT_S32_LE:
+                               _clear_func = &Alsa_pcmi::clear_32;
+                               _play_func  = &Alsa_pcmi::play_32swap;
+                               break;
+
+                       case SND_PCM_FORMAT_S32_BE:
+                               _clear_func = &Alsa_pcmi::clear_32;
+                               _play_func  = &Alsa_pcmi::play_32;
+                               break;
+
+                       case SND_PCM_FORMAT_S24_3LE:
+                               _clear_func = &Alsa_pcmi::clear_24;
+                               _play_func  = &Alsa_pcmi::play_24swap;
+                               break;
+
+                       case SND_PCM_FORMAT_S24_3BE:
+                               _clear_func = &Alsa_pcmi::clear_24;
+                               _play_func  = &Alsa_pcmi::play_24;
+                               break;
+
+                       case SND_PCM_FORMAT_S16_LE:
+                               _clear_func = &Alsa_pcmi::clear_16;
+                               _play_func  = &Alsa_pcmi::play_16swap;
+                               break;
+
+                       case SND_PCM_FORMAT_S16_BE:
+                               _clear_func = &Alsa_pcmi::clear_16;
+                               _play_func  = &Alsa_pcmi::play_16;
+                               break;
+
+                       default:
+                               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't handle playback sample format.\n");
+                               _state = -6;
+                               return;
+               }
+#else
+#error "System byte order is undefined or not supported"
+#endif
+
+               _play_npfd = snd_pcm_poll_descriptors_count (_play_handle);
+       }
+
+       if (_capt_handle)
+       {
+               if (snd_pcm_hw_params_get_rate (_capt_hwpar, &fsamp, &dir) || (fsamp != _fsamp) || dir)
+               {
+                       if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't get requested sample rate for capture.\n");
+                       _state = -3;
+                       return;
+               }
+               if (snd_pcm_hw_params_get_period_size (_capt_hwpar, &fsize, &dir) || (fsize != _fsize) || dir)
+               {
+                       if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't get requested period size for capture.\n");
+                       _state = -4;
+                       return;
+               }
+               if (snd_pcm_hw_params_get_periods (_capt_hwpar, &nfrag, &dir) || (nfrag != _nfrag) || dir)
+               {
+                       if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't get requested number of periods for capture.\n");
+                       _state = -5;
+                       return;
+               }
+
+               if (_play_handle) _synced = ! snd_pcm_link (_play_handle, _capt_handle);
+
+               snd_pcm_hw_params_get_format (_capt_hwpar, &_capt_format);
+               snd_pcm_hw_params_get_access (_capt_hwpar, &_capt_access);
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+               switch (_capt_format)
+               {
+                       case SND_PCM_FORMAT_FLOAT_LE:
+                               _capt_func  = &Alsa_pcmi::capt_float;
+                               break;
+
+                       case SND_PCM_FORMAT_S32_LE:
+                               _capt_func  = &Alsa_pcmi::capt_32;
+                               break;
+
+                       case SND_PCM_FORMAT_S32_BE:
+                               _capt_func  = &Alsa_pcmi::capt_32swap;
+                               break;
+
+                       case SND_PCM_FORMAT_S24_3LE:
+                               _capt_func  = &Alsa_pcmi::capt_24;
+                               break;
+
+                       case SND_PCM_FORMAT_S24_3BE:
+                               _capt_func  = &Alsa_pcmi::capt_24swap;
+                               break;
+
+                       case SND_PCM_FORMAT_S16_LE:
+                               _capt_func  = &Alsa_pcmi::capt_16;
+                               break;
+
+                       case SND_PCM_FORMAT_S16_BE:
+                               _capt_func  = &Alsa_pcmi::capt_16swap;
+                               break;
+
+                       default:
+                               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't handle capture sample format.\n");
+                               _state = -6;
+                               return;
+               }
+#elif __BYTE_ORDER == __BIG_ENDIAN
+               switch (_capt_format)
+               {
+                       case SND_PCM_FORMAT_S32_LE:
+                               _capt_func  = &Alsa_pcmi::capt_32swap;
+                               break;
+
+                       case SND_PCM_FORMAT_S32_BE:
+                               _capt_func  = &Alsa_pcmi::capt_32;
+                               break;
+
+                       case SND_PCM_FORMAT_S24_3LE:
+                               _capt_func  = &Alsa_pcmi::capt_24swap;
+                               break;
+
+                       case SND_PCM_FORMAT_S24_3BE:
+                               _capt_func  = &Alsa_pcmi::capt_24;
+                               break;
+
+                       case SND_PCM_FORMAT_S16_LE:
+                               _capt_func  = &Alsa_pcmi::capt_16swap;
+                               break;
+
+                       case SND_PCM_FORMAT_S16_BE:
+                               _capt_func  = &Alsa_pcmi::capt_16;
+                               break;
+
+                       default:
+                               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't handle capture sample format.\n");
+                               _state = -6;
+                               return;
+               }
+#else
+#error "System byte order is undefined or not supported"
+#endif
+
+               _capt_npfd = snd_pcm_poll_descriptors_count (_capt_handle);
+       }
+
+       if (_play_npfd + _capt_npfd > MAXPFD)
+       {
+               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: interface requires more than %d pollfd\n", MAXPFD);
+               return;
+       }
+
+       _state = 0;
+}
+
+
+int Alsa_pcmi::set_hwpar (snd_pcm_t *handle,  snd_pcm_hw_params_t *hwpar, const char *sname, unsigned int *nchan)
+{
+       bool err;
+
+       if (snd_pcm_hw_params_any (handle, hwpar) < 0)
+       {
+               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: no %s hw configurations available.\n",
+                               sname);
+               return -1;
+       }
+       if (snd_pcm_hw_params_set_periods_integer (handle, hwpar) < 0)
+       {
+               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't set %s period size to integral value.\n",
+                               sname);
+               return -1;
+       }
+       if (   (snd_pcm_hw_params_set_access (handle, hwpar, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) < 0)
+                       && (snd_pcm_hw_params_set_access (handle, hwpar, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0)
+                       && (snd_pcm_hw_params_set_access (handle, hwpar, SND_PCM_ACCESS_MMAP_COMPLEX) < 0))
+       {
+               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: the %s interface doesn't support mmap-based access.\n",
+                               sname);
+               return -1;
+       }
+       if (_debug & FORCE_16B)
+       {
+               err =    (snd_pcm_hw_params_set_format (handle, hwpar, SND_PCM_FORMAT_S16_LE) < 0)
+                       && (snd_pcm_hw_params_set_format (handle, hwpar, SND_PCM_FORMAT_S16_BE) < 0);
+       }
+       else
+       {
+               err =    (snd_pcm_hw_params_set_format (handle, hwpar, SND_PCM_FORMAT_FLOAT_LE) < 0)
+                       && (snd_pcm_hw_params_set_format (handle, hwpar, SND_PCM_FORMAT_S32_LE) < 0)
+                       && (snd_pcm_hw_params_set_format (handle, hwpar, SND_PCM_FORMAT_S32_BE) < 0)
+                       && (snd_pcm_hw_params_set_format (handle, hwpar, SND_PCM_FORMAT_S24_3LE) < 0)
+                       && (snd_pcm_hw_params_set_format (handle, hwpar, SND_PCM_FORMAT_S24_3BE) < 0)
+                       && (snd_pcm_hw_params_set_format (handle, hwpar, SND_PCM_FORMAT_S16_LE) < 0)
+                       && (snd_pcm_hw_params_set_format (handle, hwpar, SND_PCM_FORMAT_S16_BE) < 0);
+       }
+       if (err)
+       {
+               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: no supported sample format on %s interface.\n.",
+                               sname);
+               return -1;
+       }
+       if (snd_pcm_hw_params_set_rate (handle, hwpar, _fsamp, 0) < 0)
+       {
+               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't set %s sample rate to %u.\n",
+                               sname, _fsamp);
+               return -1;
+       }
+       snd_pcm_hw_params_get_channels_max (hwpar, nchan);
+       if (*nchan > 1024)
+       {
+               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: detected more than 1024 %s channnels, reset to 2.\n",
+                               sname);
+               *nchan = 2;
+       }
+       if (_debug & FORCE_2CH)
+       {
+               *nchan = 2;
+       }
+       if (*nchan > MAXCHAN)
+       {
+               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: number of %s channels reduced to %d.\n",
+                               sname, MAXCHAN);
+               *nchan = MAXCHAN;
+       }
+
+       if (snd_pcm_hw_params_set_channels (handle, hwpar, *nchan) < 0)
+       {
+               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't set %s channel count to %u.\n",
+                               sname, *nchan);
+               return -1;
+       }
+       if (snd_pcm_hw_params_set_period_size (handle, hwpar, _fsize, 0) < 0)
+       {
+               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't set %s period size to %lu.\n",
+                               sname, _fsize);
+               return -1;
+       }
+       if (snd_pcm_hw_params_set_periods (handle, hwpar, _nfrag, 0) < 0)
+       {
+               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't set %s periods to %u.\n",
+                               sname, _nfrag);
+               return -1;
+       }
+       if (snd_pcm_hw_params_set_buffer_size (handle, hwpar, _fsize * _nfrag) < 0)
+       {
+               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't set %s buffer length to %lu.\n",
+                               sname, _fsize * _nfrag);
+               return -1;
+       }
+       if (snd_pcm_hw_params (handle, hwpar) < 0)
+       {
+               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't set %s hardware parameters.\n",
+                               sname);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int Alsa_pcmi::set_swpar (snd_pcm_t *handle, snd_pcm_sw_params_t *swpar, const char *sname)
+{
+       int err;
+
+       snd_pcm_sw_params_current (handle, swpar);
+
+       if ((err = snd_pcm_sw_params_set_tstamp_mode (handle, swpar, SND_PCM_TSTAMP_MMAP)) < 0)
+       {
+               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't set %s timestamp mode to %u.\n",
+                               sname, SND_PCM_TSTAMP_MMAP);
+               return -1;
+       }
+       if ((err = snd_pcm_sw_params_set_avail_min (handle, swpar, _fsize)) < 0)
+       {
+               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't set %s avail_min to %lu.\n",
+                               sname, _fsize);
+               return -1;
+       }
+       if ((err = snd_pcm_sw_params (handle, swpar)) < 0)
+       {
+               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't set %s software parameters.\n",
+                               sname);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int Alsa_pcmi::recover (void)
+{
+       int                err;
+       snd_pcm_status_t   *stat;
+
+       snd_pcm_status_alloca (&stat);
+
+       if (_play_handle)
+       {
+               if ((err = snd_pcm_status (_play_handle, stat)) < 0)
+               {
+                       if (_debug & DEBUG_STAT) fprintf (stderr, "Alsa_pcmi: pcm_status(play): %s\n",
+                                       snd_strerror (err));
+               }
+               _play_xrun = xruncheck (stat);
+       }
+       if (_capt_handle)
+       {
+               if ((err = snd_pcm_status (_capt_handle, stat)) < 0)
+               {
+                       if (_debug & DEBUG_STAT) fprintf (stderr, "Alsa_pcmi: pcm_status(capt): %s\n",
+                                       snd_strerror (err));
+               }
+               _capt_xrun = xruncheck (stat);
+       }
+
+       if (pcm_stop ()) return -1;
+       if (_play_handle && ((err = snd_pcm_prepare (_play_handle)) < 0))
+       {
+               if (_debug & DEBUG_STAT) fprintf (stderr, "Alsa_pcmi: pcm_prepare(play): %s\n",
+                               snd_strerror (err));
+               return -1;
+       }
+       if (_capt_handle && !_synced && ((err = snd_pcm_prepare (_capt_handle)) < 0))
+       {
+               if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: pcm_prepare(capt): %s\n",
+                               snd_strerror (err));
+               return -1;
+       }
+       if (pcm_start ()) return -1;
+
+       return 0;
+}
+
+
+float Alsa_pcmi::xruncheck (snd_pcm_status_t *stat)
+{
+       struct timeval   tupd, trig;
+       int              ds, du;
+
+       if (snd_pcm_status_get_state (stat) == SND_PCM_STATE_XRUN)
+       {
+               snd_pcm_status_get_tstamp (stat, &tupd);
+               snd_pcm_status_get_trigger_tstamp (stat, &trig);
+               ds = tupd.tv_sec - trig.tv_sec;
+               du = tupd.tv_usec - trig.tv_usec;
+               if (du < 0)
+               {
+                       du += 1000000;
+                       ds -= 1;
+               }
+               return ds + 1e-6f * du;
+       }
+       return 0.0f;
+}
+
+
+char *Alsa_pcmi::clear_16 (char *dst, int nfrm)
+{
+       while (nfrm--)
+       {
+               *((short int *) dst) = 0;
+               dst += _play_step;
+       }
+       return dst;
+}
+
+char *Alsa_pcmi::clear_24 (char *dst, int nfrm)
+{
+       while (nfrm--)
+       {
+               dst [0] = 0;
+               dst [1] = 0;
+               dst [2] = 0;
+               dst += _play_step;
+       }
+       return dst;
+}
+
+char *Alsa_pcmi::clear_32 (char *dst, int nfrm)
+{
+       while (nfrm--)
+       {
+               *((int *) dst) = 0;
+               dst += _play_step;
+       }
+       return dst;
+}
+
+
+char *Alsa_pcmi::play_16 (const float *src, char *dst, int nfrm, int step)
+{
+       float     s;
+       short int d;
+
+       while (nfrm--)
+       {
+               s = *src;
+               if      (s >  1) d = 0x7fff;
+               else if (s < -1) d = 0x8001;
+               else d = (short int)((float) 0x7fff * s);
+               *((short int *) dst) = d;
+               dst += _play_step;
+               src += step;
+       }
+       return dst;
+}
+
+char *Alsa_pcmi::play_16swap (const float *src, char *dst, int nfrm, int step)
+{
+       float     s;
+       short int d;
+
+       while (nfrm--)
+       {
+               s = *src;
+               if      (s >  1) d = 0x7fff;
+               else if (s < -1) d = 0x8001;
+               else d = (short int)((float) 0x7fff * s);
+               dst [0] = d >> 8;
+               dst [1] = d;
+               dst += _play_step;
+               src += step;
+       }
+       return dst;
+}
+
+char *Alsa_pcmi::play_24 (const float *src, char *dst, int nfrm, int step)
+{
+       float   s;
+       int     d;
+
+       while (nfrm--)
+       {
+               s = *src;
+               if      (s >  1) d = 0x007fffff;
+               else if (s < -1) d = 0x00800001;
+               else d = (int)((float) 0x007fffff * s);
+               dst [0] = d;
+               dst [1] = d >> 8;
+               dst [2] = d >> 16;
+               dst += _play_step;
+               src += step;
+       }
+       return dst;
+}
+
+char *Alsa_pcmi::play_24swap (const float *src, char *dst, int nfrm, int step)
+{
+       float   s;
+       int     d;
+
+       while (nfrm--)
+       {
+               s = *src;
+               if      (s >  1) d = 0x007fffff;
+               else if (s < -1) d = 0x00800001;
+               else d = (int)((float) 0x007fffff * s);
+               dst [0] = d >> 16;
+               dst [1] = d >> 8;
+               dst [2] = d;
+               dst += _play_step;
+               src += step;
+       }
+       return dst;
+}
+
+char *Alsa_pcmi::play_32 (const float *src, char *dst, int nfrm, int step)
+{
+       float   s;
+       int     d;
+
+       while (nfrm--)
+       {
+               s = *src;
+               if      (s >  1) d = 0x007fffff;
+               else if (s < -1) d = 0x00800001;
+               else d = (int)((float) 0x007fffff * s);
+               *((int *) dst) = d << 8;
+               dst += _play_step;
+               src += step;
+       }
+       return dst;
+}
+
+char *Alsa_pcmi::play_32swap (const float *src, char *dst, int nfrm, int step)
+{
+       float   s;
+       int     d;
+
+       while (nfrm--)
+       {
+               s = *src;
+               if      (s >  1) d = 0x007fffff;
+               else if (s < -1) d = 0x00800001;
+               else d = (int)((float) 0x007fffff * s);
+               dst [0] = d >> 16;
+               dst [1] = d >> 8;
+               dst [2] = d;
+               dst [3] = 0;
+               dst += _play_step;
+               src += step;
+       }
+       return dst;
+}
+
+char *Alsa_pcmi::play_float (const float *src, char *dst, int nfrm, int step)
+{
+       while (nfrm--)
+       {
+               *((float *) dst) = *src;
+               dst += _play_step;
+               src += step;
+       }
+       return dst;
+}
+
+
+const char *Alsa_pcmi::capt_16 (const char *src, float *dst, int nfrm, int step)
+{
+       while (nfrm--)
+       {
+               const short int s = *((short int const *) src);
+               const float d = (float) s / (float) 0x7fff;
+               *dst = d;
+               dst += step;
+               src += _capt_step;
+       }
+       return src;
+}
+
+const char *Alsa_pcmi::capt_16swap (const char *src, float *dst, int nfrm, int step)
+{
+       float     d;
+       short int s;
+
+       while (nfrm--)
+       {
+               s =  (src [0] & 0xFF) << 8;
+               s += (src [1] & 0xFF);
+               d = (float) s / (float) 0x7fff;
+               *dst = d;
+               dst += step;
+               src += _capt_step;
+       }
+       return src;
+}
+
+const char *Alsa_pcmi::capt_24 (const char *src, float *dst, int nfrm, int step)
+{
+       float   d;
+       int     s;
+
+       while (nfrm--)
+       {
+               s  = (src [0] & 0xFF);
+               s += (src [1] & 0xFF) << 8;
+               s += (src [2] & 0xFF) << 16;
+               if (s & 0x00800000) s-= 0x01000000;
+               d = (float) s / (float) 0x007fffff;
+               *dst = d;
+               dst += step;
+               src += _capt_step;
+       }
+       return src;
+}
+
+const char *Alsa_pcmi::capt_24swap (const char *src, float *dst, int nfrm, int step)
+{
+       float   d;
+       int     s;
+
+       while (nfrm--)
+       {
+               s  = (src [0] & 0xFF) << 16;
+               s += (src [1] & 0xFF) << 8;
+               s += (src [2] & 0xFF);
+               if (s & 0x00800000) s-= 0x01000000;
+               d = (float) s / (float) 0x007fffff;
+               *dst = d;
+               dst += step;
+               src += _capt_step;
+       }
+       return src;
+}
+
+const char *Alsa_pcmi::capt_32 (const char *src, float *dst, int nfrm, int step)
+{
+       while (nfrm--)
+       {
+               const int s = *((int const *) src);
+               const float d = (float) s / (float) 0x7fffff00;
+               *dst = d;
+               dst += step;
+               src += _capt_step;
+       }
+       return src;
+}
+
+const char *Alsa_pcmi::capt_32swap (const char *src, float *dst, int nfrm, int step)
+{
+       float   d;
+       int     s;
+
+       while (nfrm--)
+       {
+               s  = (src [0] & 0xFF) << 24;
+               s += (src [1] & 0xFF) << 16;
+               s += (src [2] & 0xFF) << 8;
+               d = (float) s / (float) 0x7fffff00;
+               *dst = d;
+               dst += step;
+               src += _capt_step;
+       }
+       return src;
+}
+
+const char *Alsa_pcmi::capt_float (const char *src, float *dst, int nfrm, int step)
+{
+       while (nfrm--)
+       {
+               *dst = *((float const *) src);
+               dst += step;
+               src += _capt_step;
+       }
+       return src;
+}
diff --git a/libs/backends/alsa/zita-alsa-pcmi.h b/libs/backends/alsa/zita-alsa-pcmi.h
new file mode 100644 (file)
index 0000000..5f7377d
--- /dev/null
@@ -0,0 +1,188 @@
+// ----------------------------------------------------------------------------
+//
+//  Copyright (C) 2006-2012 Fons Adriaensen <fons@linuxaudio.org>
+//
+//  This program is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation; either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+// ----------------------------------------------------------------------------
+
+
+#ifndef _ZITA_ALSA_PCMI_H_
+#define _ZITA_ALSA_PCMI_H_
+
+
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#define ALSA_PCM_NEW_SW_PARAMS_API
+#include <alsa/asoundlib.h>
+
+
+#define ZITA_ALSA_PCMI_MAJOR_VERSION 0
+#define ZITA_ALSA_PCMI_MINOR_VERSION 2
+
+#include <stdint.h>
+
+extern int zita_alsa_pcmi_major_version (void);
+extern int zita_alsa_pcmi_minor_version (void);
+
+
+class Alsa_pcmi
+{
+public:
+
+       Alsa_pcmi (
+                       const char   *play_name,
+                       const char   *capt_name,
+                       const char   *ctrl_name,
+                       unsigned int  rate,
+                       unsigned int  frsize,
+                       unsigned int  nfrags,
+                       unsigned int  debug = 0);
+
+       ~Alsa_pcmi (void);
+
+       enum
+       {
+               DEBUG_INIT = 1,
+               DEBUG_STAT = 2,
+               DEBUG_WAIT = 4,
+               DEBUG_DATA = 8,
+               DEBUG_ALL  = 15,
+               FORCE_16B  = 256,
+               FORCE_2CH  = 512
+       };
+
+       void printinfo (void);
+
+       int pcm_start (void);
+       int pcm_stop (void);
+       snd_pcm_sframes_t pcm_wait (void);
+       int pcm_idle (int len);
+
+       int play_init (snd_pcm_uframes_t len);
+       void clear_chan (int chan, int len);
+       void play_chan (int chan, const float *src, int len, int step = 1);
+       int play_done (int len);
+
+       int capt_init (snd_pcm_uframes_t len);
+       void capt_chan (int chan, float *dst, int len, int step = 1);
+       int capt_done (int len);
+
+       int play_avail (void)
+       {
+               return snd_pcm_avail (_play_handle);
+       }
+
+       int capt_avail (void)
+       {
+               return snd_pcm_avail (_capt_handle);
+       }
+
+       int play_delay (void)
+       {
+               long k;
+               snd_pcm_delay (_play_handle, &k);
+               return k;
+       }
+
+       int capt_delay (void)
+       {
+               long k;
+               snd_pcm_delay (_capt_handle, &k);
+               return k;
+       }
+
+       float play_xrun (void) const { return _play_xrun; }
+       float capt_xrun (void) const { return _capt_xrun; }
+
+       int      state (void) const { return _state; }
+       size_t   fsize (void) const { return _fsize; }
+       uint32_t fsamp (void) const { return _fsamp; }
+       uint32_t nfrag (void) const { return _nfrag; }
+       uint32_t nplay (void) const { return _play_nchan; }
+       uint32_t ncapt (void) const { return _capt_nchan; }
+       snd_pcm_t *play_handle (void) const { return _play_handle; }
+       snd_pcm_t *capt_handle (void) const { return _capt_handle; }
+
+
+private:
+
+       typedef char *(Alsa_pcmi::*clear_function)(char *, int);
+       typedef char *(Alsa_pcmi::*play_function)(const float *, char *, int, int);
+       typedef const char *(Alsa_pcmi::*capt_function) (const char *, float *, int, int);
+
+       enum { MAXPFD = 16, MAXCHAN = 64 };
+
+       void initialise (const char *play_name, const char *capt_name, const char *ctrl_name);
+       int set_hwpar (snd_pcm_t *handle, snd_pcm_hw_params_t *hwpar, const char *sname, unsigned int *nchan);
+       int set_swpar (snd_pcm_t *handle, snd_pcm_sw_params_t *swpar, const char *sname);
+       int recover (void);
+       float xruncheck (snd_pcm_status_t *stat);
+
+       char *clear_32 (char *dst, int nfrm);
+       char *clear_24 (char *dst, int nfrm);
+       char *clear_16 (char *dst, int nfrm);
+
+       char *play_float (const float *src, char *dst, int nfrm, int step);
+       char *play_32 (const float *src, char *dst, int nfrm, int step);
+       char *play_24 (const float *src, char *dst, int nfrm, int step);
+       char *play_16 (const float *src, char *dst, int nfrm, int step);
+       char *play_32swap (const float *src, char *dst, int nfrm, int step);
+       char *play_24swap (const float *src, char *dst, int nfrm, int step);
+       char *play_16swap (const float *src, char *dst, int nfrm, int step);
+
+       const char *capt_float (const char *src, float *dst, int nfrm, int step);
+       const char *capt_32 (const char *src, float *dst, int nfrm, int step);
+       const char *capt_24 (const char *src, float *dst, int nfrm, int step);
+       const char *capt_16 (const char *src, float *dst, int nfrm, int step);
+       const char *capt_32swap (const char *src, float *dst, int nfrm, int step);
+       const char *capt_24swap (const char *src, float *dst, int nfrm, int step);
+       const char *capt_16swap (const char *src, float *dst, int nfrm, int step);
+
+       unsigned int           _fsamp;
+       snd_pcm_uframes_t      _fsize;
+       unsigned int           _nfrag;
+       unsigned int           _debug;
+       int                    _state;
+       snd_pcm_t             *_play_handle;
+       snd_pcm_t             *_capt_handle;
+       snd_ctl_t             *_ctrl_handle;
+       snd_pcm_hw_params_t   *_play_hwpar;
+       snd_pcm_sw_params_t   *_play_swpar;
+       snd_pcm_hw_params_t   *_capt_hwpar;
+       snd_pcm_sw_params_t   *_capt_swpar;
+       snd_pcm_format_t       _play_format;
+       snd_pcm_format_t       _capt_format;
+       snd_pcm_access_t       _play_access;
+       snd_pcm_access_t       _capt_access;
+       unsigned int           _play_nchan;
+       unsigned int           _capt_nchan;
+       float                  _play_xrun;
+       float                  _capt_xrun;
+       bool                   _synced;
+       int                    _play_npfd;
+       int                    _capt_npfd;
+       struct pollfd          _poll_fd [MAXPFD];
+       snd_pcm_uframes_t      _capt_offs;
+       snd_pcm_uframes_t      _play_offs;
+       int                    _play_step;
+       int                    _capt_step;
+       char                  *_play_ptr [MAXCHAN];
+       const char            *_capt_ptr [MAXCHAN];
+       clear_function         _clear_func;
+       play_function          _play_func;
+       capt_function          _capt_func;
+       void                  *_dummy [16];
+};
+
+#endif
index 7a3aa78513caf726b0f08d80d3e402eb13cd5a61..3a969aa479bc2cbf12512dc3453464b987267da3 100644 (file)
 #include <sys/time.h>
 #include <regex.h>
 
+#include <glibmm.h>
+
 #include "dummy_audiobackend.h"
+
 #include "pbd/error.h"
+#include "ardour/port_manager.h"
 #include "i18n.h"
 
 using namespace ARDOUR;
 
 static std::string s_instance_name;
 size_t DummyAudioBackend::_max_buffer_size = 8192;
+std::vector<std::string> DummyAudioBackend::_midi_options;
+std::vector<AudioBackend::DeviceStatus> DummyAudioBackend::_device_status;
 
 DummyAudioBackend::DummyAudioBackend (AudioEngine& e, AudioBackendInfo& info)
        : AudioBackend (e, info)
@@ -38,15 +44,20 @@ DummyAudioBackend::DummyAudioBackend (AudioEngine& e, AudioBackendInfo& info)
        , _dsp_load (0)
        , _n_inputs (0)
        , _n_outputs (0)
+       , _n_midi_inputs (0)
+       , _n_midi_outputs (0)
        , _systemic_input_latency (0)
        , _systemic_output_latency (0)
        , _processed_samples (0)
+       , _port_change_flag (false)
 {
        _instance_name = s_instance_name;
+       pthread_mutex_init (&_port_callback_mutex, 0);
 }
 
 DummyAudioBackend::~DummyAudioBackend ()
 {
+       pthread_mutex_destroy (&_port_callback_mutex);
 }
 
 /* AUDIOBACKEND API */
@@ -66,9 +77,10 @@ DummyAudioBackend::is_realtime () const
 std::vector<AudioBackend::DeviceStatus>
 DummyAudioBackend::enumerate_devices () const
 {
-       std::vector<AudioBackend::DeviceStatus> s;
-       s.push_back (DeviceStatus (_("Dummy"), true));
-       return s;
+       if (_device_status.empty()) {
+               _device_status.push_back (DeviceStatus (_("Dummy"), true));
+       }
+       return _device_status;
 }
 
 std::vector<float>
@@ -240,25 +252,41 @@ DummyAudioBackend::systemic_output_latency () const
        return _systemic_output_latency;
 }
 
+
 /* MIDI */
 std::vector<std::string>
 DummyAudioBackend::enumerate_midi_options () const
 {
-       std::vector<std::string> m;
-       m.push_back (_("None"));
-       return m;
+       if (_midi_options.empty()) {
+               _midi_options.push_back (_("1 in, 1 out"));
+               _midi_options.push_back (_("2 in, 2 out"));
+               _midi_options.push_back (_("8 in, 8 out"));
+       }
+       return _midi_options;
 }
 
 int
-DummyAudioBackend::set_midi_option (const std::string&)
+DummyAudioBackend::set_midi_option (const std::string& opt)
 {
-       return -1;
+       if (opt == _("1 in, 1 out")) {
+               _n_midi_inputs = _n_midi_outputs = 1;
+       }
+       else if (opt == _("2 in, 2 out")) {
+               _n_midi_inputs = _n_midi_outputs = 2;
+       }
+       else if (opt == _("8 in, 8 out")) {
+               _n_midi_inputs = _n_midi_outputs = 8;
+       }
+       else {
+               _n_midi_inputs = _n_midi_outputs = 0;
+       }
+       return 0;
 }
 
 std::string
 DummyAudioBackend::midi_option () const
 {
-       return "";
+       return ""; // TODO
 }
 
 /* State Control */
@@ -289,6 +317,9 @@ DummyAudioBackend::_start (bool /*for_latency_measurement*/)
                return -1;
        }
 
+       engine.sample_rate_change (_samplerate);
+       engine.buffer_size_change (_samples_per_period);
+
        if (engine.reestablish_ports ()) {
                PBD::error << _("DummyAudioBackend: Could not re-establish ports.") << endmsg;
                stop ();
@@ -296,13 +327,14 @@ DummyAudioBackend::_start (bool /*for_latency_measurement*/)
        }
 
        engine.reconnect_ports ();
+       _port_change_flag = false;
 
        if (pthread_create (&_main_thread, NULL, pthread_process, this)) {
                PBD::error << _("DummyAudioBackend: cannot start.") << endmsg;
        }
 
        int timeout = 5000;
-       while (!_running && --timeout > 0) { usleep (1000); }
+       while (!_running && --timeout > 0) { Glib::usleep (1000); }
 
        if (timeout == 0 || !_running) {
                PBD::error << _("DummyAudioBackend: failed to start process thread.") << endmsg;
@@ -317,7 +349,7 @@ DummyAudioBackend::stop ()
 {
        void *status;
        if (!_running) {
-               return -1;
+               return 0;
        }
 
        _running = false;
@@ -351,7 +383,7 @@ DummyAudioBackend::raw_buffer_size (DataType t)
 {
        switch (t) {
                case DataType::AUDIO:
-                       return _max_buffer_size * sizeof(Sample);
+                       return _samples_per_period * sizeof(Sample);
                case DataType::MIDI:
                        return _max_buffer_size; // XXX not really limited
        }
@@ -401,8 +433,10 @@ DummyAudioBackend::create_process_thread (boost::function<void()> func)
 
        if (pthread_create (&thread_id, &attr, dummy_process_thread, td)) {
                PBD::error << _("AudioEngine: cannot create process thread.") << endmsg;
+               pthread_attr_destroy (&attr);
                return -1;
        }
+       pthread_attr_destroy (&attr);
 
        _threads.push_back (thread_id);
        return 0;
@@ -430,15 +464,9 @@ DummyAudioBackend::in_process_thread ()
 {
        for (std::vector<pthread_t>::const_iterator i = _threads.begin (); i != _threads.end (); ++i)
        {
-#ifdef COMPILER_MINGW
-               if (*i == GetCurrentThread ()) {
-                       return true;
-               }
-#else // pthreads
                if (pthread_equal (*i, pthread_self ()) != 0) {
                        return true;
                }
-#endif
        }
        return false;
 }
@@ -452,6 +480,8 @@ DummyAudioBackend::process_thread_count ()
 void
 DummyAudioBackend::update_latencies ()
 {
+       // trigger latency callback in RT thread (locked graph)
+       port_connect_add_remove_callback();
 }
 
 /* PORTENGINE API */
@@ -571,10 +601,10 @@ DummyAudioBackend::add_port (
        DummyPort* port = NULL;
        switch (type) {
                case DataType::AUDIO:
-                       port = new DummyAudioPort (name, flags);
+                       port = new DummyAudioPort (*this, name, flags);
                        break;
                case DataType::MIDI:
-                       port = new DummyMidiPort (name, flags);
+                       port = new DummyMidiPort (*this, name, flags);
                        break;
                default:
                        PBD::error << _("DummyBackend::register_port: Invalid Data Type.") << endmsg;
@@ -610,8 +640,8 @@ DummyAudioBackend::register_system_ports()
 
        const int a_ins = _n_inputs > 0 ? _n_inputs : 8;
        const int a_out = _n_outputs > 0 ? _n_outputs : 8;
-       const int m_ins = 2; // TODO
-       const int m_out = 2;
+       const int m_ins = _n_midi_inputs > 0 ? _n_midi_inputs : 2;
+       const int m_out = _n_midi_outputs > 0 ? _n_midi_outputs : 2;
 
        /* audio ports */
        lr.min = lr.max = _samples_per_period + _systemic_input_latency;
@@ -629,7 +659,7 @@ DummyAudioBackend::register_system_ports()
                snprintf(tmp, sizeof(tmp), "system:playback_%d", i);
                PortHandle p = add_port(std::string(tmp), DataType::AUDIO, static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal));
                if (!p) return -1;
-               set_latency_range (p, false, lr);
+               set_latency_range (p, true, lr);
        }
 
        /* midi ports */
@@ -648,9 +678,8 @@ DummyAudioBackend::register_system_ports()
                snprintf(tmp, sizeof(tmp), "system:midi_playback_%d", i);
                PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal));
                if (!p) return -1;
-               set_latency_range (p, false, lr);
+               set_latency_range (p, true, lr);
        }
-
        return 0;
 }
 
@@ -828,14 +857,14 @@ DummyAudioBackend::midi_event_put (
 uint32_t
 DummyAudioBackend::get_midi_event_count (void* port_buffer)
 {
-       assert (port_buffer && _running);
+       assert (port_buffer);
        return static_cast<DummyMidiBuffer*>(port_buffer)->size ();
 }
 
 void
 DummyAudioBackend::midi_clear (void* port_buffer)
 {
-       assert (port_buffer && _running);
+       assert (port_buffer);
        DummyMidiBuffer * buf = static_cast<DummyMidiBuffer*>(port_buffer);
        assert (buf);
        buf->clear ();
@@ -908,7 +937,7 @@ DummyAudioBackend::get_physical_outputs (DataType type, std::vector<std::string>
 {
        for (size_t i = 0; i < _ports.size (); ++i) {
                DummyPort* port = _ports[i];
-               if ((port->type () == type) && port->is_output () && port->is_physical ()) {
+               if ((port->type () == type) && port->is_input () && port->is_physical ()) {
                        port_names.push_back (port->name ());
                }
        }
@@ -919,7 +948,7 @@ DummyAudioBackend::get_physical_inputs (DataType type, std::vector<std::string>&
 {
        for (size_t i = 0; i < _ports.size (); ++i) {
                DummyPort* port = _ports[i];
-               if ((port->type () == type) && port->is_input () && port->is_physical ()) {
+               if ((port->type () == type) && port->is_output () && port->is_physical ()) {
                        port_names.push_back (port->name ());
                }
        }
@@ -972,7 +1001,7 @@ DummyAudioBackend::n_physical_inputs () const
 void*
 DummyAudioBackend::get_buffer (PortEngine::PortHandle port, pframes_t nframes)
 {
-       assert (port && _running);
+       assert (port);
        assert (valid_port (port));
        return static_cast<DummyPort*>(port)->get_buffer (nframes);
 }
@@ -985,28 +1014,61 @@ DummyAudioBackend::main_process_thread ()
        _running = true;
        _processed_samples = 0;
 
-       struct timeval clock1, clock2;
-       ::gettimeofday (&clock1, NULL);
+       manager.registration_callback();
+       manager.graph_order_callback();
+
+       uint64_t clock1, clock2;
+       clock1 = g_get_monotonic_time();
        while (_running) {
                if (engine.process_callback (_samples_per_period)) {
                        return 0;
                }
                _processed_samples += _samples_per_period;
                if (!_freewheeling) {
-                       ::gettimeofday (&clock2, NULL);
-                       const int elapsed_time = (clock2.tv_sec - clock1.tv_sec) * 1000000 + (clock2.tv_usec - clock1.tv_usec);
-                       const int nomial_time = 1000000 * _samples_per_period / _samplerate;
+                       clock2 = g_get_monotonic_time();
+                       const int64_t elapsed_time = clock2 - clock1;
+                       const int64_t nomial_time = 1e6 * _samples_per_period / _samplerate;
                        _dsp_load = elapsed_time / (float) nomial_time;
                        if (elapsed_time < nomial_time) {
-                               ::usleep (nomial_time - elapsed_time);
+                               Glib::usleep (nomial_time - elapsed_time);
                        } else {
-                               ::usleep (100); // don't hog cpu
+                               Glib::usleep (100); // don't hog cpu
                        }
                } else {
                        _dsp_load = 1.0;
-                       ::usleep (100); // don't hog cpu
+                       Glib::usleep (100); // don't hog cpu
+               }
+               clock1 = g_get_monotonic_time();
+
+               bool connections_changed = false;
+               bool ports_changed = false;
+               if (!pthread_mutex_trylock (&_port_callback_mutex)) {
+                       if (_port_change_flag) {
+                               ports_changed = true;
+                               _port_change_flag = false;
+                       }
+                       if (!_port_connection_queue.empty ()) {
+                               connections_changed = true;
+                       }
+                       while (!_port_connection_queue.empty ()) {
+                               PortConnectData *c = _port_connection_queue.back ();
+                               manager.connect_callback (c->a, c->b, c->c);
+                               _port_connection_queue.pop_back ();
+                               delete c;
+                       }
+                       pthread_mutex_unlock (&_port_callback_mutex);
+               }
+               if (ports_changed) {
+                       manager.registration_callback();
+               }
+               if (connections_changed) {
+                       manager.graph_order_callback();
+               }
+               if (connections_changed || ports_changed) {
+                       engine.latency_callback(false);
+                       engine.latency_callback(true);
                }
-               ::gettimeofday (&clock1, NULL);
+
        }
        _running = false;
        return 0;
@@ -1066,18 +1128,21 @@ extern "C" ARDOURBACKEND_API ARDOUR::AudioBackendInfo* descriptor ()
 
 
 /******************************************************************************/
-DummyPort::DummyPort (const std::string& name, PortFlags flags)
-       : _name  (name)
+DummyPort::DummyPort (DummyAudioBackend &b, const std::string& name, PortFlags flags)
+       : _dummy_backend (b)
+       , _name  (name)
        , _flags (flags)
 {
        _capture_latency_range.min = 0;
        _capture_latency_range.max = 0;
        _playback_latency_range.min = 0;
        _playback_latency_range.max = 0;
+       _dummy_backend.port_connect_add_remove_callback();
 }
 
 DummyPort::~DummyPort () {
        disconnect_all ();
+       _dummy_backend.port_connect_add_remove_callback();
 }
 
 
@@ -1127,6 +1192,7 @@ void DummyPort::_connect (DummyPort *port, bool callback)
        _connections.push_back (port);
        if (callback) {
                port->_connect (this, false);
+               _dummy_backend.port_connect_callback (name(),  port->name(), true);
        }
 }
 
@@ -1157,6 +1223,7 @@ void DummyPort::_disconnect (DummyPort *port, bool callback)
 
        if (callback) {
                port->_disconnect (this, false);
+               _dummy_backend.port_connect_callback (name(),  port->name(), false);
        }
 }
 
@@ -1165,6 +1232,7 @@ void DummyPort::disconnect_all ()
 {
        while (!_connections.empty ()) {
                _connections.back ()->_disconnect (this, false);
+               _dummy_backend.port_connect_callback (name(),  _connections.back ()->name(), false);
                _connections.pop_back ();
        }
 }
@@ -1187,8 +1255,8 @@ bool DummyPort::is_physically_connected () const
 
 /******************************************************************************/
 
-DummyAudioPort::DummyAudioPort (const std::string& name, PortFlags flags)
-       : DummyPort (name, flags)
+DummyAudioPort::DummyAudioPort (DummyAudioBackend &b, const std::string& name, PortFlags flags)
+       : DummyPort (b, name, flags)
 {
        memset (_buffer, 0, sizeof (_buffer));
 }
@@ -1222,14 +1290,20 @@ void* DummyAudioPort::get_buffer (pframes_t n_samples)
 }
 
 
-DummyMidiPort::DummyMidiPort (const std::string& name, PortFlags flags)
-       : DummyPort (name, flags)
+DummyMidiPort::DummyMidiPort (DummyAudioBackend &b, const std::string& name, PortFlags flags)
+       : DummyPort (b, name, flags)
 {
        _buffer.clear ();
 }
 
 DummyMidiPort::~DummyMidiPort () { }
 
+struct MidiEventSorter {
+       bool operator() (const boost::shared_ptr<DummyMidiEvent>& a, const boost::shared_ptr<DummyMidiEvent>& b) {
+               return *a < *b;
+       }
+};
+
 void* DummyMidiPort::get_buffer (pframes_t /* nframes */)
 {
        if (is_input ()) {
@@ -1242,7 +1316,7 @@ void* DummyMidiPort::get_buffer (pframes_t /* nframes */)
                                _buffer.push_back (boost::shared_ptr<DummyMidiEvent>(new DummyMidiEvent (**it)));
                        }
                }
-               std::sort (_buffer.begin (), _buffer.end ());
+               std::sort (_buffer.begin (), _buffer.end (), MidiEventSorter());
        } else if (is_output () && is_physical () && is_terminal()) {
                _buffer.clear ();
        }
index 7f97dd17f910213d226f383aeec07c7df018060e..70aec34ecd91110dfa37a8b645712275ef39d53f 100644 (file)
@@ -35,6 +35,8 @@
 
 namespace ARDOUR {
 
+class DummyAudioBackend;
+
 class DummyMidiEvent {
        public:
                DummyMidiEvent (const pframes_t timestamp, const uint8_t* data, size_t size);
@@ -55,7 +57,7 @@ typedef std::vector<boost::shared_ptr<DummyMidiEvent> > DummyMidiBuffer;
 
 class DummyPort {
        protected:
-               DummyPort (const std::string&, PortFlags);
+               DummyPort (DummyAudioBackend &b, const std::string&, PortFlags);
        public:
                virtual ~DummyPort ();
 
@@ -100,6 +102,7 @@ class DummyPort {
                }
 
        private:
+               DummyAudioBackend &_dummy_backend;
                std::string _name;
                const PortFlags _flags;
                LatencyRange _capture_latency_range;
@@ -113,7 +116,7 @@ class DummyPort {
 
 class DummyAudioPort : public DummyPort {
        public:
-               DummyAudioPort (const std::string&, PortFlags);
+               DummyAudioPort (DummyAudioBackend &b, const std::string&, PortFlags);
                ~DummyAudioPort ();
 
                DataType type () const { return DataType::AUDIO; };
@@ -128,7 +131,7 @@ class DummyAudioPort : public DummyPort {
 
 class DummyMidiPort : public DummyPort {
        public:
-               DummyMidiPort (const std::string&, PortFlags);
+               DummyMidiPort (DummyAudioBackend &b, const std::string&, PortFlags);
                ~DummyMidiPort ();
 
                DataType type () const { return DataType::MIDI; };
@@ -141,6 +144,7 @@ class DummyMidiPort : public DummyPort {
 }; // class DummyMidiPort
 
 class DummyAudioBackend : public AudioBackend {
+       friend class DummyPort;
        public:
                 DummyAudioBackend (AudioEngine& e, AudioBackendInfo& info);
                ~DummyAudioBackend ();
@@ -167,6 +171,8 @@ class DummyAudioBackend : public AudioBackend {
                int set_output_channels (uint32_t);
                int set_systemic_input_latency (uint32_t);
                int set_systemic_output_latency (uint32_t);
+               int set_systemic_midi_input_latency (std::string const, uint32_t) { return 0; }
+               int set_systemic_midi_output_latency (std::string const, uint32_t) { return 0; }
 
                /* Retrieving parameters */
                std::string  device_name () const;
@@ -177,6 +183,8 @@ class DummyAudioBackend : public AudioBackend {
                uint32_t     output_channels () const;
                uint32_t     systemic_input_latency () const;
                uint32_t     systemic_output_latency () const;
+               uint32_t     systemic_midi_input_latency (std::string const) const { return 0; }
+               uint32_t     systemic_midi_output_latency (std::string const) const { return 0; }
 
                /* External control app */
                std::string control_app_name () const { return std::string (); }
@@ -187,6 +195,19 @@ class DummyAudioBackend : public AudioBackend {
                int set_midi_option (const std::string&);
                std::string midi_option () const;
 
+               std::vector<DeviceStatus> enumerate_midi_devices () const {
+                       return std::vector<AudioBackend::DeviceStatus> ();
+               }
+               int set_midi_device_enabled (std::string const, bool) {
+                       return 0;
+               }
+               bool midi_device_enabled (std::string const) const {
+                       return true;
+               }
+               bool can_set_systemic_midi_latencies () const {
+                       return false;
+               }
+
                /* State Control */
        protected:
                int _start (bool for_latency_measurement);
@@ -271,6 +292,9 @@ class DummyAudioBackend : public AudioBackend {
 
        private:
                std::string _instance_name;
+               static std::vector<std::string> _midi_options;
+               static std::vector<AudioBackend::DeviceStatus> _device_status;
+
                bool  _running;
                bool  _freewheeling;
 
@@ -282,6 +306,9 @@ class DummyAudioBackend : public AudioBackend {
                uint32_t _n_inputs;
                uint32_t _n_outputs;
 
+               uint32_t _n_midi_inputs;
+               uint32_t _n_midi_outputs;
+
                uint32_t _systemic_input_latency;
                uint32_t _systemic_output_latency;
 
@@ -309,9 +336,36 @@ class DummyAudioBackend : public AudioBackend {
 
                std::vector<DummyPort *> _ports;
 
+
+               struct PortConnectData {
+                       std::string a;
+                       std::string b;
+                       bool c;
+
+                       PortConnectData (const std::string& a, const std::string& b, bool c)
+                               : a (a) , b (b) , c (c) {}
+               };
+
+               std::vector<PortConnectData *> _port_connection_queue;
+               pthread_mutex_t _port_callback_mutex;
+               bool _port_change_flag;
+
+               void port_connect_callback (const std::string& a, const std::string& b, bool conn) {
+                       pthread_mutex_lock (&_port_callback_mutex);
+                       _port_connection_queue.push_back(new PortConnectData(a, b, conn));
+                       pthread_mutex_unlock (&_port_callback_mutex);
+               }
+
+               void port_connect_add_remove_callback () {
+                       pthread_mutex_lock (&_port_callback_mutex);
+                       _port_change_flag = true;
+                       pthread_mutex_unlock (&_port_callback_mutex);
+               }
+
                bool valid_port (PortHandle port) const {
                        return std::find (_ports.begin (), _ports.end (), (DummyPort*)port) != _ports.end ();
                }
+
                DummyPort * find_port (const std::string& port_name) const {
                        for (std::vector<DummyPort*>::const_iterator it = _ports.begin (); it != _ports.end (); ++it) {
                                if ((*it)->name () == port_name) {
index 8c8db6a9f4916b087466d946882520efc5660713..9eb41a9ec07a4f1c3f4a7acd47695a705868c411 100644 (file)
@@ -30,7 +30,8 @@ def build(bld):
     obj.name     = 'dummy_audiobackend'
     obj.target   = 'dummy_audiobackend'
     obj.use      = 'libardour libpbd'
-    obj.vnum     = DUMMYBACKEND_VERSION
+    if (bld.env['build_target'] != 'mingw'):
+        obj.vnum     = DUMMYBACKEND_VERSION
     obj.install_path  = os.path.join(bld.env['LIBDIR'], 'backends')
     obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"',
                    'ARDOURBACKEND_DLL_EXPORTS'
index d61d83bc66c491bd17ef23be54ccd4d801d5188c..b60d2418c9868717e45638d245644dbb905c4330 100644 (file)
@@ -554,6 +554,7 @@ JACKAudioBackend::_start (bool for_latency_measurement)
 int
 JACKAudioBackend::stop ()
 {
+       _running = false; // no 'engine halted message'.
        GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
        
        _jack_connection->close ();
index 52edd55c638d199cc6a8415e402497aaca84be8c..000a34816191fc94ceff80e624fa3bd96ada7eea 100644 (file)
@@ -73,6 +73,8 @@ class JACKAudioBackend : public AudioBackend {
     int set_output_channels (uint32_t);
     int set_systemic_input_latency (uint32_t);
     int set_systemic_output_latency (uint32_t);
+    int set_systemic_midi_input_latency (std::string const, uint32_t) { return 0; }
+    int set_systemic_midi_output_latency (std::string const, uint32_t) { return 0; }
 
     std::string  device_name () const;
     float        sample_rate () const;
@@ -82,6 +84,8 @@ class JACKAudioBackend : public AudioBackend {
     uint32_t     output_channels () const;
     uint32_t     systemic_input_latency () const;
     uint32_t     systemic_output_latency () const;
+    uint32_t     systemic_midi_input_latency (std::string const) const { return 0; }
+    uint32_t     systemic_midi_output_latency (std::string const) const { return 0; }
     std::string  driver_name() const;
 
     std::string control_app_name () const;
@@ -149,6 +153,19 @@ class JACKAudioBackend : public AudioBackend {
     int set_midi_option (const std::string&);
     std::string midi_option () const;
 
+    std::vector<DeviceStatus> enumerate_midi_devices () const {
+       return std::vector<AudioBackend::DeviceStatus> ();
+    }
+    int set_midi_device_enabled (std::string const, bool) {
+       return 0;
+    }
+    bool midi_device_enabled (std::string const) const {
+       return true;
+    }
+    bool can_set_systemic_midi_latencies () const {
+       return false;
+    }
+
     int      midi_event_get (pframes_t& timestamp, size_t& size, uint8_t** buf, void* port_buffer, uint32_t event_index);
     int      midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size);
     uint32_t get_midi_event_count (void* port_buffer);
index 01af13fe9ce91c63ab88b8daf441e37044c21b07..8bbfab5cf1fb5c53b6097b215c2a55c103d72be2 100644 (file)
@@ -19,7 +19,7 @@
 */
 
 #ifdef HAVE_ALSA
-#include <alsa/asoundlib.h>
+#include "ardouralsautil/devicelist.h"
 #endif
 
 #ifdef __APPLE__
 #include <mach-o/dyld.h>
 #endif
 
+#ifdef PLATFORM_WINDOWS
+#include <shobjidl.h>  //  Needed for
+#include <shlguid.h>   // 'IShellLink'
+#endif
+
 #ifdef HAVE_PORTAUDIO
 #include <portaudio.h>
 #endif
@@ -105,7 +110,7 @@ get_none_string ()
 void
 ARDOUR::get_jack_audio_driver_names (vector<string>& audio_driver_names)
 {
-#ifdef WIN32
+#ifdef PLATFORM_WINDOWS
        audio_driver_names.push_back (portaudio_driver_name);
 #elif __APPLE__
        audio_driver_names.push_back (coreaudio_driver_name);
@@ -271,60 +276,7 @@ void
 ARDOUR::get_jack_alsa_device_names (device_map_t& devices)
 {
 #ifdef HAVE_ALSA
-       snd_ctl_t *handle;
-       snd_ctl_card_info_t *info;
-       snd_pcm_info_t *pcminfo;
-       snd_ctl_card_info_alloca(&info);
-       snd_pcm_info_alloca(&pcminfo);
-       string devname;
-       int cardnum = -1;
-       int device = -1;
-
-       while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
-
-               devname = "hw:";
-               devname += PBD::to_string (cardnum, std::dec);
-
-               if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
-
-                       if (snd_ctl_card_info (handle, info) < 0) {
-                               continue;
-                       }
-                       
-                       string card_name = snd_ctl_card_info_get_name (info);
-
-                       /* change devname to use ID, not number */
-
-                       devname = "hw:";
-                       devname += snd_ctl_card_info_get_id (info);
-
-                       while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
-                               
-                               /* only detect duplex devices here. more
-                                * complex arrangements are beyond our scope
-                                */
-
-                               snd_pcm_info_set_device (pcminfo, device);
-                               snd_pcm_info_set_subdevice (pcminfo, 0);
-                               snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_CAPTURE);
-                               
-                               if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
-
-                                       snd_pcm_info_set_device (pcminfo, device);
-                                       snd_pcm_info_set_subdevice (pcminfo, 0);
-                                       snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
-
-                                       if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
-                                               devname += ',';
-                                               devname += PBD::to_string (device, std::dec);
-                                               devices.insert (std::make_pair (card_name, devname));
-                                       }
-                               }
-                       }
-
-                       snd_ctl_close(handle);
-               }
-       }
+       get_alsa_audio_device_names(devices);
 #else
        /* silence a compiler unused variable warning */
        (void) devices;
@@ -543,7 +495,7 @@ ARDOUR::get_jack_audio_driver_supports_setting_period_count (const string& drive
 bool
 ARDOUR::get_jack_server_application_names (std::vector<std::string>& server_names)
 {
-#ifdef WIN32
+#ifdef PLATFORM_WINDOWS
        server_names.push_back ("jackd.exe");
 #else
        server_names.push_back ("jackd");
@@ -583,7 +535,61 @@ ARDOUR::get_jack_server_dir_paths (vector<std::string>& server_dir_paths)
 
        Searchpath sp(string(g_getenv("PATH")));
 
-#ifdef WIN32
+#ifdef PLATFORM_WINDOWS
+// N.B. The #define (immediately below) can be safely removed once we know that this code builds okay with mingw
+#ifdef COMPILER_MSVC
+       IShellLinkA  *pISL = NULL;    
+       IPersistFile *ppf  = NULL;
+
+       // Mixbus creates a Windows shortcut giving the location of its
+       // own (bundled) version of Jack. Let's see if that shortcut exists
+       if (SUCCEEDED (CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**)&pISL)))
+       {
+               if (SUCCEEDED (pISL->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf)))
+               {
+                       char  target_path[MAX_PATH];
+                       char  shortcut_pathA[MAX_PATH];
+                       WCHAR shortcut_pathW[MAX_PATH];
+
+                       // Our Windows installer should have created a shortcut to the Jack
+                       // server so let's start by finding out what drive it got installed on
+                       if (char *env_path = getenv ("windir"))
+                       {
+                               strcpy (shortcut_pathA, env_path);
+                               shortcut_pathA[2] = '\0'; // Gives us just the drive letter and colon
+                       }
+                       else // Assume 'C:'
+                               strcpy (shortcut_pathA, "C:");
+
+                       strcat (shortcut_pathA, "\\Program Files (x86)\\Jack\\Start Jack.lnk");
+
+                       MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, shortcut_pathA, -1, shortcut_pathW, MAX_PATH);
+
+                       // If it did, load the shortcut into our persistent file
+                       if (SUCCEEDED (ppf->Load(shortcut_pathW, 0)))
+                       {
+                               // Read the target information from the shortcut object
+                               if (S_OK == (pISL->GetPath (target_path, MAX_PATH, NULL, SLGP_UNCPRIORITY)))
+                               {
+                                       char *p = strrchr (target_path, '\\');
+
+                                       if (p)
+                                       {
+                                               *p = NULL;
+                                               sp.push_back (target_path);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       if (ppf)
+               ppf->Release();
+
+       if (pISL)
+               pISL->Release();
+#endif
+
        gchar *install_dir = g_win32_get_package_installation_directory_of_module (NULL);
        if (install_dir) {
                sp.push_back (install_dir);
@@ -610,8 +616,7 @@ ARDOUR::get_jack_server_paths (const vector<std::string>& server_dir_paths,
                vector<std::string>& server_paths)
 {
        for (vector<string>::const_iterator i = server_names.begin(); i != server_names.end(); ++i) {
-                Glib::PatternSpec ps (*i);
-               find_matching_files_in_directories (server_dir_paths, ps, server_paths);
+               find_files_matching_pattern (server_paths, server_dir_paths, *i);
        }
        return !server_paths.empty();
 }
@@ -692,7 +697,7 @@ ARDOUR::get_jack_command_line_string (JackCommandLineOptions& options, string& c
 
        args.push_back (options.server_path);
 
-#ifdef WIN32
+#ifdef PLATFORM_WINDOWS
        // must use sync mode on windows
        args.push_back ("-S");
 
@@ -738,11 +743,9 @@ ARDOUR::get_jack_command_line_string (JackCommandLineOptions& options, string& c
                args.push_back ("-v");
        }
 
-#ifndef WIN32
        if (options.temporary) {
                args.push_back ("-T");
        }
-#endif
 
        if (options.driver == alsa_driver_name) {
                if (options.midi_driver == alsa_seq_midi_driver_name) {
index 1f54e7a5e704db701ddebba1913363197ac13f39..b8138d2473aa1fc6cff4a9242dee3d489b2e40c8 100644 (file)
@@ -49,8 +49,8 @@ def build(bld):
         obj.uselib   = [ 'JACK', 'PORTAUDIO' ]
     else:
         obj.uselib   = [ 'JACK' ]
-    obj.use      = 'libardour libpbd'
-    obj.vnum     = JACKBACKEND_VERSION
+        obj.vnum     = JACKBACKEND_VERSION
+    obj.use      = 'libardour libpbd ardouralsautil'
     obj.install_path  = os.path.join(bld.env['LIBDIR'], 'backends')
     obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"', 
                    'ARDOURBACKEND_DLL_EXPORTS'
index 115b00101433dba2540a0e888108a2d9ef4db545..6c027824c476f7192f5339bb523e82bc4f50b151 100644 (file)
@@ -121,6 +121,10 @@ class WavesMidiPort;
 
     virtual int set_systemic_output_latency (uint32_t);
 
+    int set_systemic_midi_input_latency (std::string const, uint32_t) { return 0; }
+
+    int set_systemic_midi_output_latency (std::string const, uint32_t) { return 0; }
+
     virtual std::string device_name () const;
     
     virtual float sample_rate () const;
@@ -139,6 +143,10 @@ class WavesMidiPort;
     
     virtual uint32_t systemic_output_latency () const;
 
+    uint32_t systemic_midi_input_latency (std::string const) const { return 0; }
+
+    uint32_t systemic_midi_output_latency (std::string const) const { return 0; }
+
     virtual std::string control_app_name () const;
 
     virtual void launch_control_app ();
@@ -149,6 +157,19 @@ class WavesMidiPort;
 
     virtual std::string midi_option () const;
 
+    std::vector<DeviceStatus> enumerate_midi_devices () const {
+       return std::vector<AudioBackend::DeviceStatus> ();
+    }
+    int set_midi_device_enabled (std::string const, bool) {
+       return 0;
+    }
+    bool midi_device_enabled (std::string const) const {
+       return true;
+    }
+    bool can_set_systemic_midi_latencies () const {
+       return false;
+    }
+
     virtual int _start (bool for_latency_measurement);
 
     virtual int stop ();
index 10da07fef19900af8f5c31a93297b559e23734f1..8e8ee466dc34ced1e2cf586d3b704d860f3c51c4 100644 (file)
@@ -2658,7 +2658,7 @@ WTErr WCMRCoreAudioDeviceManager::generateDeviceListImpl()
                 pDevInfo->m_AvailableSampleRates = availableSampleRates;
                 
                 //Get max input channels
-                uint32 maxInputChannels;
+                uint32_t maxInputChannels;
                 wErr = getDeviceMaxInputChannels(pDevInfo->m_DeviceId, maxInputChannels);
                 
                 if (wErr != eNoErr)
@@ -2671,7 +2671,7 @@ WTErr WCMRCoreAudioDeviceManager::generateDeviceListImpl()
                 pDevInfo->m_MaxInputChannels = maxInputChannels;
                 
                 //Get max output channels
-                uint32 maxOutputChannels;
+                uint32_t maxOutputChannels;
                 wErr = getDeviceMaxOutputChannels(pDevInfo->m_DeviceId, maxOutputChannels);
                 
                 if (wErr != eNoErr)
index c98aa571da5f1d617cfb2b828ed2e58cdd76e740..5e3d2b4da8be1e5bcf09e9b1742310bbfa76f010 100644 (file)
 namespace wvNS { 
 UMicroseconds& UMicroseconds::ReadTime()
 {
+       // Note: g_get_monotonic_time() may be a viable alternative
+       // (it is on Linux and OSX); if not, this code should really go into libpbd
 #ifdef PLATFORM_WINDOWS
        LARGE_INTEGER Frequency, Count ;
 
        QueryPerformanceFrequency(&Frequency) ;
        QueryPerformanceCounter(&Count);
        theTime = uint64_t((Count.QuadPart * 1000000.0 / Frequency.QuadPart));
-#endif
 
-#if defined(__linux__) || defined(__APPLE__)
-//     Mac code replaced by posix calls, to reduce Carbon dependency. 
-       timeval buf;
+#elif defined __MACH__ // OSX, BSD..
+
+       clock_serv_t cclock;
+       mach_timespec_t mts;
+       host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
+       clock_get_time(cclock, &mts);
+       mach_port_deallocate(mach_task_self(), cclock);
+       theTime = (uint64_t)mts.tv_sec * 1e6 + (uint64_t)mts.tv_nsec / 1000;
+
+#else // Linux, POSIX
 
-       gettimeofday(&buf,NULL);
+       struct timespec *ts
+       clock_gettime(CLOCK_MONOTONIC, ts);
+       theTime = (uint64_t)ts.tv_sec * 1e6 + (uint64_t)buf.tv_nsec / 1000;
 
-       // micro sec
-       theTime = uint64_t(buf.tv_sec) * 1000*1000 + buf.tv_usec;
 #endif
 
        return *this;
index 78bbb87fccb260d8a2fac0bf9617d4244101ae24..6905f07bc7e4642d7b5311c1685a37ddee220d36 100755 (executable)
@@ -23,17 +23,20 @@ def configure(conf):
         autowaf.check_pkg(conf, 'portaudio-2.0', uselib_store='PORTAUDIO',
                           atleast_version='19')
     autowaf.configure(conf)
-    
+
 def build(bld):
-    
+
     if bld.env['build_target'] == 'mingw':
-       obj = bld(features = 'cxx cxxshlib')
+        obj = bld(features = 'cxx cxxshlib')
     else:
-       obj = bld(features = 'c cxx cxxshlib')
+        obj = bld(features = 'c cxx cxxshlib')
+
+    if sys.platform == 'darwin':
+        if bld.env['build_target'] not in [ 'lion' ]:
+            obj.framework = 'CoreMidi'
+        else:
+            obj.framework = 'CoreMIDI'
 
-    if bld.env['build_target'] == 'mountain_lion':
-         obj.framework = 'CoreMidi'
-       
     obj.source = [
             'waves_audiobackend.cc',
             'waves_audiobackend.latency.cc',
@@ -50,56 +53,56 @@ def build(bld):
             'wavesapi/devicemanager/WCMRAudioDeviceManager.cpp',
             'wavesapi/devicemanager/WCMRNativeAudio.cpp',
             'wavesapi/threads/WCThreadSafe.cpp',
-           'portmidi/src/pm_common/pmutil.c',
-           'portmidi/src/pm_common/portmidi.c'
+            'portmidi/src/pm_common/pmutil.c',
+            'portmidi/src/pm_common/portmidi.c'
             ]
-    
+
     if bld.env['build_target'] == 'mingw':
-       platform_dependent = [
-                       'wavesapi/miscutils/UMicroseconds.cpp',
-                       'wavesapi/devicemanager/WCMRPortAudioDeviceManager.cpp',
-                       'portmidi/src/pm_win/pmwin.c',
-                       'portmidi/src/pm_win/pmwinmm.c',
-                       'portmidi/src/porttime/ptwinmm.c'
-                       ]
+        platform_dependent = [
+                        'wavesapi/miscutils/UMicroseconds.cpp',
+                        'wavesapi/devicemanager/WCMRPortAudioDeviceManager.cpp',
+                        'portmidi/src/pm_win/pmwin.c',
+                        'portmidi/src/pm_win/pmwinmm.c',
+                        'portmidi/src/porttime/ptwinmm.c'
+                        ]
     else:
-       platform_dependent = [
-           'wavesapi/devicemanager/WCMRCoreAudioDeviceManager.cpp',
-           'portmidi/src/pm_mac/pmmac.c',
-           'portmidi/src/pm_mac/pmmacosxcm.c',
-           'portmidi/src/pm_mac/finddefault.c',
-           'portmidi/src/pm_mac/readbinaryplist.c',
-           'portmidi/src/porttime/ptmacosx_mach.c'
-           ]
-    
+            platform_dependent = [
+                'wavesapi/devicemanager/WCMRCoreAudioDeviceManager.cpp',
+                'portmidi/src/pm_mac/pmmac.c',
+                'portmidi/src/pm_mac/pmmacosxcm.c',
+                'portmidi/src/pm_mac/finddefault.c',
+                'portmidi/src/pm_mac/readbinaryplist.c',
+                'portmidi/src/porttime/ptmacosx_mach.c'
+            ]
+
     obj.source.extend(platform_dependent)
-    
+
     obj.includes = ['.',
-          'wavesapi',
-          'wavesapi/refmanager',
-          'wavesapi/wavespublicapi',
-          'wavesapi/devicemanager',
-          'wavesapi/miscutils',
-          'wavesapi/threads',
-          'portmidi',
-          'portmidi/src/pm_common'
-           ]
-    
+           'wavesapi',
+           'wavesapi/refmanager',
+           'wavesapi/wavespublicapi',
+           'wavesapi/devicemanager',
+           'wavesapi/miscutils',
+           'wavesapi/threads',
+           'portmidi',
+           'portmidi/src/pm_common'
+            ]
+
     obj.cxxflags = [ '-fPIC' ]
     obj.cflags = [ '-fPIC', '-fms-extensions' ]
     obj.name     = 'waves_audiobackend'
     obj.target   = 'waves_audiobackend'
     obj.use      = 'libardour libpbd'
     if bld.env['build_target'] == 'mingw':
-           obj.uselib   = ['PORTAUDIO']
+            obj.uselib   = ['PORTAUDIO']
     obj.vnum     = WAVESAUDIOBACKEND_VERSION
     obj.install_path  = os.path.join(bld.env['LIBDIR'], 'backends')
-    
+
     if bld.env['build_target']== 'mingw':
-       obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"',
-               'ARDOURBACKEND_DLL_EXPORTS'
-               ]
+        obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"',
+                'ARDOURBACKEND_DLL_EXPORTS'
+                ]
     else:
-       obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"',
-               'ARDOURBACKEND_DLL_EXPORTS'
-               ]
+        obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"',
+                'ARDOURBACKEND_DLL_EXPORTS'
+                ]
index 36ef5c1ecd9c3d036c6af406f73e17a1ab65562e..7f3dcaef289e5e5f77b6385d65b1a4bf681d357b 100644 (file)
@@ -1,17 +1,14 @@
 #!/usr/bin/env python
 from waflib.extras import autowaf as autowaf
+from waflib import Options
 import os
 import sys
+import re
 
 # Mandatory variables
 top = '.'
 out = 'build'
 
-backends = [ 'jack', 'dummy' ]
-
-if sys.platform == 'darwin':
-    backends += ['wavesaudio' ]
-
 def options(opt):
     autowaf.set_options(opt)
 
@@ -23,9 +20,33 @@ def configure(conf):
     autowaf.set_recursive()
     autowaf.configure(conf)
 
+    backends = [ 'jack' ]
+
+    if sys.platform == 'darwin' or sys.platform == 'mingw' or sys.platform == 'msvc':
+        backends += [ 'wavesaudio' ]
+
+    if Options.options.build_dummy: 
+        backends += [ 'dummy' ]
+
+    if Options.options.build_alsabackend:
+        if re.search ("linux", sys.platform) != None:
+            backends += [ 'alsa' ]
+
     for i in backends:
         sub_config_and_use(conf, i)
 
 def build(bld):
+    backends = [ 'jack' ]
+
+    if sys.platform == 'darwin' or sys.platform == 'mingw' or sys.platform == 'msvc':
+        backends += [ 'wavesaudio' ]
+
+    if bld.env['BUILD_DUMMYBACKEND']:
+        backends += [ 'dummy' ]
+
+    if re.search ("linux", sys.platform) != None:
+        if bld.env['BUILD_ALSABACKEND'] and bld.is_defined('HAVE_ALSA'):
+            backends += [ 'alsa' ]
+
     for i in backends:
         bld.recurse(i)
index 75aa1f3f3e0ee0a73d9e242329780397891cce76..cc9a1a72013826b8e2f3aead763f44d6f25ddf29 100644 (file)
                                >
                        </File>
                        <File
-                               RelativePath="..\curve.cc"
+                               RelativePath="..\container.cc"
                                >
                        </File>
                        <File
-                               RelativePath="..\debug.cc"
+                               RelativePath="..\curve.cc"
                                >
                        </File>
                        <File
-                               RelativePath="..\drag_handle.cc"
+                               RelativePath="..\debug.cc"
                                >
                        </File>
                        <File
                                RelativePath="..\flag.cc"
                                >
                        </File>
-                       <File
-                               RelativePath="..\group.cc"
-                               >
-                       </File>
                        <File
                                RelativePath="..\image.cc"
                                >
                                RelativePath="..\root_group.cc"
                                >
                        </File>
+                       <File
+                               RelativePath="..\ruler.cc"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\scroll_group.cc"
+                               >
+                       </File>
                        <File
                                RelativePath="..\stateful_image.cc"
                                >
                                RelativePath="..\text.cc"
                                >
                        </File>
+                       <File
+                               RelativePath="..\tracking_text.cc"
+                               >
+                       </File>
                        <File
                                RelativePath="..\types.cc"
                                >
                                RelativePath="..\wave_view.cc"
                                >
                        </File>
+                       <File
+                               RelativePath="..\widget.cc"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\xfade_curve.cc"
+                               >
+                       </File>
                </Filter>
                <Filter
                        Name="Header Files"
                                >
                        </File>
                        <File
-                               RelativePath="..\canvas\curve.h"
+                               RelativePath="..\canvas\container.h"
                                >
                        </File>
                        <File
-                               RelativePath="..\canvas\debug.h"
+                               RelativePath="..\canvas\curve.h"
                                >
                        </File>
                        <File
-                               RelativePath="..\canvas\drag_handle.h"
+                               RelativePath="..\canvas\debug.h"
                                >
                        </File>
                        <File
                                >
                        </File>
                        <File
-                               RelativePath="..\canvas\group.h"
+                               RelativePath="..\i18n.h"
                                >
                        </File>
                        <File
-                               RelativePath="..\i18n.h"
+                               RelativePath="..\canvas\image.h"
                                >
                        </File>
                        <File
-                               RelativePath="..\canvas\image.h"
+                               RelativePath="..\canvas\interpolated_curve.h"
                                >
                        </File>
                        <File
                                RelativePath="..\canvas\root_group.h"
                                >
                        </File>
+                       <File
+                               RelativePath="..\canvas\ruler.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\canvas\scroll_group.h"
+                               >
+                       </File>
                        <File
                                RelativePath="..\canvas\stateful_image.h"
                                >
                                RelativePath="..\canvas\text.h"
                                >
                        </File>
+                       <File
+                               RelativePath="..\canvas\tracking_text.h"
+                               >
+                       </File>
                        <File
                                RelativePath="..\canvas\types.h"
                                >
                                RelativePath="..\canvas\wave_view.h"
                                >
                        </File>
+                       <File
+                               RelativePath="..\canvas\widget.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\canvas\xfade_curve.h"
+                               >
+                       </File>
                </Filter>
        </Files>
        <Globals>
index e95fbe0ad599f82b402e5ed1f5cadd15454a12e0..baec2fafb14f46e3758e2039cfb992975ce2c72f 100644 (file)
 using namespace std;
 using namespace ArdourCanvas;
 
-Arc::Arc (Group* parent)
-       : Item (parent)
-       , Outline (parent)
-       , Fill (parent)
+Arc::Arc (Canvas* c)
+       : Item (c)
        , _radius (0.0)
        , _arc_degrees (0.0)
        , _start_degrees (0.0)
 {
+}
 
+Arc::Arc (Item* parent)
+       : Item (parent)
+       , _radius (0.0)
+       , _arc_degrees (0.0)
+       , _start_degrees (0.0)
+{
 }
 
 void
@@ -126,7 +131,7 @@ Arc::set_start (double deg)
 bool
 Arc::covers (Duple const & point) const
 {
-       Duple p = canvas_to_item (point);
+       Duple p = window_to_item (point);
 
        double angle_degs = atan (p.y/p.x) * 2.0 * M_PI;
        double radius = sqrt (p.x * p.x + p.y * p.y);
index 55f5173253b79587122c6110795aa624eccb1620..70dbc0b498d1b4492618e89004d9153da240c566 100644 (file)
  *  @brief Implementation of the Arrow canvas object.
  */
 
+#include "pbd/compose.h"
+
 #include "canvas/arrow.h"
+#include "canvas/debug.h"
 #include "canvas/polygon.h"
 #include "canvas/line.h"
 
@@ -31,24 +34,36 @@ using namespace ArdourCanvas;
 /** Construct an Arrow.
  *  @param parent Parent canvas group.
  */
-Arrow::Arrow (Group* parent)
-       : Group (parent)
+Arrow::Arrow (Canvas* c)
+       : Container (c)
+{
+       setup ();
+}
+
+Arrow::Arrow (Item* parent)
+       : Container (parent)
 {
-       assert (parent);
+       setup ();
+}
 
+void
+Arrow::setup ()
+{
        /* set up default arrow heads at each end */
        for (int i = 0; i < 2; ++i) {
                _heads[i].polygon = new Polygon (this);
-               _heads[i].show = true;
                _heads[i].outward = true;
                _heads[i].width = 4;
                _heads[i].height = 4;
                setup_polygon (i);
+               CANVAS_DEBUG_NAME (_heads[i].polygon, string_compose ("arrow head %1", i));
        }
        
        _line = new Line (this);
+       CANVAS_DEBUG_NAME (_line, "arrow line");
 }
 
+
 /** Set whether to show an arrow head at one end or other
  *  of the line.
  *  @param which 0 or 1 to specify the arrow head to set up.
@@ -61,9 +76,13 @@ Arrow::set_show_head (int which, bool show)
        
        begin_change ();
        
-       _heads[which].show = show;
+       if (!show) {
+               delete _heads[which].polygon;
+               _heads[which].polygon = 0;
+       } else {
+               setup_polygon (which);
+       }
 
-       setup_polygon (which);
        _bounding_box_dirty = true;
        end_change ();
 }
@@ -131,8 +150,12 @@ void
 Arrow::set_outline_width (Distance width)
 {
        _line->set_outline_width (width);
-       _heads[0].polygon->set_outline_width (width);
-       _heads[1].polygon->set_outline_width (width);
+       if (_heads[0].polygon) {
+               _heads[0].polygon->set_outline_width (width);
+       }
+       if (_heads[1].polygon) {
+               _heads[1].polygon->set_outline_width (width);
+       }
 }
 
 /** Set the x position of our line.
@@ -144,7 +167,9 @@ Arrow::set_x (Coord x)
        _line->set_x0 (x);
        _line->set_x1 (x);
        for (int i = 0; i < 2; ++i) {
-               _heads[i].polygon->set_x_position (x - _heads[i].width / 2);
+               if (_heads[i].polygon) {
+                       _heads[i].polygon->set_x_position (x - _heads[i].width / 2);
+               }
        }
                
 }
@@ -156,7 +181,9 @@ void
 Arrow::set_y0 (Coord y0)
 {
        _line->set_y0 (y0);
-       _heads[0].polygon->set_y_position (y0);
+       if (_heads[0].polygon) {
+               _heads[0].polygon->set_y_position (y0);
+       }
 }
 
 /** Set the y position of end 1 of our line.
@@ -166,7 +193,9 @@ void
 Arrow::set_y1 (Coord y1)
 {
        _line->set_y1 (y1);
-       _heads[1].polygon->set_y_position (y1 - _heads[1].height);
+       if (_heads[1].polygon) {
+               _heads[1].polygon->set_y_position (y1 - _heads[1].height);
+       }
 }
 
 /** @return x position of our line in pixels (in our coordinate system) */
@@ -217,8 +246,10 @@ Arrow::set_color (Color color)
 {
        _line->set_outline_color (color);
        for (int i = 0; i < 2; ++i) {
-               _heads[i].polygon->set_outline_color (color);
-               _heads[i].polygon->set_fill_color (color);
+               if (_heads[i].polygon) {
+                       _heads[i].polygon->set_outline_color (color);
+                       _heads[i].polygon->set_fill_color (color);
+               }
        }
 }
 
index d5d85623612c33f8bbe1a54ca28fada03a5c49b2..6b0c4f94c016303a6fe3ebcaf04c242e39649716 100644 (file)
@@ -33,6 +33,7 @@
 #include "canvas/canvas.h"
 #include "canvas/debug.h"
 #include "canvas/line.h"
+#include "canvas/scroll_group.h"
 
 using namespace std;
 using namespace ArdourCanvas;
@@ -40,8 +41,6 @@ using namespace ArdourCanvas;
 /** Construct a new Canvas */
 Canvas::Canvas ()
        : _root (this)
-       , _scroll_offset_x (0)
-       , _scroll_offset_y (0)
 {
        set_epoch ();
 }
@@ -49,12 +48,29 @@ Canvas::Canvas ()
 void
 Canvas::scroll_to (Coord x, Coord y)
 {
-       _scroll_offset_x = x;
-       _scroll_offset_y = y;
+       /* We do things this way because we do not want to recurse through
+          the canvas for every scroll. In the presence of large MIDI
+          tracks this means traversing item lists that include
+          thousands of items (notes).
+
+          This design limits us to moving only those items (groups, typically)
+          that should move in certain ways as we scroll. In other terms, it
+          becomes O(1) rather than O(N).
+       */
+
+       for (list<ScrollGroup*>::iterator i = scrollers.begin(); i != scrollers.end(); ++i) {
+               (*i)->scroll_to (Duple (x, y));
+       }
 
        pick_current_item (0); // no current mouse position 
 }
 
+void
+Canvas::add_scroller (ScrollGroup& i)
+{
+       scrollers.push_back (&i);
+}
+
 void
 Canvas::zoomed ()
 {
@@ -62,7 +78,7 @@ Canvas::zoomed ()
 }
 
 /** Render an area of the canvas.
- *  @param area Area in canvas coordinates.
+ *  @param area Area in window coordinates.
  *  @param context Cairo context to render to.
  */
 void
@@ -94,11 +110,17 @@ Canvas::render (Rect const & area, Cairo::RefPtr<Cairo::Context> const & context
 
                _root.render (*draw, context);
 
-               // This outlines the rect being rendered, after it has been drawn.
-               // context->rectangle (draw->x0, draw->y0, draw->x1 - draw->x0, draw->y1 - draw->y0);
-               // context->set_source_rgba (1.0, 0, 0, 1.0);
-               // context->stroke ();
-
+#ifdef CANVAS_DEBUG
+               if (getenv ("CANVAS_HARLEQUIN_DEBUGGING")) {
+                       // This transparently colors the rect being rendered, after it has been drawn.
+                       double r = (random() % 65536) /65536.0;
+                       double g = (random() % 65536) /65536.0;
+                       double b = (random() % 65536) /65536.0;
+                       context->rectangle (draw->x0, draw->y0, draw->x1 - draw->x0, draw->y1 - draw->y0);
+                       context->set_source_rgba (r, g, b, 0.25);
+                       context->fill ();
+               }
+#endif
        }
 
 }
@@ -149,7 +171,9 @@ Canvas::item_shown_or_hidden (Item* item)
 {
        boost::optional<Rect> bbox = item->bounding_box ();
        if (bbox) {
-               queue_draw_item_area (item, bbox.get ());
+               if (item->item_to_window (*bbox).intersection (visible_area ())) {
+                       queue_draw_item_area (item, bbox.get ());
+               }
        }
 }
 
@@ -162,7 +186,9 @@ Canvas::item_visual_property_changed (Item* item)
 {
        boost::optional<Rect> bbox = item->bounding_box ();
        if (bbox) {
-               queue_draw_item_area (item, bbox.get ());
+               if (item->item_to_window (*bbox).intersection (visible_area ())) {
+                       queue_draw_item_area (item, bbox.get ());
+               }
        }
 }
 
@@ -174,57 +200,98 @@ Canvas::item_visual_property_changed (Item* item)
 void
 Canvas::item_changed (Item* item, boost::optional<Rect> pre_change_bounding_box)
 {
+       
+       Rect window_bbox = visible_area ();
+
        if (pre_change_bounding_box) {
-               /* request a redraw of the item's old bounding box */
-               queue_draw_item_area (item, pre_change_bounding_box.get ());
+
+               if (item->item_to_window (*pre_change_bounding_box).intersection (window_bbox)) {
+                       /* request a redraw of the item's old bounding box */
+                       queue_draw_item_area (item, pre_change_bounding_box.get ());
+               }
        }
 
        boost::optional<Rect> post_change_bounding_box = item->bounding_box ();
        if (post_change_bounding_box) {
-               /* request a redraw of the item's new bounding box */
-               queue_draw_item_area (item, post_change_bounding_box.get ());
+               
+               if (item->item_to_window (*post_change_bounding_box).intersection (window_bbox)) {
+                       /* request a redraw of the item's new bounding box */
+                       queue_draw_item_area (item, post_change_bounding_box.get ());
+               }
        }
 }
 
 Duple
 Canvas::window_to_canvas (Duple const & d) const
 {
-       return d.translate (Duple (_scroll_offset_x, _scroll_offset_y));
-}
+       /* Find the scroll group that covers d (a window coordinate). Scroll groups are only allowed
+        * as children of the root group, so we just scan its first level
+        * children and see what we can find.
+        */
 
-Duple
-Canvas::canvas_to_window (Duple const & d) const
-{
-       Duple wd = d.translate (Duple (-_scroll_offset_x, -_scroll_offset_y));
+       std::list<Item*> const& root_children (_root.items());
+       ScrollGroup* sg = 0;
 
-       /* Note that this intentionally always returns integer coordinates */
+       /* if the coordinates are negative, clamp to zero and find the item
+        * that covers that "edge" position.
+        */
 
-       wd.x = round (wd.x);
-       wd.y = round (wd.y);
+       Duple in_window (d);
 
-       return wd;
-}      
+       if (in_window.x < 0) {
+               in_window.x = 0;
+       }
+       if (in_window.y < 0) {
+               in_window.y = 0;
+       }
 
-Rect
-Canvas::window_to_canvas (Rect const & r) const
-{
-       return r.translate (Duple (_scroll_offset_x, _scroll_offset_y));
+       for (std::list<Item*>::const_iterator i = root_children.begin(); i != root_children.end(); ++i) {
+               if (((sg = dynamic_cast<ScrollGroup*>(*i)) != 0) && sg->covers_window (in_window)) {
+                       break;
+               }
+       }
+
+       if (sg) {
+               return d.translate (sg->scroll_offset());
+       }
+
+       return d;
 }
 
-Rect
-Canvas::canvas_to_window (Rect const & r) const
+Duple
+Canvas::canvas_to_window (Duple const & d, bool rounded) const
 {
-       Rect wr = r.translate (Duple (-_scroll_offset_x, -_scroll_offset_y));
+       /* Find the scroll group that covers d (a canvas coordinate). Scroll groups are only allowed
+        * as children of the root group, so we just scan its first level
+        * children and see what we can find.
+        */
+
+       std::list<Item*> const& root_children (_root.items());
+       ScrollGroup* sg = 0;
+       Duple wd;
 
-       /* Note that this intentionally always returns integer coordinates */
+       for (std::list<Item*>::const_iterator i = root_children.begin(); i != root_children.end(); ++i) {
+               if (((sg = dynamic_cast<ScrollGroup*>(*i)) != 0) && sg->covers_canvas (d)) {
+                       break;
+               }
+       }
+       
 
-       wr.x0 = round (wr.x0);
-       wr.x1 = round (wr.x1);
-       wr.y0 = round (wr.y0);
-       wr.y1 = round (wr.y1);
+       if (sg) {
+               wd = d.translate (-sg->scroll_offset());
+       } else {
+               wd = d;
+       }
 
-       return wr;
-}      
+       /* Note that this intentionally almost always returns integer coordinates */
+
+       if (rounded) {
+               wd.x = round (wd.x);
+               wd.y = round (wd.y);
+       }
+
+       return wd;
+}
 
 /** Called when an item has moved.
  *  @param item Item that has moved.
@@ -261,9 +328,7 @@ Canvas::item_moved (Item* item, boost::optional<Rect> pre_change_parent_bounding
 void
 Canvas::queue_draw_item_area (Item* item, Rect area)
 {
-       ArdourCanvas::Rect canvas_area = item->item_to_canvas (area);
-       // cerr << "CANVAS " << this << " for " << item << ' ' << item->whatami() << ' ' << item->name << " invalidate " << area << " TRANSLATE AS " << canvas_area << " window = " << canvas_to_window (canvas_area) << std::endl;
-       request_redraw (canvas_area);
+       request_redraw (item->item_to_window (area));
 }
 
 /** Construct a GtkCanvas */
@@ -275,7 +340,7 @@ GtkCanvas::GtkCanvas ()
 {
        /* these are the events we want to know about */
        add_events (Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK |
-                   Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK);
+                   Gdk::SCROLL_MASK | Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK);
 }
 
 void
@@ -295,9 +360,13 @@ GtkCanvas::pick_current_item (int state)
                return;
        }
 
-       pick_current_item (window_to_canvas (Duple (x, y)), state);
+       pick_current_item (Duple (x, y), state);
 }
-               
+
+/** Given @param point (a position in window coordinates)
+ *  and mouse state @param state, check to see if _current_item
+ *  (which will be used to deliver events) should change.
+ */
 void
 GtkCanvas::pick_current_item (Duple const & point, int state)
 {
@@ -307,7 +376,7 @@ GtkCanvas::pick_current_item (Duple const & point, int state)
                return;
        }
 
-       /* find the items at the given position */
+       /* find the items at the given window position */
 
        vector<Item const *> items;
        _root.add_items_at_point (point, items);
@@ -318,9 +387,9 @@ GtkCanvas::pick_current_item (Duple const & point, int state)
        if (DEBUG_ENABLED(PBD::DEBUG::CanvasEnterLeave)) {
                for (vector<Item const*>::const_iterator it = items.begin(); it != items.end(); ++it) {
 #ifdef CANVAS_DEBUG
-                       std::cerr << "\tItem " << (*it)->whatami() << '/' << (*it)->name << std::endl;
+                       std::cerr << "\tItem " << (*it)->whatami() << '/' << (*it)->name << " ignore events ? " << (*it)->ignore_events() << " vis ? " << (*it)->visible() << std::endl;
 #else
-                       std::cerr << "\tItem " << (*it)->whatami() << std::endl;
+                       std::cerr << "\tItem " << (*it)->whatami() << '/' << " ignore events ? " << (*it)->ignore_events() << " vis ? " << (*it)->visible() << std::endl;
 #endif
                }
        }
@@ -337,17 +406,18 @@ GtkCanvas::pick_current_item (Duple const & point, int state)
 
        for (i = items.begin(); i != items.end(); ++i) {
 
-               Item const * new_item = *i;
+               Item const * possible_item = *i;
 
-               /* We ignore invisible items, groups and items that ignore events */
+               /* We ignore invisible items, containers and items that ignore events */
 
-               if (!new_item->visible() || new_item->ignore_events() || dynamic_cast<Group const *>(new_item) != 0) {
+               if (!possible_item->visible() || possible_item->ignore_events() || dynamic_cast<ArdourCanvas::Container const *>(possible_item) != 0) {
                        continue;
                }
-               
-               within_items.push_front (new_item);
+               within_items.push_front (possible_item);
        }
 
+       DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("after filtering insensitive + containers, we have  %1 items\n", within_items.size()));
+
        if (within_items.empty()) {
 
                /* no items at point, just send leave event below */
@@ -368,6 +438,9 @@ GtkCanvas::pick_current_item (Duple const & point, int state)
        }
 }
 
+/** Deliver a series of enter & leave events based on the pointer position being at window
+ * coordinate @param point, and pointer @param state (modifier keys, etc)
+ */
 void
 GtkCanvas::deliver_enter_leave (Duple const & point, int state)
 {
@@ -381,8 +454,14 @@ GtkCanvas::deliver_enter_leave (Duple const & point, int state)
        enter_event.mode = GDK_CROSSING_NORMAL;
        enter_event.focus = FALSE;
        enter_event.state = state;
-       enter_event.x = point.x;
-       enter_event.y = point.y;
+
+       /* Events delivered to canvas items are expected to be in canvas
+        * coordinates but @param point is in window coordinates.
+        */
+       
+       Duple c = window_to_canvas (point);
+       enter_event.x = c.x;
+       enter_event.y = c.y;
 
        GdkEventCrossing leave_event = enter_event;
        leave_event.type = GDK_LEAVE_NOTIFY;
@@ -429,7 +508,6 @@ GtkCanvas::deliver_enter_leave (Duple const & point, int state)
                 * heirarchy between current and new_current.
                 */
                
-
                for (i = _current_item->parent(); i && i != _new_current_item; i = i->parent()) {
                        items_to_leave_virtual.push_back (i);
                }
@@ -437,7 +515,6 @@ GtkCanvas::deliver_enter_leave (Duple const & point, int state)
                enter_detail = GDK_NOTIFY_INFERIOR;
                leave_detail = GDK_NOTIFY_ANCESTOR;
 
-
        } else if (_new_current_item->is_descendant_of (*_current_item)) {
                /* move from ancestor to descendant (X: "_new_current_item is
                 * an inferior ("child") of _current_item")
@@ -590,6 +667,11 @@ GtkCanvas::item_going_away (Item* item, boost::optional<Rect> bounding_box)
                _focused_item = 0;
        }
 
+       ScrollGroup* sg = dynamic_cast<ScrollGroup*>(item);
+       if (sg) {
+               scrollers.remove (sg);
+       }
+
        if (_current_item == item) {
                /* no need to send a leave event to this item, since it is going away 
                 */
@@ -623,6 +705,32 @@ GtkCanvas::context ()
        return w->create_cairo_context ();
 }
 
+/** Handler for GDK scroll events.
+ *  @param ev Event.
+ *  @return true if the event was handled.
+ */
+bool
+GtkCanvas::on_scroll_event (GdkEventScroll* ev)
+{
+       /* translate event coordinates from window to canvas */
+
+       GdkEvent copy = *((GdkEvent*)ev);
+       Duple winpos = Duple (ev->x, ev->y);
+       Duple where = window_to_canvas (winpos);
+       
+       pick_current_item (winpos, ev->state);
+
+       copy.button.x = where.x;
+       copy.button.y = where.y;
+       
+       /* Coordinates in the event will be canvas coordinates, correctly adjusted
+          for scroll if this GtkCanvas is in a GtkCanvasViewport.
+       */
+
+       DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas scroll @ %1, %2 => %3\n", ev->x, ev->y, where));
+       return deliver_event (reinterpret_cast<GdkEvent*>(&copy));
+}
+
 /** Handler for GDK button press events.
  *  @param ev Event.
  *  @return true if the event was handled.
@@ -633,7 +741,10 @@ GtkCanvas::on_button_press_event (GdkEventButton* ev)
        /* translate event coordinates from window to canvas */
 
        GdkEvent copy = *((GdkEvent*)ev);
-       Duple where = window_to_canvas (Duple (ev->x, ev->y));
+       Duple winpos = Duple (ev->x, ev->y);
+       Duple where = window_to_canvas (winpos);
+       
+       pick_current_item (winpos, ev->state);
 
        copy.button.x = where.x;
        copy.button.y = where.y;
@@ -642,7 +753,6 @@ GtkCanvas::on_button_press_event (GdkEventButton* ev)
           for scroll if this GtkCanvas is in a GtkCanvasViewport.
        */
 
-       pick_current_item (where, ev->state);
        DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button press @ %1, %2 => %3\n", ev->x, ev->y, where));
        return deliver_event (reinterpret_cast<GdkEvent*>(&copy));
 }
@@ -657,9 +767,10 @@ GtkCanvas::on_button_release_event (GdkEventButton* ev)
        /* translate event coordinates from window to canvas */
 
        GdkEvent copy = *((GdkEvent*)ev);
-       Duple where = window_to_canvas (Duple (ev->x, ev->y));
+       Duple winpos = Duple (ev->x, ev->y);
+       Duple where = window_to_canvas (winpos);
        
-       pick_current_item (where, ev->state);
+       pick_current_item (winpos, ev->state);
 
        copy.button.x = where.x;
        copy.button.y = where.y;
@@ -668,11 +779,32 @@ GtkCanvas::on_button_release_event (GdkEventButton* ev)
           for scroll if this GtkCanvas is in a GtkCanvasViewport.
        */
 
-       pick_current_item (where, ev->state);
        DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button release @ %1, %2 => %3\n", ev->x, ev->y, where));
        return deliver_event (reinterpret_cast<GdkEvent*>(&copy));
 }
 
+bool
+GtkCanvas::get_mouse_position (Duple& winpos) const
+{
+       int x;
+       int y;
+       Gdk::ModifierType mask;
+       Glib::RefPtr<Gdk::Window> self = Glib::RefPtr<Gdk::Window>::cast_const (get_window ());
+
+       if (!self) {
+               std::cerr << " no self window\n";
+               winpos = Duple (0, 0);
+               return false;
+       }
+
+       Glib::RefPtr<Gdk::Window> win = self->get_pointer (x, y, mask);
+
+       winpos.x = x;
+       winpos.y = y;
+
+       return true;
+}
+
 /** Handler for GDK motion events.
  *  @param ev Event.
  *  @return true if the event was handled.
@@ -692,9 +824,11 @@ GtkCanvas::on_motion_notify_event (GdkEventMotion* ev)
        /* Coordinates in "copy" will be canvas coordinates, 
        */
 
-       // DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas motion @ %1, %2\n", ev->x, ev->y));
+       DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas motion @ %1, %2 canvas @ %3, %4\n", ev->x, ev->y, copy.motion.x, copy.motion.y));
+
+       MouseMotion (point); /* EMIT SIGNAL */
 
-       pick_current_item (where, ev->state);
+       pick_current_item (point, ev->state);
 
        /* Now deliver the motion event.  It may seem a little inefficient
           to recompute the items under the event, but the enter notify/leave
@@ -708,8 +842,7 @@ GtkCanvas::on_motion_notify_event (GdkEventMotion* ev)
 bool
 GtkCanvas::on_enter_notify_event (GdkEventCrossing* ev)
 {
-       Duple where = window_to_canvas (Duple (ev->x, ev->y));
-       pick_current_item (where, ev->state);
+       pick_current_item (Duple (ev->x, ev->y), ev->state);
        return true;
 }
 
@@ -717,24 +850,29 @@ bool
 GtkCanvas::on_leave_notify_event (GdkEventCrossing* ev)
 {
        _new_current_item = 0;
-       Duple where = window_to_canvas (Duple (ev->x, ev->y));
-       deliver_enter_leave (where, ev->state);
+       deliver_enter_leave (Duple (ev->x, ev->y), ev->state);
        return true;
 }
 
 /** Called to request a redraw of our canvas.
- *  @param area Area to redraw, in canvas coordinates.
+ *  @param area Area to redraw, in window coordinates.
  */
 void
 GtkCanvas::request_redraw (Rect const & request)
 {
-       boost::optional<Rect> req = request.intersection (visible_area());
+       Rect real_area;
 
-       if (req) {
-               Rect r = req.get();
-               Rect area = canvas_to_window (r);
-               queue_draw_area (area.x0, area.y0, area.width(), area.height());
-       }
+       Coord const w = width ();
+       Coord const h = height ();
+
+       /* clamp area requested to actual visible window */
+
+       real_area.x0 = max (0.0, min (w, request.x0));
+       real_area.x1 = max (0.0, min (w, request.x1));
+       real_area.y0 = max (0.0, min (h, request.y0));
+       real_area.y1 = max (0.0, min (h, request.y1));
+
+       queue_draw_area (real_area.x0, real_area.y0, real_area.width(), real_area.height());
 }
 
 /** Called to request that we try to get a particular size for ourselves.
@@ -795,13 +933,23 @@ GtkCanvas::unfocus (Item* item)
        }
 }
 
-/** @return The visible area of the canvas, in canvas coordinates */
+/** @return The visible area of the canvas, in window coordinates */
 Rect
 GtkCanvas::visible_area () const
 {
-       Distance const xo = _scroll_offset_x;
-       Distance const yo = _scroll_offset_y;
-       return Rect (xo, yo, xo + get_allocation().get_width (), yo + get_allocation().get_height ());
+       return Rect (0, 0, get_allocation().get_width (), get_allocation().get_height ());
+}
+
+Coord
+GtkCanvas::width() const
+{
+       return get_allocation().get_width();
+}
+
+Coord
+GtkCanvas::height() const
+{
+       return get_allocation().get_height();
 }
 
 /** Create a GtkCanvaSViewport.
index 306a1acb7bfd9f18187445a10a98ccaf86aef755..c2b32d9e108fbd11f5919338aa4ae903aec6d2e7 100644 (file)
 
 namespace ArdourCanvas {
 
-class LIBCANVAS_API Arc : virtual public Item, public Outline, public Fill
+class Canvas;
+
+class LIBCANVAS_API Arc : public Item
 {
 public:
-       Arc (Group *);
+       Arc (Canvas*);
+       Arc (Item*);
 
        void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
        void compute_bounding_box () const;
index beaed84c27e48e4d9fde6493ee861087646cc2e3..45c6eb110e30e3a42c4d67f8ffc052f3284f170d 100644 (file)
 #define __CANVAS_ARROW_H__
 
 #include "canvas/visibility.h"
-
-#include "canvas/group.h"
+#include "canvas/container.h"
 
 namespace ArdourCanvas {
 
+class Canvas;
 class Line;
 class Polygon;
 
@@ -45,10 +45,11 @@ class Polygon;
  *  to draw lines at any angle.
  */
        
-class LIBCANVAS_API Arrow : public Group
+class LIBCANVAS_API Arrow : public Container
 {
 public:
-       Arrow (Group *);
+       Arrow (Canvas*);
+       Arrow (Item*);
 
        void set_show_head (int, bool);
        void set_head_outward (int, bool);
@@ -68,11 +69,11 @@ public:
 
 private:
        void setup_polygon (int);
+       void setup ();
 
        /** Representation of a single arrow head */
        struct Head {
                Polygon* polygon; ///< the polygon which represents its shape
-               bool show;        ///< true if this head should be visible
                bool outward;     ///< true if this head points out from the line
                Distance height;  ///< the height of the head
                Distance width;   ///< the maximum width of the head
index 616336409d49d55c27efa40d236b911808bdb54f..b14f20b69c0e13bafab72e0f9c1418beef5ce9dc 100644 (file)
 
 namespace ArdourCanvas
 {
+struct Rect;
 
-class Rect;
-class Group;   
+class Item;
+class ScrollGroup;
 
 /** The base class for our different types of canvas.
  *
@@ -60,7 +61,7 @@ public:
        Canvas ();
        virtual ~Canvas () {}
 
-       /** called to request a redraw of an area of the canvas */
+       /** called to request a redraw of an area of the canvas in WINDOW coordinates */
        virtual void request_redraw (Rect const &) = 0;
        /** called to ask the canvas to request a particular size from its host */
        virtual void request_size (Duple) = 0;
@@ -77,7 +78,7 @@ public:
        void render (Rect const &, Cairo::RefPtr<Cairo::Context> const &) const;
 
        /** @return root group */
-       Group* root () {
+       Item* root () {
                return &_root;
        }
 
@@ -90,9 +91,7 @@ public:
 
         virtual Cairo::RefPtr<Cairo::Context> context () = 0;
 
-        Rect canvas_to_window (Rect const&) const;
-        Rect window_to_canvas (Rect const&) const;
-        Duple canvas_to_window (Duple const&) const;
+        Duple canvas_to_window (Duple const&, bool rounded = true) const;
         Duple window_to_canvas (Duple const&) const;
 
         void canvas_to_window (Coord cx, Coord cy, Coord& wx, Coord& wy) {
@@ -108,7 +107,28 @@ public:
         }
 
         void scroll_to (Coord x, Coord y);
-        virtual Rect visible_area () const = 0;
+       void add_scroller (ScrollGroup& i);
+        
+        virtual Rect  visible_area () const = 0;
+        virtual Coord width () const = 0;
+        virtual Coord height () const = 0;
+
+       /** Store the coordinates of the mouse pointer in window coordinates in
+          @param winpos. Return true if the position was within the window,
+          false otherwise.
+       */
+       virtual bool get_mouse_position (Duple& winpos) const = 0;
+
+       /** Signal to be used by items that need to track the mouse position
+          within the window.
+       */
+       sigc::signal<void,Duple const&> MouseMotion;
+
+       /** Ensures that the position given by @param winpos (in window
+           coordinates) is within the current window area, possibly reduced by
+           @param border.
+       */
+       Duple clamp_to_window (Duple const& winpos, Duple border = Duple());
 
         void zoomed();
     
@@ -119,14 +139,13 @@ public:
 protected:
        void queue_draw_item_area (Item *, Rect);
        
-       /** our root group */
-       RootGroup _root;
-
-        Coord _scroll_offset_x;
-        Coord _scroll_offset_y;
+       /** our root item */
+       Root _root;
 
         virtual void pick_current_item (int state) = 0;
         virtual void pick_current_item (Duple const &, int state) = 0;
+
+       std::list<ScrollGroup*> scrollers;
 };
 
 /** A canvas which renders onto a GTK EventBox */
@@ -145,8 +164,13 @@ public:
        Cairo::RefPtr<Cairo::Context> context ();
 
        Rect visible_area () const;
+       Coord width() const;
+       Coord height() const;
+
+       bool get_mouse_position (Duple& winpos) const;
 
 protected:
+       bool on_scroll_event (GdkEventScroll *);
        bool on_expose_event (GdkEventExpose *);
        bool on_button_press_event (GdkEventButton *);
        bool on_button_release_event (GdkEventButton* event);
index ee17208a1fb464eb07f31ffdeb2290dac11542ac..c84e3aceec6adb91ff6eac0e95655541fc5f92ca 100644 (file)
@@ -27,8 +27,9 @@ namespace ArdourCanvas {
 
 class LIBCANVAS_API Circle : public Arc
 {
-public:
-       Circle (Group *);
+  public:
+       Circle (Canvas*);
+       Circle (Item*);
 };
        
 }
diff --git a/libs/canvas/canvas/container.h b/libs/canvas/canvas/container.h
new file mode 100644 (file)
index 0000000..59d9345
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+    Copyright (C) 2011-2014 Paul Davis
+    Original Author: Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __CANVAS_CONTAINER_H__
+#define __CANVAS_CONTAINER_H__
+
+#include "canvas/item.h"
+
+namespace ArdourCanvas
+{
+
+/** a Container is an item which has no content of its own
+ * but renders its children in some geometrical arrangement.
+ *
+ * Imagined examples of containers:
+ *
+ *   Container: renders each child at the child's self-determined position
+ *   Box: renders each child along an axis (vertical or horizontal)
+ *   Table/Grid: renders each child within a two-dimensional grid
+ *
+ *   Other?
+ */
+class LIBCANVAS_API Container : public Item
+{
+public:
+       Container (Canvas *);
+       Container (Item *);
+       Container (Item *, Duple const & position);
+
+       /** The compute_bounding_box() method is likely to be identical
+        * in all containers (the union of the children's bounding boxes).
+        * It can be overriden as necessary.
+        */
+       void compute_bounding_box () const;
+
+       /** The render() method is likely to be identical in all containers
+        *  (just call Item::render_children()). It can be overridden as necessary.
+        */ 
+       void render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const;
+};
+
+}
+
+#endif
index d05107640cc2a2efea17d8b11663de62016e665b..d27291e3537732d979dd2ce4da8e6c2e9a68d3f0 100644 (file)
 
 #include "canvas/visibility.h"
 
+#include "canvas/interpolated_curve.h"
 #include "canvas/poly_item.h"
 #include "canvas/fill.h"
 
 namespace ArdourCanvas {
 
-class LIBCANVAS_API Curve : public PolyItem, public Fill
+class XFadeCurve;
+
+class LIBCANVAS_API Curve : public PolyItem, public InterpolatedCurve
 {
-public:
-    Curve (Group *);
+  public:
+    Curve (Canvas*);
+    Curve (Item*);
 
-    enum SplineType {
-           CatmullRomUniform,
-           CatmullRomCentripetal,
+    enum CurveFill {
+           None,
+           Inside,
+           Outside,
     };
-    
+
     void compute_bounding_box () const;
     void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
     void set (Points const &);
@@ -43,19 +48,18 @@ public:
     void set_points_per_segment (uint32_t n);
 
     bool covers (Duple const &) const;
+    void set_fill_mode (CurveFill cf) { curve_fill = cf; }
 
   private:
     Points samples;
     Points::size_type n_samples;
     uint32_t points_per_segment;
-    SplineType curve_type;
+    InterpolatedCurve::SplineType curve_type;
+    CurveFill curve_fill;
 
-    double map_value (double) const;
     void interpolate ();
-
-    static void interpolate (const Points& coordinates, uint32_t points_per_segment, SplineType, bool closed, Points& results);
 };
-       
+
 }
 
 #endif
diff --git a/libs/canvas/canvas/drag_handle.h b/libs/canvas/canvas/drag_handle.h
deleted file mode 100644 (file)
index 389386f..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
-    Copyright (C) 2011-2014 Paul Davis
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef __canvas_drag_handle_h__
-#define __canvas_drag_handle_h__
-
-#include "canvas/rectangle.h"
-#include "canvas/circle.h"
-
-namespace ArdourCanvas
-{
-
-class LIBCANVAS_API DragHandle : public Rectangle
-{
-  public:
-       DragHandle (Group *, Rect const &, bool left_side);
-       void render (Rect const &, Cairo::RefPtr<Cairo::Context>) const;
-
-  protected:
-       bool _left_side;
-};
-
-}
-
-
-#endif /* __canvas_drag_handle_h__ */
index 56044de4bd29f72d6c24f19636204af0b6ebdf2b..a4a36eb3451349873cd418d5810b9a014a77c65e 100644 (file)
 #include <vector>
 #include <stdint.h>
 
+#include <boost/noncopyable.hpp>
+
 #include "canvas/visibility.h"
-#include "canvas/item.h"
+#include "canvas/types.h"
 
 namespace ArdourCanvas {
 
-class LIBCANVAS_API Fill : virtual public Item
+class Item;
+
+class LIBCANVAS_API Fill : public boost::noncopyable
 {
 public:
-       Fill (Group *);
+       Fill (Item& self);
+       virtual ~Fill() {}
 
        virtual void set_fill_color (Color);
        virtual void set_fill (bool);
@@ -51,7 +56,8 @@ public:
 protected:
        void setup_fill_context (Cairo::RefPtr<Cairo::Context>) const;
         void setup_gradient_context (Cairo::RefPtr<Cairo::Context>, Rect const &, Duple const &) const;
-               
+
+               Item& _self;
        Color _fill_color;
        bool _fill;
        bool _transparent;
index 2a15f83921b667d0e2afe7887ae501b128561a35..3aff9ed2416f8750ac67221b36ae7afbed4e3b40 100644 (file)
@@ -18,8 +18,8 @@
 */
 
 #include "canvas/visibility.h"
-#include "canvas/group.h"
 #include "canvas/types.h"
+#include "canvas/container.h"
 
 namespace ArdourCanvas {
 
@@ -27,10 +27,11 @@ class Text;
 class Line;
 class Rectangle;
 
-class LIBCANVAS_API Flag : public Group
+class LIBCANVAS_API Flag : public Container
 {
 public:
-       Flag (Group *, Distance, Color, Color, Duple);
+       Flag (Canvas *, Distance, Color, Color, Duple);
+       Flag (Item*, Distance, Color, Color, Duple);
 
        void set_text (std::string const &);
        void set_height (Distance);
@@ -38,6 +39,8 @@ public:
         bool covers (Duple const &) const;
        
 private:
+       void setup (Distance height, Duple position);
+
        Color _outline_color;
        Color _fill_color;
        Text* _text;
index 1e812f9144c5ba3c8078eef9528705af9db6fb15..61cab3c9d23e694a7c8646a54fe8550e4a04fe99 100644 (file)
 namespace ArdourCanvas {
        class WaveView;
        class Line;
+        class LineSet;
        class Rectangle;
+        class Ruler;
        class Polygon;
        class PolyLine;
        class GtkCanvas;
        class GtkCanvasViewport;
        class Text;
        class Curve;
+        class ScrollGroup;
 }
 
 #endif /* __canvas_canvas_fwd_h__ */
diff --git a/libs/canvas/canvas/group.h b/libs/canvas/canvas/group.h
deleted file mode 100644 (file)
index 94aabfd..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
-    Copyright (C) 2011-2013 Paul Davis
-    Author: Carl Hetherington <cth@carlh.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef __CANVAS_GROUP_H__
-#define __CANVAS_GROUP_H__
-
-#include <list>
-#include <vector>
-
-#include "canvas/visibility.h"
-#include "canvas/item.h"
-#include "canvas/types.h"
-#include "canvas/lookup_table.h"
-
-namespace ArdourCanvas {
-
-class LIBCANVAS_API Group : public Item
-{
-public:
-       explicit Group (Group *);
-       explicit Group (Group *, Duple);
-       ~Group ();
-
-       void render (Rect const &, Cairo::RefPtr<Cairo::Context>) const;
-       virtual void compute_bounding_box () const;
-
-       void add (Item *);
-       void remove (Item *);
-         void clear (bool with_delete = false);
-       std::list<Item*> const & items () const {
-               return _items;
-       }
-
-       void raise_child_to_top (Item *);
-       void raise_child (Item *, int);
-       void lower_child_to_bottom (Item *);
-       void child_changed ();
-
-       void add_items_at_point (Duple, std::vector<Item const *> &) const;
-
-        void dump (std::ostream&) const;
-
-       static int default_items_per_cell;
-
-protected:
-       
-       explicit Group (Canvas *);
-       
-private:
-       friend class ::OptimizingLookupTableTest;
-       
-       Group (Group const &);
-       void ensure_lut () const;
-       void invalidate_lut () const;
-       void clear_items (bool with_delete);
-
-       /* our items, from lowest to highest in the stack */
-       std::list<Item*> _items;
-
-       mutable LookupTable* _lut;
-};
-
-}
-
-#endif
index d54edd382776f86b16a423b7fd200aa83b1bbc5e..64d70a5751dcac609e96e87042d00a2021b9011c 100644 (file)
@@ -34,7 +34,8 @@ namespace ArdourCanvas {
 class LIBCANVAS_API Image : public Item
 {
 public:
-    Image (Group *, Cairo::Format, int width, int height);
+    Image (Canvas *, Cairo::Format, int width, int height);
+    Image (Item*, Cairo::Format, int width, int height);
     
     struct Data {
        Data (uint8_t *d, int w, int h, int s, Cairo::Format fmt)
diff --git a/libs/canvas/canvas/interpolated_curve.h b/libs/canvas/canvas/interpolated_curve.h
new file mode 100644 (file)
index 0000000..6c5d6b0
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+    Copyright (C) 2013 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __CANVAS_INTERPOLATED_CURVE_H__
+#define __CANVAS_INTERPOLATED_CURVE_H__
+
+#include "canvas/visibility.h"
+#include "canvas/types.h"
+
+namespace ArdourCanvas {
+
+class LIBCANVAS_API InterpolatedCurve
+{
+public:
+    enum SplineType {
+           CatmullRomUniform,
+           CatmullRomCentripetal,
+    };
+
+protected:
+
+       /**
+        * This method will calculate the Catmull-Rom interpolation curve, returning
+        * it as a list of Coord coordinate objects.  This method in particular
+        * adds the first and last control points which are not visible, but required
+        * for calculating the spline.
+        *
+        * @param coordinates The list of original straight line points to calculate
+        * an interpolation from.
+        * @param points_per_segment The integer number of equally spaced points to
+        * return along each curve.  The actual distance between each
+        * point will depend on the spacing between the control points.
+        * @return The list of interpolated coordinates.
+        * @param curve_type Chordal (stiff), Uniform(floppy), or Centripetal(medium)
+        * @throws gov.ca.water.shapelite.analysis.CatmullRomException if
+        * points_per_segment is less than 2.
+        */
+       static void
+               interpolate (const Points& coordinates, uint32_t points_per_segment, SplineType curve_type, bool closed, Points& results)
+       {
+               if (points_per_segment < 2) {
+                       return;
+               }
+
+               // Cannot interpolate curves given only two points.  Two points
+               // is best represented as a simple line segment.
+               if (coordinates.size() < 3) {
+                       results = coordinates;
+                       return;
+               }
+
+               // Copy the incoming coordinates. We need to modify it during interpolation
+               Points vertices = coordinates;
+
+               // Test whether the shape is open or closed by checking to see if
+               // the first point intersects with the last point.  M and Z are ignored.
+               if (closed) {
+                       // Use the second and second from last points as control points.
+                       // get the second point.
+                       Duple p2 = vertices[1];
+                       // get the point before the last point
+                       Duple pn1 = vertices[vertices.size() - 2];
+
+                       // insert the second from the last point as the first point in the list
+                       // because when the shape is closed it keeps wrapping around to
+                       // the second point.
+                       vertices.insert(vertices.begin(), pn1);
+                       // add the second point to the end.
+                       vertices.push_back(p2);
+               } else {
+                       // The shape is open, so use control points that simply extend
+                       // the first and last segments
+
+                       // Get the change in x and y between the first and second coordinates.
+                       double dx = vertices[1].x - vertices[0].x;
+                       double dy = vertices[1].y - vertices[0].y;
+
+                       // Then using the change, extrapolate backwards to find a control point.
+                       double x1 = vertices[0].x - dx;
+                       double y1 = vertices[0].y - dy;
+
+                       // Actaully create the start point from the extrapolated values.
+                       Duple start (x1, y1);
+
+                       // Repeat for the end control point.
+                       int n = vertices.size() - 1;
+                       dx = vertices[n].x - vertices[n - 1].x;
+                       dy = vertices[n].y - vertices[n - 1].y;
+                       double xn = vertices[n].x + dx;
+                       double yn = vertices[n].y + dy;
+                       Duple end (xn, yn);
+
+                       // insert the start control point at the start of the vertices list.
+                       vertices.insert (vertices.begin(), start);
+
+                       // append the end control ponit to the end of the vertices list.
+                       vertices.push_back (end);
+               }
+
+               // When looping, remember that each cycle requires 4 points, starting
+               // with i and ending with i+3.  So we don't loop through all the points.
+
+               for (Points::size_type i = 0; i < vertices.size() - 3; i++) {
+
+                       // Actually calculate the Catmull-Rom curve for one segment.
+                       Points r;
+
+                       _interpolate (vertices, i, points_per_segment, curve_type, r);
+
+                       // Since the middle points are added twice, once for each bordering
+                       // segment, we only add the 0 index result point for the first
+                       // segment.  Otherwise we will have duplicate points.
+
+                       if (results.size() > 0) {
+                               r.erase (r.begin());
+                       }
+
+                       // Add the coordinates for the segment to the result list.
+
+                       results.insert (results.end(), r.begin(), r.end());
+               }
+       }
+
+private:
+       /**
+        * Calculate the same values but introduces the ability to "parameterize" the t
+        * values used in the calculation. This is based on Figure 3 from
+        * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf
+        *
+        * @param p An array of double values of length 4, where interpolation
+        * occurs from p1 to p2.
+        * @param time An array of time measures of length 4, corresponding to each
+        * p value.
+        * @param t the actual interpolation ratio from 0 to 1 representing the
+        * position between p1 and p2 to interpolate the value.
+        */
+       static double
+               __interpolate (double p[4], double time[4], double t)
+       {
+               const double L01 = p[0] * (time[1] - t) / (time[1] - time[0]) + p[1] * (t - time[0]) / (time[1] - time[0]);
+               const double L12 = p[1] * (time[2] - t) / (time[2] - time[1]) + p[2] * (t - time[1]) / (time[2] - time[1]);
+               const double L23 = p[2] * (time[3] - t) / (time[3] - time[2]) + p[3] * (t - time[2]) / (time[3] - time[2]);
+               const double L012 = L01 * (time[2] - t) / (time[2] - time[0]) + L12 * (t - time[0]) / (time[2] - time[0]);
+               const double L123 = L12 * (time[3] - t) / (time[3] - time[1]) + L23 * (t - time[1]) / (time[3] - time[1]);
+               const double C12 = L012 * (time[2] - t) / (time[2] - time[1]) + L123 * (t - time[1]) / (time[2] - time[1]);
+               return C12;
+       }
+
+       /**
+        * Given a list of control points, this will create a list of points_per_segment
+        * points spaced uniformly along the resulting Catmull-Rom curve.
+        *
+        * @param points The list of control points, leading and ending with a
+        * coordinate that is only used for controling the spline and is not visualized.
+        * @param index The index of control point p0, where p0, p1, p2, and p3 are
+        * used in order to create a curve between p1 and p2.
+        * @param points_per_segment The total number of uniformly spaced interpolated
+        * points to calculate for each segment. The larger this number, the
+        * smoother the resulting curve.
+        * @param curve_type Clarifies whether the curve should use uniform, chordal
+        * or centripetal curve types. Uniform can produce loops, chordal can
+        * produce large distortions from the original lines, and centripetal is an
+        * optimal balance without spaces.
+        * @return the list of coordinates that define the CatmullRom curve
+        * between the points defined by index+1 and index+2.
+        */
+       static void
+               _interpolate (const Points& points, Points::size_type index, int points_per_segment, SplineType curve_type, Points& results)
+       {
+               double x[4];
+               double y[4];
+               double time[4];
+
+               for (int i = 0; i < 4; i++) {
+                       x[i] = points[index + i].x;
+                       y[i] = points[index + i].y;
+                       time[i] = i;
+               }
+
+               double tstart = 1;
+               double tend = 2;
+
+               if (curve_type != CatmullRomUniform) {
+                       double total = 0;
+                       for (int i = 1; i < 4; i++) {
+                               double dx = x[i] - x[i - 1];
+                               double dy = y[i] - y[i - 1];
+                               if (curve_type == CatmullRomCentripetal) {
+                                       total += pow (dx * dx + dy * dy, .25);
+                               } else {
+                                       total += pow (dx * dx + dy * dy, .5);
+                               }
+                               time[i] = total;
+                       }
+                       tstart = time[1];
+                       tend = time[2];
+               }
+
+               int segments = points_per_segment - 1;
+               results.push_back (points[index + 1]);
+
+               for (int i = 1; i < segments; i++) {
+                       double xi = __interpolate (x, time, tstart + (i * (tend - tstart)) / segments);
+                       double yi = __interpolate (y, time, tstart + (i * (tend - tstart)) / segments);
+                       results.push_back (Duple (xi, yi));
+               }
+
+               results.push_back (points[index + 2]);
+       }
+};
+
+}
+
+#endif
index da061f5bfc7a7e6b975f28beada5df157a701501..9b058ab83afd1d366d4b809899d04191ac07033b 100644 (file)
 
 #include "canvas/visibility.h"
 #include "canvas/types.h"
+#include "canvas/fill.h"
+#include "canvas/outline.h"
+#include "canvas/lookup_table.h"
 
 namespace ArdourCanvas
 {
+struct Rect;   
 
 class Canvas;
-class Group;
-class Rect;    
+class ScrollGroup;
 
 /** The parent class for anything that goes on the canvas.
  *
@@ -49,12 +52,12 @@ class Rect;
  *  and all except the `root group' have a pointer to their parent group.
  */
        
-class LIBCANVAS_API Item
+class LIBCANVAS_API Item : public Fill, public Outline
 {
 public:
        Item (Canvas *);
-       Item (Group *);
-       Item (Group *, Duple);
+       Item (Item *);
+       Item (Item *, Duple const& p);
        virtual ~Item ();
 
         void redraw () const;
@@ -72,9 +75,16 @@ public:
         */
        virtual void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const = 0;
 
-       virtual void add_items_at_point (Duple, std::vector<Item const *>& items) const {
-               items.push_back (this);
-       }
+       /** Adds one or more items to the vector @param items based on their
+        * covering @param point which is in **window** coordinates
+        *
+        * Note that Item::add_items_at_window_point() is only intended to be 
+        * called on items already looked up in a LookupTable (i.e. by a
+        * parent) and thus known to cover @param point already.
+        *
+        * Derived classes may add more items than themselves (e.g. containers).
+        */
+       virtual void add_items_at_point (Duple /*point*/, std::vector<Item const *>& items) const;
 
         virtual bool covers (Duple const &) const;
 
@@ -85,10 +95,10 @@ public:
        void ungrab ();
        
        void unparent ();
-       void reparent (Group *);
+       void reparent (Item *);
 
        /** @return Parent group, or 0 if this is the root group */
-       Group* parent () const {
+       Item* parent () const {
                return _parent;
        }
     
@@ -117,6 +127,11 @@ public:
                return _position;
        }
 
+       Duple window_origin() const;
+       Duple canvas_origin() const;
+
+       ScrollGroup* scroll_parent() const { return _scroll_parent; }
+
        boost::optional<Rect> bounding_box () const;
         Coord height() const;
         Coord width() const;
@@ -125,21 +140,23 @@ public:
        Rect item_to_parent (Rect const &) const;
        Duple parent_to_item (Duple const &) const;
        Rect parent_to_item (Rect const &) const;
-       /* XXX: it's a pity these aren't the same form as item_to_parent etc.,
+
+       /* XXX: it's a pity these two aren't the same form as item_to_parent etc.,
           but it makes a bit of a mess in the rest of the code if they are not.
        */
-
         void canvas_to_item (Coord &, Coord &) const;
-        Duple canvas_to_item (Duple const &) const;
        void item_to_canvas (Coord &, Coord &) const;
-       Rect item_to_canvas (Rect const &) const;
-       Rect canvas_to_item (Rect const &) const;
-        Duple item_to_canvas (Duple const &) const;
 
-        Duple item_to_window (Duple const&) const;
+        Duple canvas_to_item (Duple const&) const;
+       Rect item_to_canvas (Rect const&) const;
+        Duple item_to_canvas (Duple const&) const;
+       Rect canvas_to_item (Rect const&) const;
+
+        Duple item_to_window (Duple const&, bool rounded = true) const;
         Duple window_to_item (Duple const&) const;
         Rect item_to_window (Rect const&) const;
-
+        Rect window_to_item (Rect const&) const;
+        
        void raise_to_top ();
        void raise (int);
        void lower_to_bottom ();
@@ -166,6 +183,21 @@ public:
 
        void set_data (std::string const &, void *);
        void* get_data (std::string const &) const;
+
+       /* nested item ("grouping") API */
+       void add (Item *);
+       void remove (Item *);
+        void clear (bool with_delete = false);
+       std::list<Item*> const & items () const {
+               return _items;
+       }
+       void raise_child_to_top (Item *);
+       void raise_child (Item *, int);
+       void lower_child_to_bottom (Item *);
+       void child_changed ();
+
+       static int default_items_per_cell;
+
        
        /* This is a sigc++ signal because it is solely
           concerned with GUI stuff and is thus single-threaded
@@ -200,6 +232,8 @@ public:
         std::string whatami() const;
 
 protected:
+       friend class Fill;
+       friend class Outline;
 
         /** To be called at the beginning of any property change that
         *  may alter the bounding box of this item
@@ -220,7 +254,9 @@ protected:
 
        Canvas* _canvas;
        /** parent group; may be 0 if we are the root group or if we have been unparent()ed */
-       Group* _parent;
+       Item* _parent;
+       /** scroll parent group; may be 0 if we are the root group or if we have been unparent()ed */
+       ScrollGroup* _scroll_parent;
        /** position of this item in parent coordinates */
        Duple _position;
        /** true if this item is visible (ie to be drawn), otherwise false */
@@ -236,10 +272,28 @@ protected:
        /* XXX: this is a bit grubby */
        std::map<std::string, void *> _data;
 
+       /* nesting ("grouping") API */
+
+       void invalidate_lut () const;
+       void clear_items (bool with_delete);
+
+       void ensure_lut () const;
+       mutable LookupTable* _lut;
+       /* our items, from lowest to highest in the stack */
+       std::list<Item*> _items;
+
+       void add_child_bounding_boxes() const;
+       void render_children (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const;
+
 private:
        void init ();
 
        bool _ignore_events;
+
+       Duple scroll_offset() const;
+       Duple position_offset() const;
+
+       void find_scroll_parent ();
 };
 
 extern LIBCANVAS_API std::ostream& operator<< (std::ostream&, const ArdourCanvas::Item&);
index 85b1d41e15b5f3e89ba5a9d0c523bad82ded9f68..b178554c849762c04c83635e4c08acde5b7afd5f 100644 (file)
 
 namespace ArdourCanvas {
 
-class LIBCANVAS_API Line : virtual public Item, public Outline
+class LIBCANVAS_API Line : public Item
 {
-public:
-       Line (Group *);
+  public:
+       Line (Canvas*);
+       Line (Item*);
 
        void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
        void compute_bounding_box () const;
index a9b13651d9ed9c736c9bc0fb4151865591e62a13..fad100fdf9dd58521978418e6dbf184be8e90b33 100644 (file)
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
+#ifndef __CANVAS_LINESET_H__
+#define __CANVAS_LINESET_H__
+
+#include <vector>
+
 #include "canvas/visibility.h"
 #include "canvas/item.h"
 
@@ -30,29 +35,34 @@ public:
                Horizontal
        };
 
-       LineSet (Group *);
+       LineSet (Canvas*, Orientation o = Vertical);
+       LineSet (Item*, Orientation o = Vertical);
 
        void compute_bounding_box () const;
        void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
 
         bool covers (Duple const &) const;
 
-       void set_height (Distance);
+       void set_extent (Distance);
+       Distance extent() const { return _extent; }
 
        void add (Coord, Distance, Color);
        void clear ();
 
        struct Line {
-               Line (Coord y_, Distance width_, Color color_) : y (y_), width (width_), color (color_) {}
+               Line (Coord p, Distance width_, Color color_) : pos (p), width (width_), color (color_) {}
                
-               Coord y;
+               Coord pos;
                Distance width;
                Color color;
        };
 
 private:
-       std::list<Line> _lines;
-       Distance _height;
+       std::vector<Line> _lines;
+       Distance          _extent;
+       Orientation       _orientation;
 };
 
 }
+
+#endif /* __CANVAS_LINESET_H__ */
index 5be33c1bca0d9df555acae60ee52ae7bc87fe87d..29452365cdb2db38bd50617042a3949e07cfba55 100644 (file)
@@ -31,12 +31,11 @@ class OptimizingLookupTableTest;
 namespace ArdourCanvas {
 
 class Item;
-class Group;
 
 class LIBCANVAS_API LookupTable
 {
 public:
-    LookupTable (Group const &);
+    LookupTable (Item const &);
     virtual ~LookupTable ();
     
     virtual std::vector<Item*> get (Rect const &) = 0;
@@ -45,13 +44,13 @@ public:
 
 protected:
        
-    Group const & _group;
+    Item const & _item;
 };
 
 class LIBCANVAS_API DumbLookupTable : public LookupTable
 {
 public:
-    DumbLookupTable (Group const &);
+       DumbLookupTable (Item const &);
     
     std::vector<Item*> get (Rect const &);
     std::vector<Item*> items_at_point (Duple const &) const;
@@ -61,7 +60,7 @@ public:
 class LIBCANVAS_API OptimizingLookupTable : public LookupTable
 {
 public:
-    OptimizingLookupTable (Group const &, int);
+    OptimizingLookupTable (Item const &, int);
     ~OptimizingLookupTable ();
     std::vector<Item*> get (Rect const &);
     std::vector<Item*> items_at_point (Duple const &) const;
index 972c07b11c3d8369832b04368d58a210e7c877a6..474501c8c78c1a38711a388abb0069ffa6d96cda 100644 (file)
 
 #include <stdint.h>
 
+#include <boost/noncopyable.hpp>
+
 #include "canvas/visibility.h"
 #include "canvas/types.h"
-#include "canvas/item.h"
 
 namespace ArdourCanvas {
 
-class LIBCANVAS_API Outline : virtual public Item
+class Item;
+
+class LIBCANVAS_API Outline : public boost::noncopyable
 {
 public:
-       Outline (Group *);
-       virtual ~Outline () {}
+       Outline (Item& self);
+       virtual ~Outline() {}
 
        Color outline_color () const {
                return _outline_color;
@@ -55,10 +58,11 @@ public:
 protected:
 
        void setup_outline_context (Cairo::RefPtr<Cairo::Context>) const;
-       
-       Color _outline_color;
+
+       Item&    _self;
+       Color    _outline_color;
        Distance _outline_width;
-       bool _outline;
+       bool     _outline;
 };
 
 }
index 3974b560a9d6508d89a8f9bed69b0c2205739742..2749b966682281298c16e649b4119576efea80b1 100644 (file)
@@ -34,7 +34,8 @@ namespace ArdourCanvas {
 class LIBCANVAS_API Pixbuf : public Item
 {
 public:
-       Pixbuf (Group *);
+       Pixbuf (Canvas*);
+       Pixbuf (Item*);
 
        void render (Rect const &, Cairo::RefPtr<Cairo::Context>) const;
        void compute_bounding_box () const;
index 4745d24ab930894579b4d8ed31fd07038f8c5a3f..732adb14b30a030ebe967380c69d123216cb0241 100644 (file)
 
 namespace ArdourCanvas {
 
-class LIBCANVAS_API PolyItem : virtual public Item, public Outline
+class LIBCANVAS_API PolyItem : public Item
 {
 public:
-       PolyItem (Group *);
+       PolyItem (Canvas*);
+       PolyItem (Item*);
 
        void compute_bounding_box () const;
 
index 4f1b2b01577eb9a31607add69f6fe9a39d45e455..16db9a69e2c2bcf299f49c9a323f20e3cd7879dc 100644 (file)
@@ -29,7 +29,8 @@ namespace ArdourCanvas {
 class LIBCANVAS_API PolyLine : public PolyItem
 {
   public:
-       PolyLine (Group *);
+       PolyLine (Canvas*);
+       PolyLine (Item*);
        
        void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
        
index 917122a9a26c8164d413c7a1e13d74e37abab7e8..9703eb46e9325c3b59236df3a0ed24b8d87c3982 100644 (file)
 
 namespace ArdourCanvas {
 
-class LIBCANVAS_API Polygon : public PolyItem, public Fill
+class LIBCANVAS_API Polygon : public PolyItem
 {
 public:
-       Polygon (Group *);
+       Polygon (Canvas*);
+       Polygon (Item*);
         virtual ~Polygon();
 
        void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
index 91f23f9336178539efbf851967f773e5a60220c2..5c6b66fbb644be0d8ff6a70b77faa6a89afe951e 100644 (file)
 #include "canvas/visibility.h"
 #include "canvas/item.h"
 #include "canvas/types.h"
-#include "canvas/outline.h"
-#include "canvas/fill.h"
 
 namespace ArdourCanvas
 {
 
-class LIBCANVAS_API Rectangle : virtual public Item, public Outline, public Fill
+class LIBCANVAS_API Rectangle : public Item
 {
 public:
-       Rectangle (Group *);
-       Rectangle (Group *, Rect const &);
+       Rectangle (Canvas*);
+       Rectangle (Canvas*, Rect const &);
+       Rectangle (Item*);
+       Rectangle (Item*, Rect const &);
        
        void render (Rect const &, Cairo::RefPtr<Cairo::Context>) const;
        void compute_bounding_box () const;
index 70c3e5b90b4ff4b29a4f7eb4fd3d08b9754ae358..1763fc11df445a3c7d5fe502bd0f85566e81b2ae 100644 (file)
 #define __CANVAS_ROOT_GROUP_H__
 
 #include "canvas/visibility.h"
-#include "canvas/group.h"
+#include "canvas/container.h"
 
 namespace ArdourCanvas {
 
-class LIBCANVAS_API RootGroup : public Group
+class LIBCANVAS_API Root : public Container
 {
 private:
        friend class Canvas;
        
-       RootGroup (Canvas *);
+       Root (Canvas *);
 
        void compute_bounding_box () const;
-       void child_changed ();
 };
 
 }
diff --git a/libs/canvas/canvas/ruler.h b/libs/canvas/canvas/ruler.h
new file mode 100644 (file)
index 0000000..2504024
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+    Copyright (C) 2014 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __CANVAS_RULER_H__
+#define __CANVAS_RULER_H__
+
+#include <string>
+#include <vector>
+
+#include <pangomm/fontdescription.h>
+
+#include "canvas/rectangle.h"
+
+namespace ArdourCanvas
+{
+       
+class LIBCANVAS_API Ruler : public Rectangle
+{
+public:
+       struct Mark {
+               enum Style {
+                       Major,
+                       Minor,
+                       Micro
+               };
+               std::string label;
+               double position;
+               Style  style;
+       };
+       
+       struct Metric {
+               Metric () : units_per_pixel (0) {}
+               virtual ~Metric() {}
+
+               double units_per_pixel;
+
+               /* lower and upper and sample positions, which are also canvas coordinates
+                */
+               
+               virtual void get_marks (std::vector<Mark>&, double lower, double upper, int maxchars) const = 0;
+       };
+       
+       Ruler (Canvas*, const Metric& m);
+       Ruler (Canvas*, const Metric& m, Rect const&);
+       Ruler (Item*, const Metric& m);
+       Ruler (Item*, const Metric& m, Rect const&);
+       
+       void set_range (double lower, double upper);
+       void set_font_description (Pango::FontDescription);
+       
+       void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
+       
+private:
+       const Metric& _metric;
+
+       /* lower and upper and sample positions, which are also canvas coordinates
+        */
+
+       Coord         _lower;
+       Coord         _upper;
+
+       Pango::FontDescription* _font_description;
+       mutable std::vector<Mark> marks;
+       mutable bool _need_marks;
+};
+
+}
+
+
+#endif
diff --git a/libs/canvas/canvas/scroll_group.h b/libs/canvas/canvas/scroll_group.h
new file mode 100644 (file)
index 0000000..463abc0
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+    Copyright (C) 2014 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __CANVAS_SCROLL_GROUP_H__
+#define __CANVAS_SCROLL_GROUP_H__
+
+#include "canvas/container.h"
+
+namespace ArdourCanvas {
+
+/** A ScrollGroup has no contents of its own, but renders
+ *  its children in a way that reflects the most recent
+ *  call to its scroll_to() method.
+ */
+class LIBCANVAS_API ScrollGroup : public Container
+{
+  public:
+       enum ScrollSensitivity {
+               ScrollsVertically = 0x1,
+               ScrollsHorizontally = 0x2
+       };
+       
+       ScrollGroup (Canvas*, ScrollSensitivity);
+       ScrollGroup (Item*, ScrollSensitivity);
+
+       void scroll_to (Duple const& d);
+       Duple scroll_offset() const { return _scroll_offset; }
+
+       bool covers_canvas (Duple const& d) const;
+       bool covers_window (Duple const& d) const;
+
+       void render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const;
+
+  private:
+       ScrollSensitivity _scroll_sensitivity;
+       Duple             _scroll_offset;
+};
+
+}
+
+#endif
index 80687c5113a14998e1c9a03ffa8f6085ad20cef7..7c00b0c5a475c5773221548733ee1b70ff9d4049 100644 (file)
@@ -49,7 +49,8 @@ class StatefulImage : public Item
 
   public:
 
-    StatefulImage (Group*, const XMLNode&);
+    StatefulImage (Canvas*, const XMLNode&);
+    StatefulImage (Item*, const XMLNode&);
     ~StatefulImage ();
 
     bool set_state (States::size_type);
index 59d2007ceba17d30828d9f2874eaac3318b70e2d..85262ee9848cf5c8b0f7525da70c2899933510f7 100644 (file)
@@ -31,7 +31,8 @@ namespace ArdourCanvas {
 class LIBCANVAS_API Text : public Item
 {
 public:
-       Text (Group *);
+       Text (Canvas*);
+       Text (Item*);
        ~Text();
 
        void render (Rect const &, Cairo::RefPtr<Cairo::Context>) const;
diff --git a/libs/canvas/canvas/tracking_text.h b/libs/canvas/canvas/tracking_text.h
new file mode 100644 (file)
index 0000000..901e5c2
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+    Copyright (C) 2011-2013 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __ardour_canvas_tracking_text_h__
+#define __ardour_canvas_tracking_text_h__
+
+#include <string>
+#include "canvas/text.h"
+
+namespace ArdourCanvas {
+
+class LIBCANVAS_API TrackingText : public Text
+{
+  public:
+       TrackingText (Canvas*);
+       TrackingText (Item*);
+       
+       void show_and_track (bool track_x, bool track_y);
+       void set_offset (Duple const &);
+       void set_x_offset (double);
+       void set_y_offset (double);
+
+  private:
+       bool track_x;
+       bool track_y;
+       Duple offset;
+
+       void pointer_motion (Duple const&);
+       void init ();
+};
+
+}
+
+#endif /* __ardour_canvas_tracking_text_h__ */
index 2800ccc91be392d34be1b912864c786fa2df8204..6d952794c920d9efd35c7c36cd4b8575656f2972 100644 (file)
@@ -30,7 +30,7 @@
 #include "canvas/visibility.h"
 
 namespace Cairo {
-       struct Context;
+       class Context;
 }
 
 namespace ArdourCanvas
index cd9d884475a21c5eece012cb6dfdfff1edaf0c5a..e269ca215c3e5834a4f1124971070e4e7fee57dc 100644 (file)
@@ -31,5 +31,7 @@ namespace ArdourCanvas {
        extern LIBCANVAS_API void set_source_rgba (Cairo::RefPtr<Cairo::Context>, Color);
 
        Distance LIBCANVAS_API distance_to_segment_squared (Duple const & p, Duple const & p1, Duple const & p2, double& t, Duple& at);
+
+        uint32_t LIBCANVAS_API contrasting_text_color (uint32_t c);
 }
 
index 791d0d17dabfda4ccced3548a71fe8843e28131f..aa4ff61c3d588d00cfbc6d7cc181cd26c996fc97 100644 (file)
@@ -45,14 +45,33 @@ class WaveViewTest;
        
 namespace ArdourCanvas {
 
-class LIBCANVAS_API WaveView : virtual public Item, public Outline, public Fill
+class LIBCANVAS_API WaveView : public Item
 {
 public:
+
         enum Shape { 
                Normal,
-               Rectified,
+               Rectified
         };
-
+       
+       struct CacheEntry {
+               int channel;
+               Coord height;
+               float amplitude;
+               Color fill_color;
+               Color outline_color;
+               framepos_t start;
+               framepos_t end;
+               Cairo::RefPtr<Cairo::ImageSurface> image;
+       CacheEntry() : 
+               channel (0), height (0), amplitude(0), fill_color (0), 
+                       outline_color (0), start (0), end (0), image (0) {} 
+       CacheEntry(int chan, Coord hght, float amp, Color fcol, Color ocol, 
+                  framepos_t strt, framepos_t ed, Cairo::RefPtr<Cairo::ImageSurface> img) :
+               channel (chan), height (hght), amplitude (amp), fill_color (fcol),
+                       outline_color (ocol), start (strt), end (ed), image (img) {} 
+       };
+       
     /* Displays a single channel of waveform data for the given Region.
 
        x = 0 in the waveview corresponds to the first waveform datum taken
@@ -72,7 +91,8 @@ public:
     */
 
 
-       WaveView (Group *, boost::shared_ptr<ARDOUR::AudioRegion>);
+       WaveView (Canvas *, boost::shared_ptr<ARDOUR::AudioRegion>);
+       WaveView (Item*, boost::shared_ptr<ARDOUR::AudioRegion>);
        ~WaveView ();
 
        void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
@@ -132,7 +152,9 @@ private:
 
         friend class ::WaveViewTest;
 
-       void invalidate_image ();
+       static std::map <boost::shared_ptr<ARDOUR::AudioSource>, std::vector <CacheEntry> > _image_cache;
+       void consolidate_image_cache () const;
+       void invalidate_image_cache ();
 
        boost::shared_ptr<ARDOUR::AudioRegion> _region;
        int    _channel;
@@ -148,18 +170,14 @@ private:
         bool   _logscaled_independent;
         bool   _gradient_depth_independent;
         double _amplitude_above_axis;
+       float  _region_amplitude;
 
        /** The `start' value to use for the region; we can't use the region's
         *  value as the crossfade editor needs to alter it.
         */
        ARDOUR::frameoffset_t _region_start;
-    
-    
-        mutable ARDOUR::framepos_t _sample_start; 
-        mutable ARDOUR::framepos_t _sample_end; 
-        mutable Cairo::RefPtr<Cairo::ImageSurface> _image;
 
-        PBD::ScopedConnection invalidation_connection;
+        PBD::ScopedConnectionList invalidation_connection;
 
         static double _global_gradient_depth;
         static bool   _global_logscaled;
@@ -170,10 +188,12 @@ private:
         static PBD::Signal0<void> VisualPropertiesChanged;
 
         void handle_visual_property_change ();
+        void handle_clip_level_change ();
+
+       void get_image (Cairo::RefPtr<Cairo::ImageSurface>& image, framepos_t start, framepos_t end, double& image_offset) const;
 
-        void ensure_cache (ARDOUR::framepos_t sample_start, ARDOUR::framepos_t sample_end) const;
-        ArdourCanvas::Coord position (double) const;
-        void draw_image (ARDOUR::PeakData*, int npeaks) const;
+        ArdourCanvas::Coord y_extent (double, bool) const;
+       void draw_image (Cairo::RefPtr<Cairo::ImageSurface>&, ARDOUR::PeakData*, int) const;
 };
 
 }
diff --git a/libs/canvas/canvas/widget.h b/libs/canvas/canvas/widget.h
new file mode 100644 (file)
index 0000000..590bb3a
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+    Copyright (C) 2011-2013 Paul Davis
+    Author: Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __CANVAS_WIDGET_H__
+#define __CANVAS_WIDGET_H__
+
+#include "canvas/visibility.h"
+#include "canvas/item.h"
+
+#include "gtkmm2ext/cairo_widget.h"
+
+namespace ArdourCanvas
+{
+
+class LIBCANVAS_API Widget : public Item
+{
+public:
+       Widget (Canvas*, CairoWidget&);
+       Widget (Item*, CairoWidget&);
+       
+       void render (Rect const &, Cairo::RefPtr<Cairo::Context>) const;
+       void compute_bounding_box () const;
+
+       CairoWidget const & get () const {
+               return _widget;
+       }
+
+private:
+       CairoWidget& _widget;
+       bool event_proxy (GdkEvent*);
+};
+
+}
+
+#endif
diff --git a/libs/canvas/canvas/xfade_curve.h b/libs/canvas/canvas/xfade_curve.h
new file mode 100644 (file)
index 0000000..c63e47c
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+    Copyright (C) 2013 Paul Davis
+    Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __CANVAS_XFADECURVE_H__
+#define __CANVAS_XFADECURVE_H__
+
+#include "canvas/visibility.h"
+#include "canvas/item.h"
+#include "canvas/curve.h"
+
+namespace ArdourCanvas {
+
+class LIBCANVAS_API XFadeCurve : public Item, public InterpolatedCurve
+{
+public:
+       enum XFadePosition {
+               Start,
+               End,
+       };
+
+       XFadeCurve (Canvas *);
+       XFadeCurve (Canvas *, XFadePosition);
+       XFadeCurve (Item*);
+       XFadeCurve (Item*, XFadePosition);
+
+       void set_fade_position (XFadePosition xfp) { _xfadeposition = xfp; }
+       
+       void compute_bounding_box () const;
+       void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
+
+       void set_points_per_segment (uint32_t n);
+       void set_inout (Points const & in, Points const & out);
+
+       void set_outline_color (Color c) {
+               begin_visual_change ();
+               _outline_color = c;
+               end_visual_change ();
+       };
+
+       void set_fill_color (Color c) {
+               begin_visual_change ();
+               _fill_color = c;
+               end_visual_change ();
+       }
+
+private:
+       struct CanvasCurve {
+               CanvasCurve() : n_samples(0) { }
+               Points points;
+               Points samples;
+               Points::size_type n_samples;
+       };
+
+       Cairo::Path * get_path(Rect const &, Cairo::RefPtr<Cairo::Context>, CanvasCurve const &) const;
+       void close_path(Rect const &, Cairo::RefPtr<Cairo::Context>, CanvasCurve const &p, bool) const;
+
+       uint32_t points_per_segment;
+
+       CanvasCurve _in;
+       CanvasCurve _out;
+
+       XFadePosition _xfadeposition;
+       Color _outline_color;
+       Color _fill_color;
+
+       void interpolate ();
+};
+
+}
+
+#endif
index 15a1679f748048c517c227f751a53a913a8d8389..1859434123182d14af5e5af34480d464804d3eac 100644 (file)
 
 using namespace ArdourCanvas;
 
-Circle::Circle (Group* parent)
-       : Item (parent)
-       , Arc (parent)
+Circle::Circle (Canvas* c)
+       : Arc (c)
 {
        set_arc (360.0);
 }
 
+Circle::Circle (Item * parent)
+       : Arc (parent)
+{
+       set_arc (360.0);
+}
+
+
diff --git a/libs/canvas/container.cc b/libs/canvas/container.cc
new file mode 100644 (file)
index 0000000..6aa265f
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+    Copyright (C) 2011-2014 Paul Davis
+    Author: Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "canvas/container.h"
+
+using namespace ArdourCanvas;
+
+Container::Container (Canvas* canvas) 
+       : Item (canvas)
+{
+}
+
+Container::Container (Item* parent) 
+       : Item (parent)
+{
+}
+
+
+Container::Container (Item* parent, Duple const & p) 
+       : Item (parent, p)
+{
+}
+
+void
+Container::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
+{
+       Item::render_children (area, context);
+}
+
+void
+Container::compute_bounding_box () const
+{
+       _bounding_box = boost::none;
+       add_child_bounding_boxes ();
+       _bounding_box_dirty = false;
+}
+
index af7e3c110a1d94e3886f2fc27072baa7193f82fe..ba6ac68df6d651730b436f4a447ee26defe23dba 100644 (file)
@@ -27,13 +27,21 @@ using namespace ArdourCanvas;
 using std::min;
 using std::max;
 
-Curve::Curve (Group* parent)
-       : Item (parent)
-       , PolyItem (parent)
-       , Fill (parent)
+Curve::Curve (Canvas* c)
+       : PolyItem (c)
        , n_samples (0)
        , points_per_segment (16)
        , curve_type (CatmullRomCentripetal)
+       , curve_fill (None)
+{
+}
+
+Curve::Curve (Item* parent)
+       : PolyItem (parent)
+       , n_samples (0)
+       , points_per_segment (16)
+       , curve_type (CatmullRomCentripetal)
+       , curve_fill (None)
 {
 }
 
@@ -73,233 +81,10 @@ void
 Curve::interpolate ()
 {
        samples.clear ();
-       interpolate (_points, points_per_segment, CatmullRomCentripetal, false, samples);
+       InterpolatedCurve::interpolate (_points, points_per_segment, CatmullRomCentripetal, false, samples);
        n_samples = samples.size();
 }
 
-/* Cartmull-Rom code from http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/19283471#19283471
- * 
- * Thanks to Ted for his Java version, which I translated into Ardour-idiomatic
- * C++ here.
- */
-
-/**
- * Calculate the same values but introduces the ability to "parameterize" the t
- * values used in the calculation. This is based on Figure 3 from
- * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf
- *
- * @param p An array of double values of length 4, where interpolation
- * occurs from p1 to p2.
- * @param time An array of time measures of length 4, corresponding to each
- * p value.
- * @param t the actual interpolation ratio from 0 to 1 representing the
- * position between p1 and p2 to interpolate the value.
- */
-static double 
-__interpolate (double p[4], double time[4], double t) 
-{
-        const double L01 = p[0] * (time[1] - t) / (time[1] - time[0]) + p[1] * (t - time[0]) / (time[1] - time[0]);
-        const double L12 = p[1] * (time[2] - t) / (time[2] - time[1]) + p[2] * (t - time[1]) / (time[2] - time[1]);
-        const double L23 = p[2] * (time[3] - t) / (time[3] - time[2]) + p[3] * (t - time[2]) / (time[3] - time[2]);
-        const double L012 = L01 * (time[2] - t) / (time[2] - time[0]) + L12 * (t - time[0]) / (time[2] - time[0]);
-        const double L123 = L12 * (time[3] - t) / (time[3] - time[1]) + L23 * (t - time[1]) / (time[3] - time[1]);
-        const double C12 = L012 * (time[2] - t) / (time[2] - time[1]) + L123 * (t - time[1]) / (time[2] - time[1]);
-        return C12;
-}   
-
-/**
- * Given a list of control points, this will create a list of points_per_segment
- * points spaced uniformly along the resulting Catmull-Rom curve.
- *
- * @param points The list of control points, leading and ending with a 
- * coordinate that is only used for controling the spline and is not visualized.
- * @param index The index of control point p0, where p0, p1, p2, and p3 are
- * used in order to create a curve between p1 and p2.
- * @param points_per_segment The total number of uniformly spaced interpolated
- * points to calculate for each segment. The larger this number, the
- * smoother the resulting curve.
- * @param curve_type Clarifies whether the curve should use uniform, chordal
- * or centripetal curve types. Uniform can produce loops, chordal can
- * produce large distortions from the original lines, and centripetal is an
- * optimal balance without spaces.
- * @return the list of coordinates that define the CatmullRom curve
- * between the points defined by index+1 and index+2.
- */
-static void
-_interpolate (const Points& points, Points::size_type index, int points_per_segment, Curve::SplineType curve_type, Points& results) 
-{
-        double x[4];
-        double y[4];
-        double time[4];
-
-        for (int i = 0; i < 4; i++) {
-                x[i] = points[index + i].x;
-                y[i] = points[index + i].y;
-                time[i] = i;
-        }
-        
-        double tstart = 1;
-        double tend = 2;
-
-        if (curve_type != Curve::CatmullRomUniform) {
-                double total = 0;
-                for (int i = 1; i < 4; i++) {
-                        double dx = x[i] - x[i - 1];
-                        double dy = y[i] - y[i - 1];
-                        if (curve_type == Curve::CatmullRomCentripetal) {
-                                total += pow (dx * dx + dy * dy, .25);
-                        } else {
-                                total += pow (dx * dx + dy * dy, .5);
-                        }
-                        time[i] = total;
-                }
-                tstart = time[1];
-                tend = time[2];
-        }
-
-        int segments = points_per_segment - 1;
-        results.push_back (points[index + 1]);
-
-        for (int i = 1; i < segments; i++) {
-                double xi = __interpolate (x, time, tstart + (i * (tend - tstart)) / segments);
-                double yi = __interpolate (y, time, tstart + (i * (tend - tstart)) / segments);
-                results.push_back (Duple (xi, yi));
-        }
-
-        results.push_back (points[index + 2]);
-}
-
-/**
- * This method will calculate the Catmull-Rom interpolation curve, returning
- * it as a list of Coord coordinate objects.  This method in particular
- * adds the first and last control points which are not visible, but required
- * for calculating the spline.
- *
- * @param coordinates The list of original straight line points to calculate
- * an interpolation from.
- * @param points_per_segment The integer number of equally spaced points to
- * return along each curve.  The actual distance between each
- * point will depend on the spacing between the control points.
- * @return The list of interpolated coordinates.
- * @param curve_type Chordal (stiff), Uniform(floppy), or Centripetal(medium)
- * @throws gov.ca.water.shapelite.analysis.CatmullRomException if
- * points_per_segment is less than 2.
- */
-
-void
-Curve::interpolate (const Points& coordinates, uint32_t points_per_segment, SplineType curve_type, bool closed, Points& results)
-{
-        if (points_per_segment < 2) {
-                return;
-        }
-        
-        // Cannot interpolate curves given only two points.  Two points
-        // is best represented as a simple line segment.
-        if (coordinates.size() < 3) {
-                results = coordinates;
-                return;
-        }
-
-        // Copy the incoming coordinates. We need to modify it during interpolation
-        Points vertices = coordinates;
-        
-        // Test whether the shape is open or closed by checking to see if
-        // the first point intersects with the last point.  M and Z are ignored.
-        if (closed) {
-                // Use the second and second from last points as control points.
-                // get the second point.
-                Duple p2 = vertices[1];
-                // get the point before the last point
-                Duple pn1 = vertices[vertices.size() - 2];
-                
-                // insert the second from the last point as the first point in the list
-                // because when the shape is closed it keeps wrapping around to
-                // the second point.
-                vertices.insert(vertices.begin(), pn1);
-                // add the second point to the end.
-                vertices.push_back(p2);
-        } else {
-                // The shape is open, so use control points that simply extend
-                // the first and last segments
-                
-                // Get the change in x and y between the first and second coordinates.
-                double dx = vertices[1].x - vertices[0].x;
-                double dy = vertices[1].y - vertices[0].y;
-                
-                // Then using the change, extrapolate backwards to find a control point.
-                double x1 = vertices[0].x - dx;
-                double y1 = vertices[0].y - dy;
-                
-                // Actaully create the start point from the extrapolated values.
-                Duple start (x1, y1);
-                
-                // Repeat for the end control point.
-                int n = vertices.size() - 1;
-                dx = vertices[n].x - vertices[n - 1].x;
-                dy = vertices[n].y - vertices[n - 1].y;
-                double xn = vertices[n].x + dx;
-                double yn = vertices[n].y + dy;
-                Duple end (xn, yn);
-                
-                // insert the start control point at the start of the vertices list.
-                vertices.insert (vertices.begin(), start);
-                
-                // append the end control ponit to the end of the vertices list.
-                vertices.push_back (end);
-        }
-        
-        // When looping, remember that each cycle requires 4 points, starting
-        // with i and ending with i+3.  So we don't loop through all the points.
-        
-        for (Points::size_type i = 0; i < vertices.size() - 3; i++) {
-
-                // Actually calculate the Catmull-Rom curve for one segment.
-               Points r;
-
-                _interpolate (vertices, i, points_per_segment, curve_type, r);
-                // Since the middle points are added twice, once for each bordering
-                // segment, we only add the 0 index result point for the first
-                // segment.  Otherwise we will have duplicate points.
-
-                if (results.size() > 0) {
-                        r.erase (r.begin());
-                }
-                
-                // Add the coordinates for the segment to the result list.
-
-                results.insert (results.end(), r.begin(), r.end());
-        }
-}
-
-/** Given a fractional position within the x-axis range of the
- * curve, return the corresponding y-axis value
- */
-
-double
-Curve::map_value (double x) const
-{
-       if (x > 0.0 && x < 1.0) {
-
-               double f;
-               Points::size_type index;
-               
-               /* linearly interpolate between two of our smoothed "samples"
-                */
-               
-               x = x * (n_samples - 1);
-               index = (Points::size_type) x; // XXX: should we explicitly use floor()?
-               f = x - index;
-
-               return (1.0 - f) * samples[index].y + f * samples[index+1].y;
-               
-       } else if (x >= 1.0) {
-               return samples.back().y;
-       } else {
-               return samples.front().y;
-       }
-}
-
 void
 Curve::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
 {
@@ -333,7 +118,32 @@ Curve::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
                window_space = item_to_window (_points.back());
                context->line_to (window_space.x, window_space.y);
 
-               context->stroke ();
+
+               switch (curve_fill) {
+                       case None:
+                               context->stroke();
+                               break;
+                       case Inside:
+                               context->stroke_preserve ();
+                               window_space = item_to_window (Duple(_points.back().x, draw.height()));
+                               context->line_to (window_space.x, window_space.y);
+                               window_space = item_to_window (Duple(_points.front().x, draw.height()));
+                               context->line_to (window_space.x, window_space.y);
+                               context->close_path();
+                               setup_fill_context(context);
+                               context->fill ();
+                               break;
+                       case Outside:
+                               context->stroke_preserve ();
+                               window_space = item_to_window (Duple(_points.back().x, 0.0));
+                               context->line_to (window_space.x, window_space.y);
+                               window_space = item_to_window (Duple(_points.front().x, 0.0));
+                               context->line_to (window_space.x, window_space.y);
+                               context->close_path();
+                               setup_fill_context(context);
+                               context->fill ();
+                               break;
+               }
 
        } else {
 
@@ -366,46 +176,61 @@ Curve::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
                        draw.x1 = w2.x;
                }
 
-               /* full width of the curve */
-               const double xextent = _points.back().x - _points.front().x;
-               /* Determine where the first drawn point will be */
-               Duple item_space = window_to_item (Duple (draw.x0, 0)); /* y value is irrelevant */
-               /* determine the fractional offset of this location into the overall extent of the curve */
-               const double xfract_offset = (item_space.x - _points.front().x)/xextent;
-               const uint32_t pixels = draw.width ();
+               /* find left and right-most sample */
                Duple window_space;
+               Points::size_type left = 0;
+               Points::size_type right = n_samples;
 
-               /* draw the first point */
-
-               for (uint32_t pixel = 0; pixel < pixels; ++pixel) {
-
-                       /* fractional distance into the total horizontal extent of the curve */
-                       double xfract = xfract_offset + (pixel / xextent);
-                       /* compute vertical coordinate (item-space) at that location */
-                       double y = map_value (xfract);
-                       
-                       /* convert to window space for drawing */
-                       window_space = item_to_window (Duple (0.0, y)); /* x-value is irrelevant */
-
-                       /* we are moving across the draw area pixel-by-pixel */
-                       window_space.x = draw.x0 + pixel;
-                       
-                       /* plot this point */
-                       if (pixel == 0) {
-                               context->move_to (window_space.x, window_space.y);
-                       } else {
-                               context->line_to (window_space.x, window_space.y);
-                       }
+               for (Points::size_type idx = 0; idx < n_samples - 1; ++idx) {
+                       left = idx;
+                       window_space = item_to_window (Duple (samples[idx].x, 0.0));
+                       if (window_space.x >= draw.x0) break;
+               }
+               for (Points::size_type idx = n_samples; idx > left + 1; --idx) {
+                       window_space = item_to_window (Duple (samples[idx].x, 0.0));
+                       if (window_space.x <= draw.x1) break;
+                       right = idx;
                }
 
-               context->stroke ();
+               /* draw line between samples */
+               window_space = item_to_window (Duple (samples[left].x, samples[left].y));
+               context->move_to (window_space.x, window_space.y);
+               for (uint32_t idx = left + 1; idx < right; ++idx) {
+                       window_space = item_to_window (Duple (samples[idx].x, samples[idx].y));
+                       context->line_to (window_space.x, window_space.y);
+               }
+
+               switch (curve_fill) {
+                       case None:
+                               context->stroke();
+                               break;
+                       case Inside:
+                               context->stroke_preserve ();
+                               window_space = item_to_window (Duple (samples[right-1].x, draw.height()));
+                               context->line_to (window_space.x, window_space.y);
+                               window_space = item_to_window (Duple (samples[left].x, draw.height()));
+                               context->line_to (window_space.x, window_space.y);
+                               context->close_path();
+                               setup_fill_context(context);
+                               context->fill ();
+                               break;
+                       case Outside:
+                               context->stroke_preserve ();
+                               window_space = item_to_window (Duple (samples[right-1].x, 0.0));
+                               context->line_to (window_space.x, window_space.y);
+                               window_space = item_to_window (Duple (samples[left].x, 0.0));
+                               context->line_to (window_space.x, window_space.y);
+                               context->close_path();
+                               setup_fill_context(context);
+                               context->fill ();
+                               break;
+               }
                context->restore ();
        }
 
-#if 1
+#if 0
        /* add points */
-       
-       setup_fill_context (context);
+       setup_outline_context (context);
        for (Points::const_iterator p = _points.begin(); p != _points.end(); ++p) {
                Duple window_space (item_to_window (*p));
                context->arc (window_space.x, window_space.y, 5.0, 0.0, 2 * M_PI);
@@ -417,7 +242,7 @@ Curve::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
 bool
 Curve::covers (Duple const & pc) const
 {
-       Duple point = canvas_to_item (pc);
+       Duple point = window_to_item (pc);
 
        /* O(N) N = number of points, and not accurate */
 
diff --git a/libs/canvas/drag_handle.cc b/libs/canvas/drag_handle.cc
deleted file mode 100644 (file)
index 94b2e7d..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
-    Copyright (C) 2014 Paul Davis
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include <iostream>
-#include <cairomm/context.h>
-#include "pbd/stacktrace.h"
-#include "pbd/compose.h"
-
-#include "canvas/drag_handle.h"
-
-using namespace ArdourCanvas;
-
-DragHandle::DragHandle (Group* g, Rect const & r, bool left_side)
-       : Item (g)
-       , Rectangle (g, r)
-       , _left_side (left_side)
-{
-}
-
-void
-DragHandle::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
-{
-       Rectangle::render (area, context);
-
-#if 0
-       Duple circle_center (_left_side ? x0() : x1(), (y1() - y0())/2.0);
-       Duple window_circle_center = item_to_window (circle_center);
-       
-       context->set_source_rgba (1.0, 0.0, 0.0, 1.0);
-
-       if (_left_side) {
-               context->arc (window_circle_center.x, window_circle_center.y, 7.0, -M_PI/2.0, +M_PI/2.0);
-       } else {
-               context->arc_negative (window_circle_center.x, window_circle_center.y, 7.0, -M_PI/2.0, +M_PI/2.0);
-       }
-
-       context->fill ();
-#endif
-
-}
index 41c616a0f970deeb4dcc55d09808e46eb36b96b8..42bcbfff3ccff2c82f0cf601e21af6d1cbf502d5 100644 (file)
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
+#include <cairomm/cairomm.h>
+
 #include "ardour/utils.h"
 
 #include "pbd/compose.h"
 #include "pbd/convert.h"
 
 #include "canvas/fill.h"
+#include "canvas/item.h"
+#include "canvas/types.h"
 #include "canvas/utils.h"
 
 using namespace std;
 using namespace ArdourCanvas;
 
-Fill::Fill (Group* parent)
-       : Item (parent)
+Fill::Fill (Item& self)
+       : _self (self)
        , _fill_color (0x000000ff)
        , _fill (true)
        , _transparent (false)
 {
-
 }
 
 void
 Fill::set_fill_color (Color color)
 {
        if (_fill_color != color) {
-               begin_visual_change ();
+               _self.begin_visual_change ();
                _fill_color = color;
 
                double r, g, b, a;
@@ -52,7 +55,7 @@ Fill::set_fill_color (Color color)
                        _transparent = false;
                }
 
-               end_visual_change ();
+               _self.end_visual_change ();
        }
 }
 
@@ -60,9 +63,9 @@ void
 Fill::set_fill (bool fill)
 {
        if (_fill != fill) {
-               begin_visual_change ();
+               _self.begin_visual_change ();
                _fill = fill;
-               end_visual_change ();
+               _self.end_visual_change ();
        }
 }
 
@@ -95,7 +98,7 @@ Fill::setup_gradient_context (Cairo::RefPtr<Cairo::Context> context, Rect const
 void
 Fill::set_gradient (StopList const & stops, bool vertical)
 {
-       begin_visual_change ();
+       _self.begin_visual_change ();
 
        if (stops.empty()) {
                _stops.clear ();
@@ -104,5 +107,5 @@ Fill::set_gradient (StopList const & stops, bool vertical)
                _vertical_gradient = vertical;
        }
 
-       end_visual_change ();
+       _self.end_visual_change ();
 }
index e72aece1f56ac2c7a0a7fb13d749a0e5beef2434..243e71cd170e822735a278a8695d1efe81b3f7ca 100644 (file)
 using namespace std;
 using namespace ArdourCanvas;
 
-Flag::Flag (Group* parent, Distance height, Color outline_color, Color fill_color, Duple position)
-       : Group (parent)
+Flag::Flag (Canvas* canvas, Distance height, Color outline_color, Color fill_color, Duple position)
+       : Container (canvas)
        , _outline_color (outline_color)
        , _fill_color (fill_color)
+{
+       setup (height, position);
+}
+
+Flag::Flag (Item* parent, Distance height, Color outline_color, Color fill_color, Duple position)
+       : Container (parent)
+       , _outline_color (outline_color)
+       , _fill_color (fill_color)
+{
+       setup (height, position);
+}
+
+void 
+Flag::setup (Distance height, Duple position)
 {
        _text = new Text (this);
        _text->set_alignment (Pango::ALIGN_CENTER);
diff --git a/libs/canvas/group.cc b/libs/canvas/group.cc
deleted file mode 100644 (file)
index fbe252a..0000000
+++ /dev/null
@@ -1,387 +0,0 @@
-/*
-    Copyright (C) 2011-2013 Paul Davis
-    Author: Carl Hetherington <cth@carlh.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include <iostream>
-#include <cairomm/context.h>
-
-#include "pbd/stacktrace.h"
-#include "pbd/compose.h"
-
-#include "canvas/group.h"
-#include "canvas/types.h"
-#include "canvas/debug.h"
-#include "canvas/item.h"
-#include "canvas/canvas.h"
-
-using namespace std;
-using namespace ArdourCanvas;
-
-int Group::default_items_per_cell = 64;
-
-
-Group::Group (Canvas* canvas)
-       : Item (canvas)
-       , _lut (0)
-{
-       
-}
-
-Group::Group (Group* parent)
-       : Item (parent)
-       , _lut (0)
-{
-       
-}
-
-Group::Group (Group* parent, Duple position)
-       : Item (parent, position)
-       , _lut (0)
-{
-       
-}
-
-Group::~Group ()
-{
-       clear_items (true);
-}
-
-/** @param area Area to draw in this group's coordinates.
- *  @param context Context, set up with its origin at this group's position.
- */
-void
-Group::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
-{
-       ensure_lut ();
-       vector<Item*> items = _lut->get (area);
-
-#ifdef CANVAS_DEBUG
-       if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
-               cerr << string_compose ("%1GROUP %2 @ %7 render %5 @ %6 %3 items out of %4\n", 
-                                       _canvas->render_indent(), (name.empty() ? string ("[unnamed]") : name), items.size(), _items.size(), area, _position, this);
-       }
-#endif
-
-       ++render_depth;
-               
-       for (vector<Item*>::const_iterator i = items.begin(); i != items.end(); ++i) {
-
-               if (!(*i)->visible ()) {
-#ifdef CANVAS_DEBUG
-                       if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
-                               cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] invisible - skipped\n";
-                       }
-#endif
-                       continue;
-               }
-               
-               boost::optional<Rect> item_bbox = (*i)->bounding_box ();
-
-               if (!item_bbox) {
-#ifdef CANVAS_DEBUG
-                       if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
-                               cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] empty - skipped\n";
-                       }
-#endif
-                       continue;
-               }
-               
-               Rect item = (*i)->item_to_window (item_bbox.get());
-               boost::optional<Rect> d = item.intersection (area);
-               
-               if (d) {
-                       Rect draw = d.get();
-                       if (draw.width() && draw.height()) {
-#ifdef CANVAS_DEBUG
-                               if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
-                                       if (dynamic_cast<Group*>(*i) == 0) {
-                                               cerr << _canvas->render_indent() << "render "
-                                                    << ' ' 
-                                                    << (*i)
-                                                    << ' '
-                                                    << (*i)->whatami()
-                                                    << ' '
-                                                    << (*i)->name
-                                                    << " item = " 
-                                                    << item
-                                                    << " intersect = "
-                                                    << draw
-                                                    << " @ " 
-                                                    << _position
-                                                    << endl;
-                                       }
-                               }
-#endif
-
-                               (*i)->render (area, context);
-                               ++render_count;
-                       }
-
-               } else {
-
-#ifdef CANVAS_DEBUG
-                       if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
-                               cerr << string_compose ("%1skip render of %2 %3, no intersection\n", _canvas->render_indent(), (*i)->whatami(),
-                                                       (*i)->name);
-                       }
-#endif
-
-               }
-       }
-
-       --render_depth;
-}
-
-void
-Group::compute_bounding_box () const
-{
-       Rect bbox;
-       bool have_one = false;
-
-       for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
-
-               boost::optional<Rect> item_bbox = (*i)->bounding_box ();
-
-               if (!item_bbox) {
-                       continue;
-               }
-
-               Rect group_bbox = (*i)->item_to_parent (item_bbox.get ());
-               if (have_one) {
-                       bbox = bbox.extend (group_bbox);
-               } else {
-                       bbox = group_bbox;
-                       have_one = true;
-               }
-       }
-
-       if (!have_one) {
-               _bounding_box = boost::optional<Rect> ();
-       } else {
-               _bounding_box = bbox;
-       }
-
-       _bounding_box_dirty = false;
-}
-
-void
-Group::add (Item* i)
-{
-       /* XXX should really notify canvas about this */
-
-       _items.push_back (i);
-       invalidate_lut ();
-       _bounding_box_dirty = true;
-
-       
-}
-
-void
-Group::remove (Item* i)
-{
-
-       if (i->parent() != this) {
-               return;
-       }
-
-       /* we cannot call bounding_box() here because that will iterate over
-          _items, one of which (the argument, i) may be in the middle of
-          deletion, making it impossible to call compute_bounding_box()
-          on it.
-       */
-
-       if (_bounding_box) {
-               _pre_change_bounding_box = _bounding_box;
-       } else {
-               _pre_change_bounding_box = Rect();
-       }
-
-       i->unparent ();
-       _items.remove (i);
-       invalidate_lut ();
-       _bounding_box_dirty = true;
-       
-       end_change ();
-}
-
-void
-Group::clear (bool with_delete)
-{
-       begin_change ();
-
-       clear_items (with_delete);
-
-       invalidate_lut ();
-       _bounding_box_dirty = true;
-
-       end_change ();
-}
-
-void
-Group::clear_items (bool with_delete)
-{
-       for (list<Item*>::iterator i = _items.begin(); i != _items.end(); ) {
-
-               list<Item*>::iterator tmp = i;
-               Item *item = *i;
-
-               ++tmp;
-
-               /* remove from list before doing anything else, because we
-                * don't want to find the item in _items during any activity
-                * driven by unparent-ing or deletion.
-                */
-
-               _items.erase (i);
-               item->unparent ();
-               
-               if (with_delete) {
-                       delete item;
-               }
-
-               i = tmp;
-       }
-}
-
-void
-Group::raise_child_to_top (Item* i)
-{
-       if (!_items.empty()) {
-               if (_items.back() == i) {
-                       return;
-               }
-       }
-
-       _items.remove (i);
-       _items.push_back (i);
-       invalidate_lut ();
-}
-
-void
-Group::raise_child (Item* i, int levels)
-{
-       list<Item*>::iterator j = find (_items.begin(), _items.end(), i);
-       assert (j != _items.end ());
-
-       ++j;
-       _items.remove (i);
-
-       while (levels > 0 && j != _items.end ()) {
-               ++j;
-               --levels;
-       }
-
-       _items.insert (j, i);
-       invalidate_lut ();
-}
-
-void
-Group::lower_child_to_bottom (Item* i)
-{
-       if (!_items.empty()) {
-               if (_items.front() == i) {
-                       return;
-               }
-       }
-       _items.remove (i);
-       _items.push_front (i);
-       invalidate_lut ();
-}
-
-void
-Group::ensure_lut () const
-{
-       if (!_lut) {
-               _lut = new DumbLookupTable (*this);
-       }
-}
-
-void
-Group::invalidate_lut () const
-{
-       delete _lut;
-       _lut = 0;
-}
-
-void
-Group::child_changed ()
-{
-       invalidate_lut ();
-       _bounding_box_dirty = true;
-
-       if (_parent) {
-               _parent->child_changed ();
-       }
-}
-
-void
-Group::add_items_at_point (Duple const point, vector<Item const *>& items) const
-{
-       boost::optional<Rect> const bbox = bounding_box ();
-
-       /* Point is in canvas coordinate system */
-
-       if (!bbox || !item_to_canvas (bbox.get()).contains (point)) {
-               return;
-       }
-
-       /* now recurse and add any items within our group that contain point */
-
-       ensure_lut ();
-       vector<Item*> our_items = _lut->items_at_point (point);
-
-       if (!our_items.empty()) {
-               /* this adds this group itself to the list of items at point */
-               Item::add_items_at_point (point, items);
-       }
-
-       for (vector<Item*>::iterator i = our_items.begin(); i != our_items.end(); ++i) {
-               (*i)->add_items_at_point (point, items);
-       }
-}
-
-void
-Group::dump (ostream& o) const
-{
-#ifdef CANVAS_DEBUG
-       o << _canvas->indent();
-       o << "Group " << this << " [" << name << ']';
-       o << " @ " << position();
-       o << " Items: " << _items.size();
-       o << " Visible ? " << _visible;
-
-       boost::optional<Rect> bb = bounding_box();
-
-       if (bb) {
-               o << endl << _canvas->indent() << "  bbox: " << bb.get();
-               o << endl << _canvas->indent() << "  CANVAS bbox: " << item_to_canvas (bb.get());
-       } else {
-               o << "  bbox unset";
-       }
-
-       o << endl;
-#endif
-
-       ArdourCanvas::dump_depth++;
-
-       for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
-               o << **i;
-       }
-
-       ArdourCanvas::dump_depth--;
-}
index 32e453dc30e70b3a19bd271dece3ff2e9d8e28b2..46cadd0d1cc32008b3c0482725715970c11df2bc 100644 (file)
 
 using namespace ArdourCanvas;
 
-Image::Image (Group* group, Cairo::Format fmt, int width, int height)
-       : Item (group)
+Image::Image (Canvas* canvas, Cairo::Format fmt, int width, int height)
+       : Item (canvas)
+       , _format (fmt)
+       , _width (width)
+       , _height (height)
+       , _need_render (false)
+{
+       DataReady.connect (data_connections, MISSING_INVALIDATOR, boost::bind (&Image::accept_data, this), gui_context());
+}
+
+Image::Image (Item* parent, Cairo::Format fmt, int width, int height)
+       : Item (parent)
        , _format (fmt)
        , _width (width)
        , _height (height)
index e4e3c3054667ee54143bd67435933178a5e9da2e..9be1f62d9246f31a36bd4e961749d03894cdef12 100644 (file)
 
 #include "ardour/utils.h"
 
-#include "canvas/group.h"
-#include "canvas/item.h"
 #include "canvas/canvas.h"
 #include "canvas/debug.h"
+#include "canvas/item.h"
+#include "canvas/scroll_group.h"
 
 using namespace std;
 using namespace PBD;
 using namespace ArdourCanvas;
 
+int Item::default_items_per_cell = 64;
+
 Item::Item (Canvas* canvas)
-       : _canvas (canvas)
+       : Fill (*this)
+       , Outline (*this)
+       ,  _canvas (canvas)
        , _parent (0)
+       , _scroll_parent (0)
+       , _visible (true)
+       , _bounding_box_dirty (true)
+       , _lut (0)
+       , _ignore_events (false)
 {
-       init ();
-}
+       DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
+}      
 
-Item::Item (Group* parent)
-       : _canvas (parent->canvas ())
+Item::Item (Item* parent)
+       : Fill (*this)
+       , Outline (*this)
+       ,  _canvas (parent->canvas())
        , _parent (parent)
+       , _scroll_parent (0)
+       , _visible (true)
+       , _bounding_box_dirty (true)
+       , _lut (0)
+       , _ignore_events (false)
 {
-       init ();
-}
+       DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
+
+       if (parent) {
+               _parent->add (this);
+       }
 
-Item::Item (Group* parent, Duple position)
-       : _canvas (parent->canvas())
+       find_scroll_parent ();
+}      
+
+Item::Item (Item* parent, Duple const& p)
+       : Fill (*this)
+       , Outline (*this)
+       ,  _canvas (parent->canvas())
        , _parent (parent)
-       , _position (position)
+       , _scroll_parent (0)
+       , _position (p)
+       , _visible (true)
+       , _bounding_box_dirty (true)
+       , _lut (0)
+       , _ignore_events (false)
 {
-       init ();
-}
+       DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
 
-void
-Item::init ()
-{
-       _visible = true;
-       _bounding_box_dirty = true;
-       _ignore_events = false;
-       
-       if (_parent) {
+       if (parent) {
                _parent->add (this);
        }
 
-       DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
+       find_scroll_parent ();
+
 }      
 
 Item::~Item ()
@@ -77,6 +99,29 @@ Item::~Item ()
        if (_canvas) {
                _canvas->item_going_away (this, _bounding_box);
        }
+
+       clear_items (true);
+       delete _lut;
+}
+
+Duple
+Item::canvas_origin () const
+{
+       return item_to_canvas (Duple (0,0));
+}
+
+Duple
+Item::window_origin () const 
+{
+       /* This is slightly subtle. Our _position is in the coordinate space of 
+          our parent. So to find out where that is in window coordinates, we
+          have to ask our parent.
+       */
+       if (_parent) {
+               return _parent->item_to_window (_position);
+       } else {
+               return _position;
+       }
 }
 
 ArdourCanvas::Rect
@@ -85,8 +130,17 @@ Item::item_to_parent (ArdourCanvas::Rect const & r) const
        return r.translate (_position);
 }
 
-ArdourCanvas::Rect
-Item::item_to_canvas (ArdourCanvas::Rect const & r) const
+Duple
+Item::scroll_offset () const
+{
+       if (_scroll_parent) {
+               return _scroll_parent->scroll_offset();
+       } 
+       return Duple (0,0);
+}
+
+Duple
+Item::position_offset() const
 {
        Item const * i = this;
        Duple offset;
@@ -96,49 +150,31 @@ Item::item_to_canvas (ArdourCanvas::Rect const & r) const
                i = i->parent();
        }
 
-       return r.translate (offset);
+       return offset;
+}
+
+ArdourCanvas::Rect
+Item::item_to_canvas (ArdourCanvas::Rect const & r) const
+{
+       return r.translate (position_offset());
 }
 
 ArdourCanvas::Duple
 Item::item_to_canvas (ArdourCanvas::Duple const & d) const
 {
-       Item const * i = this;
-       Duple offset;
-
-       while (i) {
-               offset = offset.translate (i->position());
-               i = i->parent();
-       }
-
-       return d.translate (offset);
+       return d.translate (position_offset());
 }
 
 ArdourCanvas::Duple
-Item::canvas_to_item (ArdourCanvas::Duple const & d) const
+Item::canvas_to_item (ArdourCanvas::Duple const & r) const
 {
-       Item const * i = this;
-       Duple offset;
-
-       while (i) {
-               offset = offset.translate (-(i->position()));
-               i = i->parent();
-       }
-
-       return d.translate (offset);
+       return r.translate (-position_offset());
 }
 
 ArdourCanvas::Rect
-Item::canvas_to_item (ArdourCanvas::Rect const & d) const
+Item::canvas_to_item (ArdourCanvas::Rect const & r) const
 {
-       Item const * i = this;
-       Duple offset;
-
-       while (i) {
-               offset = offset.translate (-(i->position()));
-               i = i->parent();
-       }
-
-       return d.translate (offset);
+       return r.translate (-position_offset());
 }
 
 void
@@ -159,22 +195,43 @@ Item::canvas_to_item (Coord& x, Coord& y) const
        y = d.y;
 }
 
+
 Duple
-Item::item_to_window (ArdourCanvas::Duple const & d) const
+Item::item_to_window (ArdourCanvas::Duple const & d, bool rounded) const
 {
-       return _canvas->canvas_to_window (item_to_canvas (d));
+       Duple ret = item_to_canvas (d).translate (-scroll_offset());
+
+       if (rounded) {
+               ret.x = round (ret.x);
+               ret.y = round (ret.y);
+       }
+
+       return ret;
 }
 
 Duple
 Item::window_to_item (ArdourCanvas::Duple const & d) const
 {
-       return _canvas->window_to_canvas (canvas_to_item (d));
+       return canvas_to_item (d.translate (scroll_offset()));
 }
 
 ArdourCanvas::Rect
 Item::item_to_window (ArdourCanvas::Rect const & r) const
 {
-       return _canvas->canvas_to_window (item_to_canvas (r));
+       Rect ret = item_to_canvas (r).translate (-scroll_offset());
+
+       ret.x0 = round (ret.x0);
+       ret.x1 = round (ret.x1);
+       ret.y0 = round (ret.y0);
+       ret.y1 = round (ret.y1);
+
+       return ret;
+}
+
+ArdourCanvas::Rect
+Item::window_to_item (ArdourCanvas::Rect const & r) const
+{
+       return canvas_to_item (r.translate (scroll_offset()));
 }
 
 /** Set the position of this item in the parent's coordinates */
@@ -219,22 +276,25 @@ Item::set_y_position (Coord y)
 void
 Item::raise_to_top ()
 {
-       assert (_parent);
-       _parent->raise_child_to_top (this);
+       if (_parent) {
+               _parent->raise_child_to_top (this);
+       }
 }
 
 void
 Item::raise (int levels)
 {
-       assert (_parent);
-       _parent->raise_child (this, levels);
+       if (_parent) {
+               _parent->raise_child (this, levels);
+       }
 }
 
 void
 Item::lower_to_bottom ()
 {
-       assert (_parent);
-       _parent->lower_child_to_bottom (this);
+       if (_parent) {
+               _parent->lower_child_to_bottom (this);
+       }
 }
 
 void
@@ -242,6 +302,15 @@ Item::hide ()
 {
        if (_visible) {
                _visible = false;
+
+               /* recompute parent bounding box, which may alter now that this
+                * child is hidden.
+                */
+
+               if (_parent) {
+                       _parent->child_changed ();
+               }
+
                _canvas->item_shown_or_hidden (this);
        }
 }
@@ -251,6 +320,13 @@ Item::show ()
 {
        if (!_visible) {
                _visible = true;
+
+               /* bounding box may have changed while we were hidden */
+
+               if (_parent) {
+                       _parent->child_changed ();
+               }
+
                _canvas->item_shown_or_hidden (this);
        }
 }
@@ -277,12 +353,17 @@ void
 Item::unparent ()
 {
        _parent = 0;
+       _scroll_parent = 0;
 }
 
 void
-Item::reparent (Group* new_parent)
+Item::reparent (Item* new_parent)
 {
-       assert (_canvas == _parent->canvas());
+       if (new_parent == _parent) {
+               return;
+       }
+
+       assert (_canvas == new_parent->canvas());
 
        if (_parent) {
                _parent->remove (this);
@@ -292,9 +373,34 @@ Item::reparent (Group* new_parent)
 
        _parent = new_parent;
        _canvas = _parent->canvas ();
+
+       find_scroll_parent ();
+
        _parent->add (this);
 }
 
+void
+Item::find_scroll_parent ()
+{
+       Item const * i = this;
+       ScrollGroup const * last_scroll_group = 0;
+
+       /* Don't allow a scroll group to find itself as its own scroll parent
+        */
+
+       i = i->parent ();
+
+       while (i) {
+               ScrollGroup const * sg = dynamic_cast<ScrollGroup const *> (i);
+               if (sg) {
+                       last_scroll_group = sg;
+               }
+               i = i->parent();
+       }
+       
+       _scroll_parent = const_cast<ScrollGroup*> (last_scroll_group);
+}
+
 bool
 Item::common_ancestor_within (uint32_t limit, const Item& other) const
 {
@@ -309,10 +415,16 @@ Item::common_ancestor_within (uint32_t limit, const Item& other) const
 
        while (d1 != d2) {
                if (d1 > d2) {
+                       if (!i1) {
+                               return false;
+                       }
                        i1 = i1->parent();
                        d1--;
                        limit--;
                } else {
+                       if (!i2) {
+                               return false;
+                       }
                        i2 = i2->parent();
                        d2--;
                        limit--;
@@ -355,9 +467,15 @@ Item::closest_ancestor_with (const Item& other) const
 
        while (d1 != d2) {
                if (d1 > d2) {
+                       if (!i1) {
+                               return 0;
+                       }
                        i1 = i1->parent();
                        d1--;
                } else {
+                       if (!i2) {
+                               return 0;
+                       }
                        i2 = i2->parent();
                        d2--;
                }
@@ -405,6 +523,7 @@ Item::bounding_box () const
        if (_bounding_box_dirty) {
                compute_bounding_box ();
                assert (!_bounding_box_dirty);
+               add_child_bounding_boxes ();
        }
 
        return _bounding_box;
@@ -437,7 +556,7 @@ void
 Item::redraw () const
 {
        if (_visible && _bounding_box && _canvas) {
-               _canvas->request_redraw (item_to_canvas (_bounding_box.get()));
+               _canvas->request_redraw (item_to_window (_bounding_box.get()));
        }
 }      
 
@@ -450,10 +569,12 @@ Item::begin_change ()
 void
 Item::end_change ()
 {
-       _canvas->item_changed (this, _pre_change_bounding_box);
-       
-       if (_parent) {
-               _parent->child_changed ();
+       if (_visible) {
+               _canvas->item_changed (this, _pre_change_bounding_box);
+               
+               if (_parent) {
+                       _parent->child_changed ();
+               }
        }
 }
 
@@ -465,7 +586,9 @@ Item::begin_visual_change ()
 void
 Item::end_visual_change ()
 {
-       _canvas->item_visual_property_changed (this);
+       if (_visible) {
+               _canvas->item_visual_property_changed (this);
+       }
 }
 
 void
@@ -511,30 +634,6 @@ Item::set_ignore_events (bool ignore)
        _ignore_events = ignore;
 }
 
-void
-Item::dump (ostream& o) const
-{
-       boost::optional<ArdourCanvas::Rect> bb = bounding_box();
-
-       o << _canvas->indent() << whatami() << ' ' << this << " Visible ? " << _visible;
-       o << " @ " << position();
-       
-#ifdef CANVAS_DEBUG
-       if (!name.empty()) {
-               o << ' ' << name;
-       }
-#endif
-
-       if (bb) {
-               o << endl << _canvas->indent() << "\tbbox: " << bb.get();
-               o << endl << _canvas->indent() << "\tCANVAS bbox: " << item_to_canvas (bb.get());
-       } else {
-               o << " bbox unset";
-       }
-
-       o << endl;
-}
-
 std::string
 Item::whatami () const 
 {
@@ -557,7 +656,7 @@ Item::depth () const
 bool
 Item::covers (Duple const & point) const
 {
-       Duple p = canvas_to_item (point);
+       Duple p = window_to_item (point);
 
        if (_bounding_box_dirty) {
                compute_bounding_box ();
@@ -572,6 +671,369 @@ Item::covers (Duple const & point) const
        return r.get().contains (p);
 }
 
+/* nesting/grouping API */
+
+void
+Item::render_children (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
+{
+       if (_items.empty()) {
+               return;
+       }
+
+       ensure_lut ();
+       std::vector<Item*> items = _lut->get (area);
+
+#ifdef CANVAS_DEBUG
+       if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
+               cerr << string_compose ("%1%7 %2 @ %7 render %5 @ %6 %3 items out of %4\n", 
+                                       _canvas->render_indent(), (name.empty() ? string ("[unnamed]") : name), items.size(), _items.size(), area, _position, this,
+                                       whatami());
+       }
+#endif
+
+       ++render_depth;
+               
+       for (std::vector<Item*>::const_iterator i = items.begin(); i != items.end(); ++i) {
+
+               if (!(*i)->visible ()) {
+#ifdef CANVAS_DEBUG
+                       if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
+                               cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] invisible - skipped\n";
+                       }
+#endif
+                       continue;
+               }
+               
+               boost::optional<Rect> item_bbox = (*i)->bounding_box ();
+
+               if (!item_bbox) {
+#ifdef CANVAS_DEBUG
+                       if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
+                               cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] empty - skipped\n";
+                       }
+#endif
+                       continue;
+               }
+               
+               Rect item = (*i)->item_to_window (item_bbox.get());
+               boost::optional<Rect> d = item.intersection (area);
+               
+               if (d) {
+                       Rect draw = d.get();
+                       if (draw.width() && draw.height()) {
+#ifdef CANVAS_DEBUG
+                               if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
+                                       if (dynamic_cast<Container*>(*i) == 0) {
+                                               cerr << _canvas->render_indent() << "render "
+                                                    << ' ' 
+                                                    << (*i)
+                                                    << ' '
+                                                    << (*i)->whatami()
+                                                    << ' '
+                                                    << (*i)->name
+                                                    << " item "
+                                                    << item_bbox.get()
+                                                    << " window = " 
+                                                    << item
+                                                    << " intersect = "
+                                                    << draw
+                                                    << " @ " 
+                                                    << _position
+                                                    << endl;
+                                       }
+                               }
+#endif
+
+                               (*i)->render (area, context);
+                               ++render_count;
+                       }
+
+               } else {
+
+#ifdef CANVAS_DEBUG
+                       if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
+                               cerr << string_compose ("%1skip render of %2 %3, no intersection between %4 and %5\n", _canvas->render_indent(), (*i)->whatami(),
+                                                       (*i)->name, item, area);
+                       }
+#endif
+
+               }
+       }
+
+       --render_depth;
+}
+
+void
+Item::add_child_bounding_boxes() const
+{
+       boost::optional<Rect> self;
+       Rect bbox;
+       bool have_one = false;
+
+       if (_bounding_box) {
+               bbox = _bounding_box.get();
+               have_one = true;
+       }
+
+       for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
+
+               boost::optional<Rect> item_bbox = (*i)->bounding_box ();
+
+               if (!item_bbox) {
+                       continue;
+               }
+
+               Rect group_bbox = (*i)->item_to_parent (item_bbox.get ());
+               if (have_one) {
+                       bbox = bbox.extend (group_bbox);
+               } else {
+                       bbox = group_bbox;
+                       have_one = true;
+               }
+       }
+
+       if (!have_one) {
+               _bounding_box = boost::optional<Rect> ();
+       } else {
+               _bounding_box = bbox;
+       }
+}
+
+void
+Item::add (Item* i)
+{
+       /* XXX should really notify canvas about this */
+
+       _items.push_back (i);
+       i->reparent (this);
+       invalidate_lut ();
+       _bounding_box_dirty = true;
+}
+
+void
+Item::remove (Item* i)
+{
+
+       if (i->parent() != this) {
+               return;
+       }
+
+       /* we cannot call bounding_box() here because that will iterate over
+          _items, one of which (the argument, i) may be in the middle of
+          deletion, making it impossible to call compute_bounding_box()
+          on it.
+       */
+
+       if (_bounding_box) {
+               _pre_change_bounding_box = _bounding_box;
+       } else {
+               _pre_change_bounding_box = Rect();
+       }
+
+       i->unparent ();
+       _items.remove (i);
+       invalidate_lut ();
+       _bounding_box_dirty = true;
+       
+       end_change ();
+}
+
+void
+Item::clear (bool with_delete)
+{
+       begin_change ();
+
+       clear_items (with_delete);
+
+       invalidate_lut ();
+       _bounding_box_dirty = true;
+
+       end_change ();
+}
+
+void
+Item::clear_items (bool with_delete)
+{
+       for (list<Item*>::iterator i = _items.begin(); i != _items.end(); ) {
+
+               list<Item*>::iterator tmp = i;
+               Item *item = *i;
+
+               ++tmp;
+
+               /* remove from list before doing anything else, because we
+                * don't want to find the item in _items during any activity
+                * driven by unparent-ing or deletion.
+                */
+
+               _items.erase (i);
+               item->unparent ();
+               
+               if (with_delete) {
+                       delete item;
+               }
+
+               i = tmp;
+       }
+}
+
+void
+Item::raise_child_to_top (Item* i)
+{
+       if (!_items.empty()) {
+               if (_items.back() == i) {
+                       return;
+               }
+       }
+
+       _items.remove (i);
+       _items.push_back (i);
+       invalidate_lut ();
+}
+
+void
+Item::raise_child (Item* i, int levels)
+{
+       list<Item*>::iterator j = find (_items.begin(), _items.end(), i);
+       assert (j != _items.end ());
+
+       ++j;
+       _items.remove (i);
+
+       while (levels > 0 && j != _items.end ()) {
+               ++j;
+               --levels;
+       }
+
+       _items.insert (j, i);
+       invalidate_lut ();
+}
+
+void
+Item::lower_child_to_bottom (Item* i)
+{
+       if (!_items.empty()) {
+               if (_items.front() == i) {
+                       return;
+               }
+       }
+       _items.remove (i);
+       _items.push_front (i);
+       invalidate_lut ();
+}
+
+void
+Item::ensure_lut () const
+{
+       if (!_lut) {
+               _lut = new DumbLookupTable (*this);
+       }
+}
+
+void
+Item::invalidate_lut () const
+{
+       delete _lut;
+       _lut = 0;
+}
+
+void
+Item::child_changed ()
+{
+       invalidate_lut ();
+       _bounding_box_dirty = true;
+
+       if (_parent) {
+               _parent->child_changed ();
+       }
+}
+
+void
+Item::add_items_at_point (Duple const point, vector<Item const *>& items) const
+{
+       boost::optional<Rect> const bbox = bounding_box ();
+
+       /* Point is in window coordinate system */
+
+       if (!bbox || !item_to_window (bbox.get()).contains (point)) {
+               return;
+       }
+
+       /* recurse and add any items within our group that contain point.
+          Our children are only considered visible if we are, and similarly
+          only if we do not ignore events.
+       */
+
+       vector<Item*> our_items;
+
+       if (!_items.empty() && visible() && !_ignore_events) {
+               ensure_lut ();
+               our_items = _lut->items_at_point (point);
+       }
+
+       if (!our_items.empty() || covers (point)) {
+               /* this adds this item itself to the list of items at point */
+               items.push_back (this);
+       }
+
+       for (vector<Item*>::iterator i = our_items.begin(); i != our_items.end(); ++i) {
+               (*i)->add_items_at_point (point, items);
+       }
+}
+
+void
+Item::dump (ostream& o) const
+{
+       boost::optional<ArdourCanvas::Rect> bb = bounding_box();
+
+       o << _canvas->indent() << whatami() << ' ' << this << " Visible ? " << _visible;
+       o << " @ " << position();
+       
+#ifdef CANVAS_DEBUG
+       if (!name.empty()) {
+               o << ' ' << name;
+       }
+#endif
+
+       if (bb) {
+               o << endl << _canvas->indent() << "\tbbox: " << bb.get();
+               o << endl << _canvas->indent() << "\tCANVAS bbox: " << item_to_canvas (bb.get());
+       } else {
+               o << " bbox unset";
+       }
+
+       o << endl;
+
+       if (!_items.empty()) {
+
+#ifdef CANVAS_DEBUG
+               o << _canvas->indent();
+               o << " @ " << position();
+               o << " Items: " << _items.size();
+               o << " Visible ? " << _visible;
+               
+               boost::optional<Rect> bb = bounding_box();
+               
+               if (bb) {
+                       o << endl << _canvas->indent() << "  bbox: " << bb.get();
+                       o << endl << _canvas->indent() << "  CANVAS bbox: " << item_to_canvas (bb.get());
+               } else {
+                       o << "  bbox unset";
+               }
+               
+               o << endl;
+#endif
+               
+               ArdourCanvas::dump_depth++;
+               
+               for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
+                       o << **i;
+               }
+               
+               ArdourCanvas::dump_depth--;
+       }
+}
+
 ostream&
 ArdourCanvas::operator<< (ostream& o, const Item& i)
 {
index 8f04e2b27878699345feb26f2896e27095fe0aa4..8bd26b90678dfc49626169acbb7cab5b3fa44ea7 100644 (file)
 using namespace std;
 using namespace ArdourCanvas;
 
-Line::Line (Group* parent)
-       : Item (parent)
-       , Outline (parent)
+Line::Line (Canvas* c)
+       : Item (c)
 {
+}
 
+Line::Line (Item* parent)
+       : Item (parent)
+{
 }
 
 void
@@ -159,7 +162,7 @@ Line::set_y1 (Coord y1)
 bool
 Line::covers (Duple const & point) const
 {
-       const Duple p = canvas_to_item (point);
+       const Duple p = window_to_item (point);
        static const Distance threshold = 2.0;
 
        /* this quick check works for vertical and horizontal lines, which are
@@ -180,7 +183,7 @@ Line::covers (Duple const & point) const
        double t;
        Duple a (_points[0]);
        Duple b (_points[1]);
-       const Rect visible (_canvas->visible_area());
+       const Rect visible (window_to_item (_canvas->visible_area()));
 
        /*
           Clamp the line endpoints to the visible area of the canvas. If we do
index 1625e0478dd80cee7fe0e9f15b41664330242648..54fe980b1c03ffbb55aa6616e5bbb1a253f1986b 100644 (file)
 using namespace std;
 using namespace ArdourCanvas;
 
-/* XXX: hard-wired to horizontal only */
-
 class LineSorter {
 public:
-       bool operator() (LineSet::Line& a, LineSet::Line& b) {
-               return a.y < b.y;
+       bool operator() (LineSet::Line const & a, LineSet::Line const & b) {
+               return a.pos < b.pos;
        }
 };
 
-LineSet::LineSet (Group* parent)
-       : Item (parent)
-       , _height (0)
+LineSet::LineSet (Canvas* c, Orientation o)
+       : Item (c)
+       , _extent (0)
+       , _orientation (o)
 {
 
 }
 
+LineSet::LineSet (Item* parent, Orientation o)
+       : Item (parent)
+       , _extent (0)
+       , _orientation (o)
+{
+
+}
 
 void
 LineSet::compute_bounding_box () const
@@ -46,19 +52,36 @@ LineSet::compute_bounding_box () const
        if (_lines.empty ()) {
                _bounding_box = boost::optional<Rect> ();
        } else {
-               _bounding_box = Rect (0, _lines.front().y - (_lines.front().width/2.0), COORD_MAX, min (_height, _lines.back().y - (_lines.back().width/2.0)));
+
+               if (_orientation == Horizontal) {
+                       
+                       _bounding_box = Rect (0, /* x0 */
+                                             _lines.front().pos - (_lines.front().width/2.0), /* y0 */
+                                             _extent, /* x1 */
+                                             _lines.back().pos - (_lines.back().width/2.0) /* y1 */
+                               );
+
+               } else {
+                       
+                       _bounding_box = Rect (_lines.front().pos - _lines.front().width/2.0, /* x0 */
+                                             0, /* y0 */
+                                             _lines.back().pos + _lines.back().width/2.0, /* x1 */
+                                             _extent /* y1 */
+                               );
+               }
        }
+
        _bounding_box_dirty = false;
 }
 
 void
-LineSet::set_height (Distance height)
+LineSet::set_extent (Distance e)
 {
        begin_change ();
 
-       _height = height;
-
+       _extent = e;
        _bounding_box_dirty = true;
+
        end_change ();
 }
 
@@ -67,19 +90,42 @@ LineSet::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
 {
        /* area is in window coordinates */
 
-       for (list<Line>::const_iterator i = _lines.begin(); i != _lines.end(); ++i) {
+       for (vector<Line>::const_iterator i = _lines.begin(); i != _lines.end(); ++i) {
+
+               Rect self;
+
+               if (_orientation == Horizontal) {
+                       self = item_to_window (Rect (0, i->pos - (i->width/2.0), _extent, i->pos + (i->width/2.0)));
+               } else {
+                       self = item_to_window (Rect (i->pos - (i->width/2.0), 0, i->pos + (i->width/2.0), _extent));
+               }
 
-               Rect self = item_to_window (Rect (0, i->y - (i->width/2.0), COORD_MAX, i->y + (i->width/2.0)));
-               boost::optional<Rect> intersect = self.intersection (area);
+               boost::optional<Rect> isect = self.intersection (area);
                        
-               if (!intersect) {
+               if (!isect) {
                        continue;       
                }
 
+               Rect intersection (isect.get());
+
                set_source_rgba (context, i->color);
                context->set_line_width (i->width);
-               context->move_to (intersect->x0, self.y0 + ((self.y1 - self.y0)/2.0));
-               context->line_to (intersect->x1, self.y0 + ((self.y1 - self.y0)/2.0));
+
+               /* Not 100% sure that the computation of the invariant
+                * positions (y and x) below work correctly if the line width
+                * is not 1.0, but visual inspection suggests it is OK.
+                */
+
+               if (_orientation == Horizontal) {
+                       double y = self.y0 + ((self.y1 - self.y0)/2.0);
+                       context->move_to (intersection.x0, y);
+                       context->line_to (intersection.x1, y);
+               } else {
+                       double x = self.x0 + ((self.x1 - self.x0)/2.0);
+                       context->move_to (x, intersection.y0);
+                       context->line_to (x, intersection.y1);
+               }
+
                context->stroke ();
        }
 }
@@ -90,7 +136,7 @@ LineSet::add (Coord y, Distance width, Color color)
        begin_change ();
        
        _lines.push_back (Line (y, width, color));
-       _lines.sort (LineSorter ());
+       sort (_lines.begin(), _lines.end(), LineSorter());
 
        _bounding_box_dirty = true;
        end_change ();
@@ -108,5 +154,11 @@ LineSet::clear ()
 bool
 LineSet::covers (Duple const & /*point*/) const
 {
+       /* lines are ordered by position along primary axis, so binary search
+        * to find the place to start looking.
+        *
+        * XXX but not yet.
+        */
+
        return false;
 }
index f88531537aebb0544d85a792f6c749e8e9199dad..2396f596354504798f0cdbc625da9c3c8bccd0b5 100644 (file)
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
+#include "canvas/item.h"
 #include "canvas/lookup_table.h"
-#include "canvas/group.h"
 
 using namespace std;
 using namespace ArdourCanvas;
 
-LookupTable::LookupTable (Group const & group)
-       : _group (group)
+LookupTable::LookupTable (Item const & item)
+       : _item (item)
 {
 
 }
@@ -34,8 +34,8 @@ LookupTable::~LookupTable ()
 
 }
 
-DumbLookupTable::DumbLookupTable (Group const & group)
-       : LookupTable (group)
+DumbLookupTable::DumbLookupTable (Item const & item)
+       : LookupTable (item)
 {
 
 }
@@ -43,7 +43,7 @@ DumbLookupTable::DumbLookupTable (Group const & group)
 vector<Item *>
 DumbLookupTable::get (Rect const &)
 {
-       list<Item *> const & items = _group.items ();
+       list<Item *> const & items = _item.items ();
        vector<Item *> vitems;
        copy (items.begin(), items.end(), back_inserter (vitems));
        return vitems;
@@ -52,9 +52,9 @@ DumbLookupTable::get (Rect const &)
 vector<Item *>
 DumbLookupTable::items_at_point (Duple const & point) const
 {
-       /* Point is in canvas coordinate system */
+       /* Point is in window coordinate system */
 
-       list<Item *> const & items (_group.items ());
+       list<Item *> const & items (_item.items ());
        vector<Item *> vitems;
 
        for (list<Item *>::const_iterator i = items.begin(); i != items.end(); ++i) {
@@ -71,9 +71,9 @@ DumbLookupTable::items_at_point (Duple const & point) const
 bool
 DumbLookupTable::has_item_at_point (Duple const & point) const
 {
-       /* Point is in canvas coordinate system */
+       /* Point is in window coordinate system */
 
-       list<Item *> const & items (_group.items ());
+       list<Item *> const & items (_item.items ());
        vector<Item *> vitems;
 
        for (list<Item *>::const_iterator i = items.begin(); i != items.end(); ++i) {
@@ -92,12 +92,12 @@ DumbLookupTable::has_item_at_point (Duple const & point) const
        return false;
 }
 
-OptimizingLookupTable::OptimizingLookupTable (Group const & group, int items_per_cell)
-       : LookupTable (group)
+OptimizingLookupTable::OptimizingLookupTable (Item const & item, int items_per_cell)
+       : LookupTable (item)
        , _items_per_cell (items_per_cell)
        , _added (false)
 {
-       list<Item*> const & items = _group.items ();
+       list<Item*> const & items = _item.items ();
 
        /* number of cells */
        int const cells = items.size() / _items_per_cell;
@@ -109,8 +109,8 @@ OptimizingLookupTable::OptimizingLookupTable (Group const & group, int items_per
                _cells[i] = new Cell[_dimension];
        }
 
-       /* our group's bounding box in its coordinates */
-       boost::optional<Rect> bbox = _group.bounding_box ();
+       /* our item's bounding box in its coordinates */
+       boost::optional<Rect> bbox = _item.bounding_box ();
        if (!bbox) {
                return;
        }
@@ -130,11 +130,11 @@ OptimizingLookupTable::OptimizingLookupTable (Group const & group, int items_per
                        continue;
                }
 
-               /* and in the group's coordinates */
-               Rect const item_bbox_in_group = (*i)->item_to_parent (item_bbox.get ());
+               /* and in the item's coordinates */
+               Rect const item_bbox_in_item = (*i)->item_to_parent (item_bbox.get ());
 
                int x0, y0, x1, y1;
-               area_to_indices (item_bbox_in_group, x0, y0, x1, y1);
+               area_to_indices (item_bbox_in_item, x0, y0, x1, y1);
 
                /* XXX */
                assert (x0 >= 0);
@@ -147,19 +147,19 @@ OptimizingLookupTable::OptimizingLookupTable (Group const & group, int items_per
                //assert (y1 <= _dimension);
 
                if (x0 > _dimension) {
-                       cout << "WARNING: item outside bbox by " << (item_bbox_in_group.x0 - bbox.get().x0) << "\n";
+                       cout << "WARNING: item outside bbox by " << (item_bbox_in_item.x0 - bbox.get().x0) << "\n";
                        x0 = _dimension;
                }
                if (x1 > _dimension) {
-                       cout << "WARNING: item outside bbox by " << (item_bbox_in_group.x1 - bbox.get().x1) << "\n";
+                       cout << "WARNING: item outside bbox by " << (item_bbox_in_item.x1 - bbox.get().x1) << "\n";
                        x1 = _dimension;
                }
                if (y0 > _dimension) {
-                       cout << "WARNING: item outside bbox by " << (item_bbox_in_group.y0 - bbox.get().y0) << "\n";
+                       cout << "WARNING: item outside bbox by " << (item_bbox_in_item.y0 - bbox.get().y0) << "\n";
                        y0 = _dimension;
                }
                if (y1 > _dimension) {
-                       cout << "WARNING: item outside bbox by " << (item_bbox_in_group.y1 - bbox.get().y1) << "\n";
+                       cout << "WARNING: item outside bbox by " << (item_bbox_in_item.y1 - bbox.get().y1) << "\n";
                        y1 = _dimension;
                }
 
@@ -284,7 +284,7 @@ OptimizingLookupTable::has_item_at_point (Duple const & point) const
        return false;
 }
        
-/** @param area Area in our owning group's coordinates */
+/** @param area Area in our owning item's coordinates */
 vector<Item*>
 OptimizingLookupTable::get (Rect const & area)
 {
index be8b924df25a5b3a0f01858b1635709a7f926612..9416859e59b0a8ef9269c042b211076f01731884 100644 (file)
 #include "pbd/convert.h"
 
 #include "ardour/utils.h"
+
+#include "canvas/item.h"
 #include "canvas/outline.h"
 #include "canvas/utils.h"
 #include "canvas/debug.h"
 
 using namespace ArdourCanvas;
 
-Outline::Outline (Group* parent)
-       : Item (parent)
+Outline::Outline (Item& self)
+       : _self (self)
        , _outline_color (0x000000ff)
        , _outline_width (1.0)
        , _outline (true)
 {
-
 }
 
 void
 Outline::set_outline_color (Color color)
 {
        if (color != _outline_color) {
-               begin_visual_change ();
+               _self.begin_visual_change ();
                _outline_color = color;
-               end_visual_change ();
+               _self.end_visual_change ();
        }
 }
 
@@ -52,10 +53,10 @@ void
 Outline::set_outline_width (Distance width)
 {
        if (width != _outline_width) {
-               begin_change ();
+               _self.begin_change ();
                _outline_width = width;
-               _bounding_box_dirty = true;
-               end_change ();
+               _self._bounding_box_dirty = true;
+               _self.end_change ();
        }
 }
 
@@ -63,10 +64,10 @@ void
 Outline::set_outline (bool outline)
 {
        if (outline != _outline) {
-               begin_change ();
+               _self.begin_change ();
                _outline = outline;
-               _bounding_box_dirty = true;
-               end_change ();
+               _self._bounding_box_dirty = true;
+               _self.end_change ();
        }
 }
 
index 62d9357c61ddde4f93a67459beed0da8b516872d..d285c41b10e8ccd850f983d35c4cf113e5730739 100644 (file)
 using namespace std;
 using namespace ArdourCanvas;
 
-Pixbuf::Pixbuf (Group* g)
-       : Item (g)
+Pixbuf::Pixbuf (Canvas* c)
+       : Item (c)
+{
+}
+
+Pixbuf::Pixbuf (Item* parent)
+       : Item (parent)
 {
-       
 }
 
 void
index 0d3369f70b0a822ba94eaa359027aa75b6c7dd65..c66c3bbb7a34e81f53ca7fd353fa5147bbc0d33b 100644 (file)
 using namespace std;
 using namespace ArdourCanvas;
 
-PolyItem::PolyItem (Group* parent)
-       : Item (parent)
-       , Outline (parent)
+PolyItem::PolyItem (Canvas* c)
+       : Item (c)
 {
+}
 
+PolyItem::PolyItem (Item* parent)
+       : Item (parent)
+{
 }
 
 void
@@ -56,7 +59,7 @@ PolyItem::compute_bounding_box () const
                        ++i;
                }
 
-               _bounding_box = bbox.expand (_outline_width);
+               _bounding_box = bbox.expand (_outline_width + 0.5);
 
                
        } else {
index ae6d15a8fdceb25d0d81eda7ec3b7618f1a3de50..60bca6bccf87011de9afbc28f6a12d679d49184f 100644 (file)
 
 using namespace ArdourCanvas;
 
-PolyLine::PolyLine (Group* parent)
-       : Item (parent)
-       , PolyItem (parent)
+PolyLine::PolyLine (Canvas* c)
+       : PolyItem (c)
        , _threshold (1.0)
 {
+}
 
+PolyLine::PolyLine (Item* parent)
+       : PolyItem (parent)
+       , _threshold (1.0)
+{
 }
 
 void
@@ -46,7 +50,7 @@ PolyLine::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) cons
 bool
 PolyLine::covers (Duple const & point) const
 {
-       Duple p = canvas_to_item (point);
+       Duple p = window_to_item (point);
 
        const Points::size_type npoints = _points.size();
        
@@ -59,7 +63,7 @@ PolyLine::covers (Duple const & point) const
 
        /* repeat for each line segment */
 
-       const Rect visible (_canvas->visible_area());
+       const Rect visible (window_to_item (_canvas->visible_area()));
 
        for (i = 1, j = 0; i < npoints; ++i, ++j) {
 
index 9352e900e9ab91ea36b017e67c4e81d5f5653913..aa16a6017844c2cc6f3ba816eabac5b8ecd5f08d 100644 (file)
 
 using namespace ArdourCanvas;
 
-Polygon::Polygon (Group* parent)
-       : Item (parent)
-       , PolyItem (parent)
-       , Fill (parent)
+Polygon::Polygon (Canvas* c)
+       : PolyItem (c)
        , multiple (0)
        , constant (0)
        , cached_size (0)
 {
+}
 
+Polygon::Polygon (Item* parent)
+       : PolyItem (parent)
+       , multiple (0)
+       , constant (0)
+       , cached_size (0)
+{
 }
 
 Polygon::~Polygon ()
@@ -103,7 +108,7 @@ Polygon::cache_shape_computation () const
 bool 
 Polygon::covers (Duple const & point) const
 {
-       Duple p = canvas_to_item (point);
+       Duple p = window_to_item (point);
 
        Points::size_type npoints = _points.size();
 
index 57c26874c454de36fb256c46f3b3f75cd7bc751b..bc4ad0c9609862e34903d251a6b2fe458d33af65 100644 (file)
 using namespace std;
 using namespace ArdourCanvas;
 
-Rectangle::Rectangle (Group* parent)
+Rectangle::Rectangle (Canvas* c)
+       : Item (c)
+       , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM))
+{
+}
+
+Rectangle::Rectangle (Canvas* c, Rect const & rect)
+       : Item (c)
+       , _rect (rect)
+       , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM))
+{
+}
+
+Rectangle::Rectangle (Item* parent)
        : Item (parent)
-       , Outline (parent)
-       , Fill (parent)
        , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM))
 {
 }
 
-Rectangle::Rectangle (Group* parent, Rect const & rect)
+Rectangle::Rectangle (Item* parent, Rect const & rect)
        : Item (parent)
-       , Outline (parent)
-       , Fill (parent)
        , _rect (rect)
        , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM))
 {
-       
 }
 
 void
 Rectangle::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
 {
-       Rect self = item_to_window (_rect);
+       /* In general, a Rectangle will have a _position of (0,0) within its
+          parent, and its extent is actually defined by _rect. But in the
+          unusual case that _position is set to something other than (0,0),
+          we should take that into account when rendering.
+       */
+       Rect self = item_to_window (_rect.translate (_position));
        boost::optional<Rect> r = self.intersection (area);
 
        if (!r) {
index cded570b2c900151ffd31ddb54b05a337bd58370..accbcb9b3030775a09945f5f7b0cb28df16f94b1 100644 (file)
@@ -23,8 +23,8 @@
 using namespace std;
 using namespace ArdourCanvas;
 
-RootGroup::RootGroup (Canvas* canvas)
-       : Group (canvas)
+Root::Root (Canvas* canvas)
+       : Container (canvas)
 {
 #ifdef CANVAS_DEBUG
        name = "ROOT";
@@ -32,11 +32,12 @@ RootGroup::RootGroup (Canvas* canvas)
 }
 
 void
-RootGroup::compute_bounding_box () const
+Root::compute_bounding_box () const
 {
-       Group::compute_bounding_box ();
+       Container::compute_bounding_box ();
 
        if (_bounding_box) {
-               _canvas->request_size (Duple (_bounding_box.get().width (), _bounding_box.get().height ()));
+               Rect r (_bounding_box.get());
+               _canvas->request_size (Duple (r.width (), r.height ()));
        }
 }
diff --git a/libs/canvas/ruler.cc b/libs/canvas/ruler.cc
new file mode 100644 (file)
index 0000000..120ba84
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+    Copyright (C) 2014 Paul Davis
+    Author: Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <algorithm>
+#include <cairomm/context.h>
+
+#include <pangomm/layout.h>
+
+#include "canvas/ruler.h"
+#include "canvas/types.h"
+#include "canvas/debug.h"
+#include "canvas/utils.h"
+#include "canvas/canvas.h"
+
+using namespace std;
+using namespace ArdourCanvas;
+
+Ruler::Ruler (Canvas* c, const Metric& m)
+       : Rectangle (c)
+       , _metric (m)
+       , _lower (0)
+       , _upper (0)
+       , _need_marks (true)
+{
+}
+
+Ruler::Ruler (Canvas* c, const Metric& m, Rect const& r)
+       : Rectangle (c, r)
+       , _metric (m)
+       , _lower (0)
+       , _upper (0)
+       , _need_marks (true)
+{
+}
+
+Ruler::Ruler (Item* parent, const Metric& m)
+       : Rectangle (parent)
+       , _metric (m)
+       , _lower (0)
+       , _upper (0)
+       , _need_marks (true)
+{
+}
+
+Ruler::Ruler (Item* parent, const Metric& m, Rect const& r)
+       : Rectangle (parent, r)
+       , _metric (m)
+       , _lower (0)
+       , _upper (0)
+       , _need_marks (true)
+{
+}
+
+void
+Ruler::set_range (double l, double u)
+{
+       begin_visual_change ();
+       _lower = l;
+       _upper = u;
+       _need_marks = true;
+       end_visual_change ();
+}
+
+void
+Ruler::set_font_description (Pango::FontDescription fd)
+{
+       begin_visual_change ();
+       _font_description = new Pango::FontDescription (fd);
+       end_visual_change ();
+}
+
+void
+Ruler::render (Rect const & area, Cairo::RefPtr<Cairo::Context> cr) const
+{
+       if (_lower == _upper) {
+               /* nothing to draw */
+               return;
+       }
+
+       Rect self (item_to_window (get()));
+       boost::optional<Rect> i = self.intersection (area);
+
+       if (!i) {
+               return;
+       }
+
+       Rect intersection (i.get());
+
+       Distance height = self.height();
+
+       if (_need_marks) {
+               marks.clear ();
+               _metric.get_marks (marks, _lower, _upper, 50);
+               _need_marks = false;
+       }
+
+       /* draw background */
+
+       setup_fill_context (cr);
+       cr->rectangle (intersection.x0, intersection.y0, intersection.width(), intersection.height());
+       cr->fill ();
+
+       /* switch to outline context */
+
+       setup_outline_context (cr);
+
+       /* draw line on lower edge as a separator */
+
+       if (_outline_width == 1.0) {
+               /* Cairo single pixel line correction */
+               cr->move_to (self.x0, self.y1-0.5);
+               cr->line_to (self.x1, self.y1-0.5);
+       } else {
+               cr->move_to (self.x0, self.y1);
+               cr->line_to (self.x1, self.y1);
+       }
+       cr->stroke ();
+
+       /* draw ticks + text */
+       
+       Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create (cr);
+       if (_font_description) {
+               layout->set_font_description (*_font_description);
+       }
+
+       for (vector<Mark>::const_iterator m = marks.begin(); m != marks.end(); ++m) {
+               Duple pos;
+
+               pos.x = floor ((m->position - _lower) / _metric.units_per_pixel);
+               pos.y = self.y1; /* bottom edge */
+
+               if (_outline_width == 1.0) {
+                       /* Cairo single pixel line correction */
+                       cr->move_to (pos.x + 0.5, pos.y);
+               } else {
+                       cr->move_to (pos.x, pos.y);
+               }
+               
+               switch (m->style) {
+               case Mark::Major:
+                       cr->rel_line_to (0, -height);
+                       break;
+               case Mark::Minor:
+                       cr->rel_line_to (0, -height/2.0);
+                       break;
+               case Mark::Micro:
+                       cr->rel_line_to (0, -height/4.0);
+                       break;
+               }
+               cr->stroke ();
+
+               /* and the text */
+
+               if (!m->label.empty()) {
+                       Pango::Rectangle logical;
+
+                       layout->set_text (m->label);
+                       logical = layout->get_pixel_logical_extents ();
+                       
+                       cr->move_to (pos.x + 2.0, self.y0 + logical.get_y());
+                       layout->show_in_cairo_context (cr);
+               }
+       }
+
+       /* done! */
+}
diff --git a/libs/canvas/scroll_group.cc b/libs/canvas/scroll_group.cc
new file mode 100644 (file)
index 0000000..df51df9
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+    Copyright (C) 2014 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <iostream>
+
+#include "pbd/compose.h"
+
+#include "canvas/canvas.h"
+#include "canvas/debug.h"
+#include "canvas/scroll_group.h"
+
+using namespace std;
+using namespace ArdourCanvas;
+
+ScrollGroup::ScrollGroup (Canvas* c, ScrollSensitivity s)
+       : Container (c)
+       , _scroll_sensitivity (s)       
+{
+}
+
+ScrollGroup::ScrollGroup (Item* parent, ScrollSensitivity s)
+       : Container (parent)
+       , _scroll_sensitivity (s)       
+{
+}
+
+void
+ScrollGroup::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
+{
+       /* clip the draw to the area that this scroll group nominally occupies
+        * WITHOUT scroll offsets in effect
+        */
+
+       boost::optional<Rect> r = bounding_box();
+
+       if (!r) {
+               return;
+       }
+
+       Rect self (_position.x, _position.y, _position.x + r.get().width(), _position.y + r.get().height());
+
+       self.x1 = min (_position.x + _canvas->width(), self.x1);
+       self.y1 = min (_position.y + _canvas->height(), self.y1);
+
+       context->save ();
+       context->rectangle (self.x0, self.y0, self.width(), self.height());
+       context->clip ();
+       
+       Container::render (area, context);
+
+       context->restore ();
+}
+
+void
+ScrollGroup::scroll_to (Duple const& d)
+{
+       if (_scroll_sensitivity & ScrollsHorizontally) {
+               _scroll_offset.x = d.x;
+       }
+
+       if (_scroll_sensitivity & ScrollsVertically) {
+               _scroll_offset.y = d.y;
+       }
+}
+
+bool
+ScrollGroup::covers_canvas (Duple const& d) const
+{
+       boost::optional<Rect> r = bounding_box ();
+
+       if (!r) {
+               return false;
+       }
+
+       return r->contains (d);
+}
+
+bool
+ScrollGroup::covers_window (Duple const& d) const
+{
+       boost::optional<Rect> r = bounding_box ();
+
+       if (!r) {
+               return false;
+       }
+
+       Rect w = r->translate (-_scroll_offset);
+
+       return w.contains (d);
+}
index f2b8e7174434efcd37f6e568939a3d128ae6db47..b62da9cc8ccfeb0a535a29bfcca677136b30c754 100644 (file)
@@ -19,8 +19,8 @@ using PBD::error;
 PBD::Searchpath StatefulImage::_image_search_path;
 StatefulImage::ImageCache StatefulImage::_image_cache;
 
-StatefulImage::StatefulImage (Group* group, const XMLNode& node)
-       : Item (group)
+StatefulImage::StatefulImage (Canvas* c, const XMLNode& node)
+       : Item (c)
        , _state (0)
        , _font (0)
        , _text_x (0)
@@ -134,7 +134,7 @@ StatefulImage::find_image (const std::string& name)
 
        std::string path;
 
-       if (!find_file_in_search_path (_image_search_path, name, path)) {
+       if (!find_file (_image_search_path, name, path)) {
                error << string_compose (_("Image named %1 not found"),
                                         name) << endmsg;
                return ImageHandle();
index 438413080a2d7a539283bf157980e5f0d15f126a..6fa1d30d31872dd7dae112b8b4f5b3a6734d7534 100644 (file)
@@ -31,8 +31,8 @@
 using namespace std;
 using namespace ArdourCanvas;
 
-Text::Text (Group* parent)
-       : Item (parent)
+Text::Text (Canvas* c)
+       : Item (c)
        , _color (0x000000ff)
        , _font_description (0)
        , _alignment (Pango::ALIGN_LEFT)
@@ -41,7 +41,20 @@ Text::Text (Group* parent)
        , _need_redraw (false)
        , _clamped_width (COORD_MAX)
 {
+       _outline = false;
+}
 
+Text::Text (Item* parent)
+       : Item (parent)
+       , _color (0x000000ff)
+       , _font_description (0)
+       , _alignment (Pango::ALIGN_LEFT)
+       , _width (0)
+       , _height (0)
+       , _need_redraw (false)
+       , _clamped_width (COORD_MAX)
+{
+       _outline = false;
 }
 
 Text::~Text ()
@@ -95,24 +108,32 @@ Text::_redraw (Glib::RefPtr<Pango::Layout> layout) const
        }
 
        layout->set_alignment (_alignment);
-       
+
        int w;
        int h;
 
-       layout->get_size (w, h);
-       
-       _width = w / Pango::SCALE;
-       _height = h / Pango::SCALE;
-       
+       layout->get_pixel_size (w, h);
+
+       _width = w;
+       _height = h;
+
        _image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, _width, _height);
 
        Cairo::RefPtr<Cairo::Context> img_context = Cairo::Context::create (_image);
 
        /* and draw, in the appropriate color of course */
 
-       set_source_rgba (img_context, _color);
-
-       layout->show_in_cairo_context (img_context);
+       if (_outline) {
+               set_source_rgba (img_context, _outline_color);
+               layout->update_from_cairo_context (img_context);
+               pango_cairo_layout_path (img_context->cobj(), layout->gobj());
+               img_context->stroke_preserve ();
+               set_source_rgba (img_context, _color);
+               img_context->fill ();
+       } else {
+               set_source_rgba (img_context, _color);
+               layout->show_in_cairo_context (img_context);
+       }
 
        /* text has now been rendered in _image and is ready for blit in
         * ::render 
@@ -122,19 +143,26 @@ Text::_redraw (Glib::RefPtr<Pango::Layout> layout) const
 }
 
 void
-Text::render (Rect const & /*area*/, Cairo::RefPtr<Cairo::Context> context) const
+Text::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
 {
        if (_text.empty()) {
                return;
        }
 
+       Rect self = item_to_window (Rect (0, 0, min (_clamped_width, (double)_image->get_width ()), _image->get_height ()));
+       boost::optional<Rect> i = self.intersection (area);
+       
+       if (!i) {
+               return;
+       }
+
        if (_need_redraw) {
                redraw (context);
        }
        
-       Rect self = item_to_window (Rect (0, 0, min (_clamped_width, _width), _height));
-       
-       context->rectangle (self.x0, self.y0, self.width(), self.height());
+       Rect intersection (i.get());
+
+       context->rectangle (intersection.x0, intersection.y0, intersection.width(), intersection.height());
        context->set_source (_image, self.x0, self.y0);
        context->fill ();
 }
@@ -193,6 +221,9 @@ Text::set_color (Color color)
        begin_change ();
 
        _color = color;
+       if (_outline) {
+               set_outline_color (contrasting_text_color (_color));
+       }
        _need_redraw = true;
 
        end_change ();
diff --git a/libs/canvas/tracking_text.cc b/libs/canvas/tracking_text.cc
new file mode 100644 (file)
index 0000000..ea44102
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+    Copyright (C) 2011-2013 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <algorithm>
+
+#include "canvas/canvas.h"
+#include "canvas/tracking_text.h"
+
+using namespace ArdourCanvas;
+
+TrackingText::TrackingText (Canvas* c)
+       : Text (c)
+       , track_x (true)
+       , track_y (true)
+       , offset (Duple (10, 10))
+{
+       init ();
+}
+
+TrackingText::TrackingText (Item* p)
+       : Text (p)
+       , track_x (true)
+       , track_y (true)
+       , offset (Duple (10, 10))
+{
+       init ();
+}
+
+void
+TrackingText::init ()
+{
+       _canvas->MouseMotion.connect (sigc::mem_fun (*this, &TrackingText::pointer_motion));
+       set_ignore_events (true);
+       set_outline (true);
+       hide ();
+}
+
+void
+TrackingText::pointer_motion (Duple const & winpos)
+{
+       if (!_visible) {
+               return;
+       }
+
+       Duple pos (_parent->window_to_item (winpos));
+
+       if (!track_x) {
+               pos.x = position().x;
+       }
+
+       if (!track_y) {
+               pos.y = position().y;
+       }
+
+       pos = pos.translate (offset);
+
+       /* keep inside the window */
+       
+       Rect r (0, 0, _canvas->width(), _canvas->height());
+
+       /* border of 200 pixels on the right, and 50 on all other sides */
+
+       const double border = 50.0;
+
+       r.x0 += border;
+       r.x1 = std::max (r.x0, (r.x1 - 200.0));
+       r.y0 += border;
+       r.y1 = std::max (r.y0, (r.y1 - border));
+
+       /* clamp */
+       
+       if (pos.x < r.x0) {
+               pos.x = r.x0;
+       } else if (pos.x > r.x1) {
+               pos.x = r.x1;
+       }
+
+       if (pos.y < r.y0) {
+               pos.y = r.y0;
+       } else if (pos.y > r.y1) {
+               pos.y = r.y1;
+       }
+
+       /* move */
+
+       set_position (pos);
+}
+
+void
+TrackingText::show_and_track (bool tx, bool ty)
+{
+       track_x = tx;
+       track_y = ty;
+
+       bool was_visible = _visible;
+       show ();
+
+       if (!was_visible) {
+               /* move to current pointer location. do this after show() so that
+                * _visible is true, and thus ::pointer_motion() will do 
+                * something.
+                */
+               Duple winpos;
+
+               if (!_canvas->get_mouse_position (winpos)) {
+                       return;
+               }
+
+               pointer_motion (winpos);
+       }
+}
+
+void
+TrackingText::set_x_offset (double o)
+{
+       offset.x = o;
+}
+
+void
+TrackingText::set_y_offset (double o)
+{
+       offset.y = o;
+}
+
+void
+TrackingText::set_offset (Duple const & d)
+{
+       offset = d;
+}
index bdc8fad03966e14b3602f76d5ebc3af84e722d86..99516c849b5bcd7c834368e5343f59d64d6916be 100644 (file)
@@ -220,3 +220,35 @@ ArdourCanvas::distance_to_segment_squared (Duple const & p, Duple const & p1, Du
        return ((dpqx * dpqx) + (dpqy * dpqy));
 }
 
+uint32_t
+ArdourCanvas::contrasting_text_color (uint32_t c)
+{
+       double r, g, b, a;
+       ArdourCanvas::color_to_rgba (c, r, g, b, a);
+
+       const double black_r = 0.0;
+       const double black_g = 0.0;
+       const double black_b = 0.0;
+
+       const double white_r = 1.0;
+       const double white_g = 1.0;
+       const double white_b = 1.0;
+
+       /* Use W3C contrast guideline calculation */
+
+       double white_contrast = (max (r, white_r) - min (r, white_r)) +
+               (max (g, white_g) - min (g, white_g)) + 
+               (max (b, white_b) - min (b, white_b));
+
+       double black_contrast = (max (r, black_r) - min (r, black_r)) +
+               (max (g, black_g) - min (g, black_g)) + 
+               (max (b, black_b) - min (b, black_b));
+
+       if (white_contrast > black_contrast) {          
+               /* use white */
+               return ArdourCanvas::rgba_to_color (1.0, 1.0, 1.0, 1.0);
+       } else {
+               /* use black */
+               return ArdourCanvas::rgba_to_color (0.0, 0.0, 0.0, 1.0);
+       }
+}
index 268f12e16d6610fdd44373b3dbf2544b9637f914..6c0cc504948ccb949a36ae834f3d23f6c551243c 100644 (file)
@@ -41,6 +41,9 @@ using namespace std;
 using namespace ARDOUR;
 using namespace ArdourCanvas;
 
+#define CACHE_HIGH_WATER (2)
+
+std::map <boost::shared_ptr<AudioSource>, std::vector<WaveView::CacheEntry> >  WaveView::_image_cache;
 double WaveView::_global_gradient_depth = 0.6;
 bool WaveView::_global_logscaled = false;
 WaveView::Shape WaveView::_global_shape = WaveView::Normal;
@@ -50,10 +53,31 @@ double WaveView::_clip_level = 0.98853;
 PBD::Signal0<void> WaveView::VisualPropertiesChanged;
 PBD::Signal0<void> WaveView::ClipLevelChanged;
 
-WaveView::WaveView (Group* parent, boost::shared_ptr<ARDOUR::AudioRegion> region)
+WaveView::WaveView (Canvas* c, boost::shared_ptr<ARDOUR::AudioRegion> region)
+       : Item (c)
+       , _region (region)
+       , _channel (0)
+       , _samples_per_pixel (0)
+       , _height (64)
+       , _show_zero (false)
+       , _zero_color (0xff0000ff)
+       , _clip_color (0xff0000ff)
+       , _logscaled (_global_logscaled)
+       , _shape (_global_shape)
+       , _gradient_depth (_global_gradient_depth)
+       , _shape_independent (false)
+       , _logscaled_independent (false)
+       , _gradient_depth_independent (false)
+       , _amplitude_above_axis (1.0)
+       , _region_amplitude (_region->scale_amplitude ())
+       , _region_start (region->start())
+{
+       VisualPropertiesChanged.connect_same_thread (invalidation_connection, boost::bind (&WaveView::handle_visual_property_change, this));
+       ClipLevelChanged.connect_same_thread (invalidation_connection, boost::bind (&WaveView::handle_clip_level_change, this));
+}
+
+WaveView::WaveView (Item* parent, boost::shared_ptr<ARDOUR::AudioRegion> region)
        : Item (parent)
-       , Outline (parent)
-       , Fill (parent)
        , _region (region)
        , _channel (0)
        , _samples_per_pixel (0)
@@ -68,15 +92,16 @@ WaveView::WaveView (Group* parent, boost::shared_ptr<ARDOUR::AudioRegion> region
        , _logscaled_independent (false)
        , _gradient_depth_independent (false)
        , _amplitude_above_axis (1.0)
+       , _region_amplitude (_region->scale_amplitude ())
        , _region_start (region->start())
-       , _sample_start (-1)
-       , _sample_end (-1)
 {
        VisualPropertiesChanged.connect_same_thread (invalidation_connection, boost::bind (&WaveView::handle_visual_property_change, this));
+       ClipLevelChanged.connect_same_thread (invalidation_connection, boost::bind (&WaveView::handle_clip_level_change, this));
 }
 
 WaveView::~WaveView ()
 {
+       invalidate_image_cache ();
 }
 
 void
@@ -100,16 +125,28 @@ WaveView::handle_visual_property_change ()
        }
        
        if (changed) {
-               invalidate_image ();
+               begin_visual_change ();
+               invalidate_image_cache ();
+               end_visual_change ();
        }
 }
 
+void
+WaveView::handle_clip_level_change ()
+{
+       begin_visual_change ();
+       invalidate_image_cache ();
+       end_visual_change ();
+}
+
 void
 WaveView::set_fill_color (Color c)
 {
        if (c != _fill_color) {
-               invalidate_image ();
+               begin_visual_change ();
+               invalidate_image_cache ();
                Fill::set_fill_color (c);
+               end_visual_change ();
        }
 }
 
@@ -117,8 +154,10 @@ void
 WaveView::set_outline_color (Color c)
 {
        if (c != _outline_color) {
-               invalidate_image ();
+               begin_visual_change ();
+               invalidate_image_cache ();
                Outline::set_outline_color (c);
+               end_visual_change ();
        }
 }
 
@@ -128,13 +167,11 @@ WaveView::set_samples_per_pixel (double samples_per_pixel)
        if (samples_per_pixel != _samples_per_pixel) {
                begin_change ();
 
+               invalidate_image_cache ();
                _samples_per_pixel = samples_per_pixel;
-               
                _bounding_box_dirty = true;
                
                end_change ();
-               
-               invalidate_image ();
        }
 }
 
@@ -165,26 +202,166 @@ alt_log_meter (float power)
 void
 WaveView::set_clip_level (double dB)
 {
-       _clip_level = dB_to_coefficient (dB);
-       ClipLevelChanged ();
+       const double clip_level = dB_to_coefficient (dB);
+       if (clip_level != _clip_level) {
+               _clip_level = clip_level;
+               ClipLevelChanged ();
+       }
 }
 
-struct LineTips {
-    double top;
-    double bot;
-    bool clip_max;
-    bool clip_min;
-    
-    LineTips() : top (0.0), bot (0.0), clip_max (false), clip_min (false) {}
-};
+void
+WaveView::invalidate_image_cache ()
+{
+       vector <uint32_t> deletion_list;
+       vector <CacheEntry> caches;
+
+       if (_image_cache.find (_region->audio_source ()) != _image_cache.end ()) {
+               caches = _image_cache.find (_region->audio_source ())->second;
+       } else {
+               return;
+       }
+
+       for (uint32_t i = 0; i < caches.size (); ++i) {
+
+               if (_channel != caches[i].channel 
+                   || _height != caches[i].height
+                   || _region_amplitude != caches[i].amplitude 
+                   || _fill_color != caches[i].fill_color 
+                   || _outline_color != caches[i].outline_color) {
+
+                       continue;
+               }
+
+               deletion_list.push_back (i);
+               
+       }
+
+       while (deletion_list.size() > 0) {
+               caches[deletion_list.back ()].image.clear ();
+               caches.erase (caches.begin() + deletion_list.back());
+               deletion_list.pop_back();
+       }
+
+       if (caches.size () == 0) {
+               _image_cache.erase(_region->audio_source ());
+       } else {
+               _image_cache[_region->audio_source ()] = caches;
+       }
+
+}
 
 void
-WaveView::draw_image (PeakData* _peaks, int n_peaks) const
+WaveView::consolidate_image_cache () const
+{
+       list <uint32_t> deletion_list;
+       vector <CacheEntry> caches;
+       uint32_t other_entries = 0;
+
+       if (_image_cache.find (_region->audio_source ()) != _image_cache.end ()) {
+               caches  = _image_cache.find (_region->audio_source ())->second;
+       }
+
+       for (uint32_t i = 0; i < caches.size (); ++i) {
+
+               if (_channel != caches[i].channel 
+                   || _height != caches[i].height
+                   || _region_amplitude != caches[i].amplitude 
+                   || _fill_color != caches[i].fill_color 
+                   || _outline_color != caches[i].outline_color) {
+
+                       other_entries++;
+                       continue;
+               }
+
+               framepos_t segment_start = caches[i].start;
+               framepos_t segment_end = caches[i].end;
+
+               for (uint32_t j = i; j < caches.size (); ++j) {
+
+                       if (i == j || _channel != caches[j].channel 
+                           || _height != caches[i].height 
+                           || _region_amplitude != caches[i].amplitude 
+                           || _fill_color != caches[i].fill_color 
+                           || _outline_color != caches[i].outline_color) {
+
+                               continue;
+                       }
+
+                       if (caches[j].start >= segment_start && caches[j].end <= segment_end) {
+
+                               deletion_list.push_back (j);
+                       }
+               }
+       }
+
+       deletion_list.sort ();
+       deletion_list.unique ();
+
+       while (deletion_list.size() > 0) {
+               caches[deletion_list.back ()].image.clear ();
+               caches.erase (caches.begin() + deletion_list.back ());
+               deletion_list.pop_back();
+       }
+
+       /* We don't care if this channel/height/amplitude has anything in the cache - just drop the Last Added entries 
+          until we reach a size where there is a maximum of CACHE_HIGH_WATER + other entries.
+       */
+
+       while (caches.size() > CACHE_HIGH_WATER + other_entries) {
+               caches.front ().image.clear ();
+               caches.erase(caches.begin ());
+       }
+
+       if (caches.size () == 0) {
+               _image_cache.erase (_region->audio_source ());
+       } else {
+               _image_cache[_region->audio_source ()] = caches;
+       }
+}
+
+Coord
+WaveView::y_extent (double s, bool round_to_lower_edge) const
 {
-       _image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, n_peaks, _height);
+       /* it is important that this returns an integral value, so that we 
+          can ensure correct single pixel behaviour.
+        */
+
+       Coord pos;
+
+       switch (_shape) {
+       case Rectified:
+               if (round_to_lower_edge) {
+                       pos = ceil (_height - (s * _height));
+               } else {
+                       pos = floor (_height - (s * _height));
+               }
+               break;
+       default:
+               if (round_to_lower_edge) {
+                       pos = ceil ((1.0-s) * (_height/2.0));
+               } else {
+                       pos = floor ((1.0-s) * (_height/2.0));
+               }
+               break;
+       }
 
-       Cairo::RefPtr<Cairo::Context> context = Cairo::Context::create (_image);
+       return min (_height, (max (0.0, pos)));
+}
 
+struct LineTips {
+       double top;
+       double bot;
+       double spread;
+       bool clip_max;
+       bool clip_min;
+       
+       LineTips() : top (0.0), bot (0.0), clip_max (false), clip_min (false) {}
+};
+
+void
+WaveView::draw_image (Cairo::RefPtr<Cairo::ImageSurface>& image, PeakData* _peaks, int n_peaks) const
+{
+       Cairo::RefPtr<Cairo::Context> context = Cairo::Context::create (image);
        boost::scoped_array<LineTips> tips (new LineTips[n_peaks]);
 
        /* Clip level nominally set to -0.9dBFS to account for inter-sample
@@ -197,7 +374,7 @@ WaveView::draw_image (PeakData* _peaks, int n_peaks) const
           has been scaled by scale_amplitude() already.
        */
 
-       const double clip_level = _clip_level * _region->scale_amplitude();
+       const double clip_level = _clip_level * _region_amplitude;
 
        if (_shape == WaveView::Rectified) {
 
@@ -208,8 +385,11 @@ WaveView::draw_image (PeakData* _peaks, int n_peaks) const
 
                if (_logscaled) {
                        for (int i = 0; i < n_peaks; ++i) {
+
                                tips[i].bot = height();
-                               tips[i].top = position (alt_log_meter (fast_coefficient_to_dB (max (fabs (_peaks[i].max), fabs (_peaks[i].min)))));
+                               const double p = alt_log_meter (fast_coefficient_to_dB (max (fabs (_peaks[i].max), fabs (_peaks[i].min))));
+                               tips[i].top = y_extent (p, false);
+                               tips[i].spread = (1.0 - p) * _height;
 
                                if (fabs (_peaks[i].max) >= clip_level) {
                                        tips[i].clip_max = true;
@@ -219,9 +399,12 @@ WaveView::draw_image (PeakData* _peaks, int n_peaks) const
                                        tips[i].clip_min = true;
                                }
                        }
+
                } else {for (int i = 0; i < n_peaks; ++i) {
+
                                tips[i].bot = height();
-                               tips[i].top = position (max (fabs (_peaks[i].max), fabs (_peaks[i].min)));
+                               tips[i].top = y_extent (max (fabs (_peaks[i].max), fabs (_peaks[i].min)), true);
+                               tips[i].spread = (1.0 - fabs(_peaks[i].max - _peaks[i].min)) * _height;
 
                                if (fabs (_peaks[i].max) >= clip_level) {
                                        tips[i].clip_max = true;
@@ -237,35 +420,36 @@ WaveView::draw_image (PeakData* _peaks, int n_peaks) const
 
                if (_logscaled) {
                        for (int i = 0; i < n_peaks; ++i) {
-                               Coord top = _peaks[i].min;
-                               Coord bot = _peaks[i].max;
+                               double top = _peaks[i].min;
+                               double bot = _peaks[i].max;
 
                                if (fabs (top) >= clip_level) {
                                        tips[i].clip_max = true;
                                }
 
-                               if (fabs (bot) >= clip_level) {
+                               if (fabs (top) >= clip_level) {
                                        tips[i].clip_min = true;
                                }
 
                                if (top > 0.0) {
-                                       top = position (alt_log_meter (fast_coefficient_to_dB (top)));
+                                       top = alt_log_meter (fast_coefficient_to_dB (top));
                                } else if (top < 0.0) {
-                                       top = position (-alt_log_meter (fast_coefficient_to_dB (-top)));
+                                       top =-alt_log_meter (fast_coefficient_to_dB (-top));
                                } else {
-                                       top = position (0.0);
+                                       top = 0.0;
                                }
 
                                if (bot > 0.0) {
-                                       bot = position (alt_log_meter (fast_coefficient_to_dB (bot)));
+                                       bot = alt_log_meter (fast_coefficient_to_dB (bot));
                                } else if (bot < 0.0) {
-                                       bot = position (-alt_log_meter (fast_coefficient_to_dB (-bot)));
+                                       bot = -alt_log_meter (fast_coefficient_to_dB (-bot));
                                } else {
-                                       bot = position (0.0);
+                                       bot = 0.0;
                                }
 
-                               tips[i].top = top;
-                               tips[i].bot = bot;
+                               tips[i].spread = fabs (((1.0 - top) * (_height/2.0)) - ((1.0 - bot) * _height/2.0));
+                               tips[i].top = y_extent (top, false);
+                               tips[i].bot = y_extent (bot, true);
                        } 
 
                } else {
@@ -279,11 +463,11 @@ WaveView::draw_image (PeakData* _peaks, int n_peaks) const
                                        tips[i].clip_min = true;
                                }
 
-                               tips[i].top = position (_peaks[i].min);
-                               tips[i].bot = position (_peaks[i].max);
-
-
+                               tips[i].top = y_extent (_peaks[i].min, false);
+                               tips[i].bot = y_extent (_peaks[i].max, true);
+                               tips[i].spread = fabs (((1.0 - _peaks[i].max) * (_height/2.0)) - ((1.0 - _peaks[i].min) * (_height/2.0)));
                        }
+                       
                }
        }
 
@@ -328,107 +512,209 @@ WaveView::draw_image (PeakData* _peaks, int n_peaks) const
        context->set_line_width (0.5);
        context->translate (0.5, 0.0);
 
-       /* draw the lines */
+       /* the height of the clip-indicator should be at most 7 pixels,
+        * or 5% of the height of the waveview item.
+        */
+
+       const double clip_height = min (7.0, ceil (_height * 0.05));
+       
+       /* There are 3 possible components to draw at each x-axis position: the
+          waveform "line", the zero line and an outline/clip indicator.  We
+          have to decide which of the 3 to draw at each position, pixel by
+          pixel. This makes the rendering less efficient but it is the only
+          way I can see to do this correctly.
+
+          With only 1 pixel of spread between the top and bottom of the line,
+          we just draw the upper outline/clip indicator.
+
+          With 2 pixels of spread, we draw the upper and lower outline clip
+          indicators. 
+          
+          With 3 pixels of spread we draw the upper and lower outline/clip 
+          indicators and at least 1 pixel of the waveform line.
+          
+          With 5 pixels of spread, we draw all components.
+
+          We can do rectified as two separate passes because we have a much
+          easier decision regarding whether to draw the waveform line. We
+          always draw the clip/outline indicators.
+       */
 
        if (_shape == WaveView::Rectified) {
+
                for (int i = 0; i < n_peaks; ++i) {
-                       context->move_to (i, tips[i].top); /* down 1 pixel */
-                       context->line_to (i, tips[i].bot);
-                       context->stroke ();
-               }
-       } else {
-               for (int i = 0; i < n_peaks; ++i) {
-                       context->move_to (i, tips[i].top);
-                       context->line_to (i, tips[i].bot);
-                       context->stroke ();
+
+                       /* waveform line */
+
+                       if (tips[i].spread >= 2.0) {
+                               context->move_to (i, tips[i].top);
+                               context->line_to (i, tips[i].bot);
+                       }
                }
-       }
 
-       /* now add dots to the top and bottom of each line (this is
-        * modelled on pyramix, except that we add clipping indicators.
-        */
+               context->stroke ();
+
+               /* outline/clip indicators */
 
-       if (_global_show_waveform_clipping) {
-               
                set_source_rgba (context, _outline_color);
                
-               /* the height of the clip-indicator should be at most 7 pixels,
-                  or 5% of the height of the waveview item.
-               */
-               const double clip_height = min (7.0, ceil (_height * 0.05));
-               
                for (int i = 0; i < n_peaks; ++i) {
+
                        context->move_to (i, tips[i].top);
                        
-                       bool show_top_clip = (_shape == WaveView::Rectified && (tips[i].clip_max || tips[i].clip_min)) ||
-                               tips[i].clip_max;
-                       
-                       if (show_top_clip) {
+                       if (_global_show_waveform_clipping && (tips[i].clip_max || tips[i].clip_min)) {
+                               /* clip-indicating upper terminal line */
                                set_source_rgba (context, _clip_color);
-                               context->rel_line_to (0, clip_height);
+                               context->rel_line_to (0, min (clip_height, floor (tips[i].spread)));
                                context->stroke ();
                                set_source_rgba (context, _outline_color);
                        } else {
+                               /* normal upper terminal dot */
+                               context->rel_line_to (0, 1.0);
+                               context->stroke ();
+                       }
+               }
+               
+       } else {
+
+               /* Note the use of cairo save/restore pairs to retain the drawing
+                  context for the waveform lines, which is already set
+                  correctly when we reach here.
+               */
+
+               for (int i = 0; i < n_peaks; ++i) {
+
+                       /* waveform line */
+
+                       if (tips[i].spread >= 3.0) {
+                               context->move_to (i, tips[i].top);
+                               context->line_to (i, tips[i].bot);
+                               context->stroke ();
+                       }
+
+                       /* zero line */
+
+                       if (tips[i].spread >= 5.0 && show_zero_line()) {
+                               context->save ();
+                               set_source_rgba (context, _zero_color);
+                               context->move_to (i, _height/2.0);
+                               context->rel_line_to (0, 0.5);
+                               context->stroke ();
+                               context->restore ();
+                       }
+                       
+                       context->save ();
+
+                       /* upper outline/clip indicator */
+
+                       context->move_to (i, tips[i].top);
+                       if (_global_show_waveform_clipping && ((_shape == WaveView::Rectified && (tips[i].clip_max || tips[i].clip_min)) || tips[i].clip_max)) {
+                               /* clip-indicating upper terminal line */
+                               set_source_rgba (context, _clip_color);
+                               context->rel_line_to (0, min (clip_height, floor (tips[i].spread)));
+                               context->stroke ();
+                       } else {
+                               /* normal upper terminal dot */
+                               set_source_rgba (context, _outline_color);
                                context->rel_line_to (0, 1.0);
                                context->stroke ();
                        }
 
-                       if (_shape != WaveView::Rectified) {
+                       context->restore ();
+                       
+                       if (tips[i].spread >= 2.0) {
+
+                               /* lower outline/clip indicator */
+
+                               context->save ();
                                context->move_to (i, tips[i].bot);
-                               if (tips[i].clip_min) {
+                               if (_global_show_waveform_clipping && _shape != WaveView::Rectified && tips[i].clip_min) {
+                                       /* clip-indicating lower terminal line */
                                        set_source_rgba (context, _clip_color);
-                                       context->rel_line_to (0, -clip_height);
+                                       context->rel_line_to (0, -(min (clip_height, floor (tips[i].spread))));
                                        context->stroke ();
-                                       set_source_rgba (context, _outline_color);
                                } else {
+                                       /* normal lower terminal dot */
+                                       set_source_rgba (context, _outline_color);
                                        context->rel_line_to (0, -1.0);
                                        context->stroke ();
                                }
+                               
+                               context->restore ();
                        }
                }
        }
-
-       if (show_zero_line()) {
-
-               set_source_rgba (context, _zero_color);
-               context->set_line_width (1.0);
-               context->move_to (0, position (0.0) + 0.5);
-               context->line_to (n_peaks, position (0.0) + 0.5);
-               context->stroke ();
-       }
 }
 
 void
-WaveView::ensure_cache (framepos_t start, framepos_t end) const
+WaveView::get_image (Cairo::RefPtr<Cairo::ImageSurface>& image, framepos_t start, framepos_t end, double& image_offset) const
 {
-       if (_image && _sample_start <= start && _sample_end >= end) {
-               /* cache already covers required range, do nothing */
-               return;
+       vector <CacheEntry> caches;
+
+       if (_image_cache.find (_region->audio_source ()) != _image_cache.end ()) {
+               
+               caches = _image_cache.find (_region->audio_source ())->second;
        }
 
+       /* Find a suitable ImageSurface.
+       */
+       for (uint32_t i = 0; i < caches.size (); ++i) {
+
+               if (_channel != caches[i].channel 
+                   || _height != caches[i].height
+                   || _region_amplitude != caches[i].amplitude 
+                   || _fill_color != caches[i].fill_color 
+                   || _outline_color != caches[i].outline_color) {
+
+                       continue;
+               }
+
+               framepos_t segment_start = caches[i].start;
+               framepos_t segment_end = caches[i].end;
+
+               if (end <= segment_end && start >= segment_start) {
+                       image_offset = (segment_start - _region->start()) / _samples_per_pixel;
+                       image = caches[i].image;
+
+                       return;
+               }
+       }
+
+       consolidate_image_cache ();
+
        /* sample position is canonical here, and we want to generate
         * an image that spans about twice the canvas width 
         */
        
        const framepos_t center = start + ((end - start) / 2);
-       const framecnt_t canvas_samples = 2 * (_canvas->visible_area().width() * _samples_per_pixel);
+       const framecnt_t canvas_samples = _canvas->visible_area().width() * _samples_per_pixel; /* one canvas width */
 
        /* we can request data from anywhere in the Source, between 0 and its length
         */
 
-       _sample_start = max ((framepos_t) 0, (center - canvas_samples));
-       _sample_end = min (center + canvas_samples, _region->source_length (0));
+       framepos_t sample_start = max ((framepos_t) 0, (center - canvas_samples));
+       framepos_t sample_end = min (center + canvas_samples, _region->source_length (0));
 
-       const int n_peaks = llrintf ((_sample_end - _sample_start)/ (double) _samples_per_pixel);
+       const int n_peaks = llrintf ((sample_end - sample_start)/ (double) _samples_per_pixel);
 
        boost::scoped_array<ARDOUR::PeakData> peaks (new PeakData[n_peaks]);
 
        _region->read_peaks (peaks.get(), n_peaks, 
-                            _sample_start, _sample_end - _sample_start,
+                            sample_start, sample_end - sample_start,
                             _channel, 
                             _samples_per_pixel);
 
-       draw_image (peaks.get(), n_peaks);
+       image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, ((double)(sample_end - sample_start)) / _samples_per_pixel, _height);
+
+       draw_image (image, peaks.get(), n_peaks);
+
+       _image_cache[_region->audio_source ()].push_back (CacheEntry (_channel, _height, _region_amplitude, _fill_color, _outline_color, sample_start,  sample_end, image));
+
+       image_offset = (sample_start - _region->start()) / _samples_per_pixel;
+
+       //cerr << "_image_cache size is : " << _image_cache.size() << " entries for this audiosource : " << _image_cache.find (_region->audio_source ())->second.size() << endl;
+
+       return;
 }
 
 void
@@ -440,7 +726,7 @@ WaveView::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) cons
                return;
        }
 
-       Rect self = item_to_window (Rect (0.0, 0.0, _region->length() / _samples_per_pixel, _height));
+       Rect self = item_to_window (Rect (0.5, 0.0, _region->length() / _samples_per_pixel, _height));
        boost::optional<Rect> d = self.intersection (area);
 
        if (!d) {
@@ -476,13 +762,10 @@ WaveView::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) cons
 
        // cerr << "Sample space: " << sample_start << " .. " << sample_end << endl;
 
-       ensure_cache (sample_start, sample_end);
-
-       // cerr << "Cache contains " << _cache->pixel_start() << " .. " << _cache->pixel_end() << " / " 
-       // << _cache->sample_start() << " .. " << _cache->sample_end()
-       // << endl;
+       Cairo::RefPtr<Cairo::ImageSurface> image;
+       double image_offset = 0;
 
-       double image_offset = (_sample_start - _region->start()) / _samples_per_pixel;
+       get_image (image, sample_start, sample_end, image_offset);
 
        // cerr << "Offset into image to place at zero: " << image_offset << endl;
 
@@ -499,7 +782,7 @@ WaveView::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) cons
        y = round (y);
        context->device_to_user (x, y);
 
-       context->set_source (_image, x, y);
+       context->set_source (image, x, y);
        context->fill ();
 }
 
@@ -520,13 +803,12 @@ WaveView::set_height (Distance height)
 {
        if (height != _height) {
                begin_change ();
-               
+
+               invalidate_image_cache ();
                _height = height;
-               
+
                _bounding_box_dirty = true;
                end_change ();
-               
-               invalidate_image ();
        }
 }
 
@@ -535,50 +817,43 @@ WaveView::set_channel (int channel)
 {
        if (channel != _channel) {
                begin_change ();
-               
+
+               invalidate_image_cache ();
                _channel = channel;
-               
+
                _bounding_box_dirty = true;
                end_change ();
-               
-               invalidate_image ();
        }
 }
 
-void
-WaveView::invalidate_image ()
-{
-       begin_visual_change ();
-
-       _image.clear ();
-       _sample_start  = -1;
-       _sample_end = -1;
-       
-       end_visual_change ();
-}
-
-
 void
 WaveView::set_logscaled (bool yn)
 {
        if (_logscaled != yn) {
+               begin_visual_change ();
+               invalidate_image_cache ();
                _logscaled = yn;
-               invalidate_image ();
+               end_visual_change ();
        }
 }
 
 void
 WaveView::gain_changed ()
 {
-       invalidate_image ();
+       begin_visual_change ();
+       invalidate_image_cache ();
+       _region_amplitude = _region->scale_amplitude ();
+       end_visual_change ();
 }
 
 void
 WaveView::set_zero_color (Color c)
 {
        if (_zero_color != c) {
+               begin_visual_change ();
+               invalidate_image_cache ();
                _zero_color = c;
-               invalidate_image ();
+               end_visual_change ();
        }
 }
 
@@ -586,8 +861,10 @@ void
 WaveView::set_clip_color (Color c)
 {
        if (_clip_color != c) {
+               begin_visual_change ();
+               invalidate_image_cache ();
                _clip_color = c;
-               invalidate_image ();
+               end_visual_change ();
        }
 }
 
@@ -595,8 +872,10 @@ void
 WaveView::set_show_zero_line (bool yn)
 {
        if (_show_zero != yn) {
+               begin_visual_change ();
+               invalidate_image_cache ();
                _show_zero = yn;
-               invalidate_image ();
+               end_visual_change ();
        }
 }
 
@@ -604,8 +883,10 @@ void
 WaveView::set_shape (Shape s)
 {
        if (_shape != s) {
+               begin_visual_change ();
+               invalidate_image_cache ();
                _shape = s;
-               invalidate_image ();
+               end_visual_change ();
        }
 }
 
@@ -613,8 +894,10 @@ void
 WaveView::set_amplitude_above_axis (double a)
 {
        if (_amplitude_above_axis != a) {
+               begin_visual_change ();
+               invalidate_image_cache ();
                _amplitude_above_axis = a;
-               invalidate_image ();
+               end_visual_change ();
        }
 }
 
@@ -657,26 +940,6 @@ WaveView::region_resized ()
        end_change ();
 }
 
-Coord
-WaveView::position (double s) const
-{
-       /* it is important that this returns an integral value, so that we 
-          can ensure correct single pixel behaviour.
-        */
-
-       Coord pos;
-
-       switch (_shape) {
-       case Rectified:
-               pos = floor (_height - (s * _height));
-       default:
-               pos = floor ((1.0-s) * (_height / 2.0));
-               break;
-       }
-
-       return min (_height, (max (0.0, pos)));
-}
-
 void
 WaveView::set_global_gradient_depth (double depth)
 {
diff --git a/libs/canvas/widget.cc b/libs/canvas/widget.cc
new file mode 100644 (file)
index 0000000..17d0d29
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+    Copyright (C) 2014 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <iostream>
+#include <cairomm/context.h>
+#include "pbd/stacktrace.h"
+#include "pbd/compose.h"
+
+#include "canvas/canvas.h"
+#include "canvas/widget.h"
+#include "canvas/debug.h"
+#include "canvas/utils.h"
+
+using namespace std;
+using namespace ArdourCanvas;
+
+Widget::Widget (Canvas* c, CairoWidget& w)
+       : Item (c)
+       , _widget (w)
+{
+       Event.connect (sigc::mem_fun (*this, &Widget::event_proxy));
+}
+
+Widget::Widget (Item* parent, CairoWidget& w)
+       : Item (parent)
+       , _widget (w)
+{
+       Event.connect (sigc::mem_fun (*this, &Widget::event_proxy));
+}
+
+bool
+Widget::event_proxy (GdkEvent* ev)
+{
+       /* XXX need to translate coordinate into widget's own coordinate space */
+       return _widget.event (ev);
+}
+
+void
+Widget::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
+{
+       // std::cerr << "Render widget\n";
+
+       if (!_bounding_box) {
+               std::cerr << "no bbox\n";
+               return;
+       }
+
+       Rect self = item_to_window (_bounding_box.get());
+       boost::optional<Rect> r = self.intersection (area);
+
+       if (!r) {
+               std::cerr << "no intersection\n";
+               return;
+       }
+
+       Rect draw = r.get ();
+       cairo_rectangle_t crect;
+       crect.x = draw.x0;
+       crect.y = draw.y0;
+       crect.height = draw.height();
+       crect.width = draw.width();
+
+       // std::cerr << "will draw " << draw << "\n";
+       context->save ();
+       context->translate (-draw.x0, -draw.y0);
+       //context->rectangle (draw.x0, draw.y0, draw.width(), draw.height());
+       // context->clip ();
+
+       _widget.render (context->cobj(), &crect);
+
+       context->restore ();
+}
+
+void
+Widget::compute_bounding_box () const
+{
+       std::cerr << "cbbox for widget\n";
+
+       GtkRequisition req;
+       Gtk::Allocation alloc;
+
+       _widget.size_request (req);
+
+       std::cerr << "widget wants " << req.width << " x " << req.height << "\n";
+
+       _bounding_box = Rect (0, 0, req.width, req.height);
+
+       /* make sure the widget knows that it got what it asked for */
+       alloc.set_x (0);
+       alloc.set_y (0);
+       alloc.set_width (req.width);
+       alloc.set_height (req.height);
+
+       _widget.size_allocate (alloc);
+
+       _bounding_box_dirty = false;
+}
+
index 0c5192645c43d3683c213f6521af79c75bf17896..cb7dc83e274524b120b4a4da7d0b42beded1324d 100644 (file)
@@ -32,13 +32,12 @@ canvas_sources = [
         'arrow.cc',
         'canvas.cc',
         'circle.cc',
+        'container.cc',
         'curve.cc',
         'debug.cc',
-        'drag_handle.cc',
         'item.cc',
         'fill.cc',
         'flag.cc',
-        'group.cc',
         'image.cc',
         'line.cc',
         'line_set.cc',
@@ -50,11 +49,16 @@ canvas_sources = [
         'polygon.cc',
         'rectangle.cc',
         'root_group.cc',
+        'ruler.cc',
+        'scroll_group.cc',
         'stateful_image.cc',
         'text.cc',
+        'tracking_text.cc',
         'types.cc',
         'utils.cc',
-        'wave_view.cc'
+        'wave_view.cc',
+        'widget.cc',
+        'xfade_curve.cc',
 ]
 
 def options(opt):
diff --git a/libs/canvas/xfade_curve.cc b/libs/canvas/xfade_curve.cc
new file mode 100644 (file)
index 0000000..f97cd23
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+    Copyright (C) 2013 Paul Davis
+    Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <cmath>
+#include <exception>
+#include <algorithm>
+
+#include "canvas/xfade_curve.h"
+#include "canvas/interpolated_curve.h"
+#include "canvas/utils.h"
+
+using namespace ArdourCanvas;
+using std::min;
+using std::max;
+
+XFadeCurve::XFadeCurve (Canvas* c)
+       : Item (c)
+       , points_per_segment (32)
+       , _xfadeposition (Start)
+       , _outline_color (0x000000ff)
+       , _fill_color (0x22448880)
+{
+}
+
+XFadeCurve::XFadeCurve (Canvas* c, XFadePosition pos)
+       : Item (c)
+       , points_per_segment (32)
+       , _xfadeposition (pos)
+       , _outline_color (0x000000ff)
+       , _fill_color (0x22448880)
+{
+}
+
+XFadeCurve::XFadeCurve (Item* parent)
+       : Item (parent)
+       , points_per_segment (32)
+       , _xfadeposition (Start)
+       , _outline_color (0x000000ff)
+       , _fill_color (0x22448880)
+{
+}
+
+XFadeCurve::XFadeCurve (Item* parent, XFadePosition pos)
+       : Item (parent)
+       , points_per_segment (32)
+       , _xfadeposition (pos)
+       , _outline_color (0x000000ff)
+       , _fill_color (0x22448880)
+{
+}
+
+void
+XFadeCurve::compute_bounding_box () const
+{
+       if (!_in.points.empty() && !_out.points.empty()) {
+
+               Rect bbox;
+               Points::const_iterator i;
+
+               if (!_in.points.empty()) {
+                       i = _in.points.begin();
+                       bbox.x0 = bbox.x1 = i->x;
+                       bbox.y0 = bbox.y1 = i->y;
+
+                       ++i;
+
+                       while (i != _in.points.end()) {
+                               bbox.x0 = min (bbox.x0, i->x);
+                               bbox.y0 = min (bbox.y0, i->y);
+                               bbox.x1 = max (bbox.x1, i->x);
+                               bbox.y1 = max (bbox.y1, i->y);
+                               ++i;
+                       }
+               } else {
+                       i = _out.points.begin();
+                       bbox.x0 = bbox.x1 = i->x;
+                       bbox.y0 = bbox.y1 = i->y;
+               }
+
+               if (!_out.points.empty()) {
+                       i = _out.points.begin();
+                       while (i != _out.points.end()) {
+                               bbox.x0 = min (bbox.x0, i->x);
+                               bbox.y0 = min (bbox.y0, i->y);
+                               bbox.x1 = max (bbox.x1, i->x);
+                               bbox.y1 = max (bbox.y1, i->y);
+                               ++i;
+                       }
+               }
+
+               _bounding_box = bbox.expand (1.0);
+
+       } else {
+               _bounding_box = boost::optional<Rect> ();
+       }
+
+       _bounding_box_dirty = false;
+}
+
+void
+XFadeCurve::set_inout (Points const & in, Points const & out)
+{
+       if (_in.points == in && _out.points == out) {
+               return;
+       }
+       begin_change ();
+       _in.points = in;
+       _out.points = out;
+       _bounding_box_dirty = true;
+       interpolate ();
+       end_change ();
+}
+
+void
+XFadeCurve::set_points_per_segment (uint32_t n)
+{
+       points_per_segment = n;
+       interpolate ();
+       redraw ();
+}
+
+void
+XFadeCurve::interpolate ()
+{
+       _in.samples.clear ();
+       InterpolatedCurve::interpolate (_in.points, points_per_segment, CatmullRomCentripetal, false, _in.samples);
+       _in.n_samples = _in.samples.size();
+
+       _out.samples.clear ();
+       InterpolatedCurve::interpolate (_out.points, points_per_segment, CatmullRomCentripetal, false, _out.samples);
+       _out.n_samples = _out.samples.size();
+}
+
+Cairo::Path *
+XFadeCurve::get_path(Rect const & area, Cairo::RefPtr<Cairo::Context> context, CanvasCurve const &c) const
+{
+       assert(c.points.size() > 1);
+       context->begin_new_path ();
+       Duple window_space;
+
+       if (c.points.size () == 2) {
+
+               window_space = item_to_window (c.points.front(), false);
+               context->move_to (window_space.x, window_space.y);
+               window_space = item_to_window (c.points.back(), false);
+               context->line_to (window_space.x, window_space.y);
+
+       } else {
+
+               /* find left and right-most sample */
+               Points::size_type left = 0;
+               Points::size_type right = c.n_samples;
+
+               for (Points::size_type idx = 0; idx < c.n_samples - 1; ++idx) {
+                       left = idx;
+                       window_space = item_to_window (Duple (c.samples[idx].x, 0.0), false);
+                       if (window_space.x >= area.x0) break;
+               }
+               for (Points::size_type idx = c.n_samples; idx > left + 1; --idx) {
+                       window_space = item_to_window (Duple (c.samples[idx].x, 0.0), false);
+                       if (window_space.x <= area.x1) break;
+                       right = idx;
+               }
+
+               /* draw line between samples */
+               window_space = item_to_window (Duple (c.samples[left].x, c.samples[left].y), false);
+               context->move_to (window_space.x, window_space.y);
+               for (uint32_t idx = left + 1; idx < right; ++idx) {
+                       window_space = item_to_window (Duple (c.samples[idx].x, c.samples[idx].y), false);
+                       context->line_to (window_space.x, window_space.y);
+               }
+       }
+       return context->copy_path ();
+}
+
+void
+XFadeCurve::close_path(Rect const & area, Cairo::RefPtr<Cairo::Context> context, CanvasCurve const &c, bool inside) const
+{
+       Duple window_space;
+       if (inside) {
+               window_space = item_to_window (Duple(c.points.back().x, area.height()), false);
+               context->line_to (window_space.x, window_space.y);
+               window_space = item_to_window (Duple(c.points.front().x, area.height()), false);
+               context->line_to (window_space.x, window_space.y);
+               context->close_path();
+       } else {
+               window_space = item_to_window (Duple(c.points.back().x, 0.0), false);
+               context->line_to (window_space.x, window_space.y);
+               window_space = item_to_window (Duple(c.points.front().x, 0.0), false);
+               context->line_to (window_space.x, window_space.y);
+               context->close_path();
+       }
+}
+
+void
+XFadeCurve::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
+{
+       if (!_bounding_box) { return; }
+       if (_in.points.size() < 2) { return; }
+       if (_out.points.size() < 2) { return; }
+
+       Rect self = item_to_window (_bounding_box.get());
+       boost::optional<Rect> d = self.intersection (area);
+       assert (d);
+       Rect draw = d.get ();
+
+       context->save ();
+       context->rectangle (draw.x0, draw.y0, draw.width(), draw.height());
+       context->clip ();
+
+       /* expand drawing area by several pixels on each side to avoid cairo stroking effects at the boundary.
+        * they will still occur, but cairo's clipping will hide them.
+        */
+       draw = draw.expand (4.0);
+
+       Cairo::Path *path_in = get_path(draw, context, _in);
+       Cairo::Path *path_out = get_path(draw, context, _out);
+
+       Color outline_shaded = _outline_color;
+       outline_shaded = 0.5 * (outline_shaded & 0xff) + (outline_shaded & ~0xff);
+
+       Color fill_shaded = _fill_color;
+       fill_shaded = 0.5 * (fill_shaded & 0xff) + (fill_shaded & ~0xff);
+
+#define IS (_xfadeposition == Start)
+
+       /* fill primary fade */
+       context->begin_new_path ();
+       context->append_path (IS ? *path_in : *path_out);
+       close_path(draw, context, IS ?_in : _out, false);
+       set_source_rgba (context, _fill_color);
+       context->fill ();
+
+       /* fill background fade */
+       context->save ();
+       context->begin_new_path ();
+       context->append_path (IS ? *path_in : *path_out);
+       close_path(draw, context, IS ? _in : _out, true);
+       context->set_fill_rule (Cairo::FILL_RULE_EVEN_ODD);
+       context->clip ();
+       context->begin_new_path ();
+       context->append_path (IS ? *path_out: *path_in);
+       close_path(draw, context, IS ? _out : _in, true);
+       set_source_rgba (context, fill_shaded);
+       context->set_fill_rule (Cairo::FILL_RULE_WINDING);
+       context->fill ();
+       context->restore ();
+
+       /* draw lines over fills */
+       set_source_rgba (context, IS ? _outline_color : outline_shaded);
+       context->set_line_width (IS ? 1.0 : .5);
+
+       context->begin_new_path ();
+       context->append_path (*path_in);
+       context->stroke();
+
+       set_source_rgba (context, IS ? outline_shaded :_outline_color);
+       context->set_line_width (IS ? .5 : 1.0);
+
+       context->begin_new_path ();
+       context->append_path (*path_out);
+       context->stroke();
+
+       context->restore ();
+
+       delete path_in;
+       delete path_out;
+}
index 0774cc528afdfbd2f8b690f50ed57edcfaa06ed6..e66cbe2253444956b231102ed493abe57304fb58 100644 (file)
 
 #include <cassert>
 #include <list>
+#include <stdint.h>
+
 #include <boost/pool/pool.hpp>
 #include <boost/pool/pool_alloc.hpp>
+
 #include <glibmm/threads.h>
+
 #include "pbd/signals.h"
 
 #include "evoral/visibility.h"
index 91bc928d9abb81560a427b17a17a280ad03021d8..5b04e277b95d9affc2c16005a9bda506f18c5a22 100644 (file)
@@ -52,7 +52,9 @@ public:
        SMF() : _smf(0), _smf_track(0), _empty(true) {};
        virtual ~SMF();
 
+       static bool test(const std::string& path);
        int  open(const std::string& path, int track=1) THROW_FILE_ERROR;
+       // XXX 19200 = 10 * Timecode::BBT_Time::ticks_per_beat
        int  create(const std::string& path, int track=1, uint16_t ppqn=19200) THROW_FILE_ERROR;
        void close() THROW_FILE_ERROR;
 
index 7259a5c0debce0995a9350cfa430814d6c0a46af..b642fba4c266cd7f1fdb96733f92979c1cc16c1f 100644 (file)
@@ -42,7 +42,8 @@ const MusicalTime MaxMusicalTime = DBL_MAX;
 const MusicalTime MinMusicalTime = DBL_MIN;
 
 static inline bool musical_time_equal (MusicalTime a, MusicalTime b) {
-       /* acceptable tolerance is 1 tick. Nice if there was no magic number here */
+       /* acceptable tolerance is 1 tick. Nice if there was no magic number here
+        * -> Timecode::BBT_Time::ticks_per_beat */
        return fabs (a - b) <= (1.0/1920.0);
 }
 
index 4b7704fff129688720c1c59e3543c05d11b67da4..e5073182143d2e889754f566594df9ab30fee75b 100644 (file)
@@ -1446,7 +1446,13 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double& x, do
                        assert(inclusive ? x >= start : x > start);
                        return true;
                } else {
-                       return false;
+                       if (inclusive) {
+                               x = next->when;
+                       } else {
+                               x = start;
+                       }
+                       _search_cache.left = x;
+                       return true;
                }
 
                /* No points in the future, so no steps (towards them) in the future */
index 44fc48f7282c168d7f31f3d70a7fa7234089cea8..20cc5d9ec3a088b9f5ca311621d08f9c1e97e9b0 100644 (file)
@@ -403,15 +403,13 @@ Curve::multipoint_eval (double x)
                double tdelta = x - before->when;
                double trange = after->when - before->when;
 
-               return before->value + (vdelta * (tdelta / trange));
-
-#if 0
-               double x2 = x * x;
-               ControlEvent* ev = *range.second;
-
-               return = ev->coeff[0] + (ev->coeff[1] * x) + (ev->coeff[2] * x2) + (ev->coeff[3] * x2 * x);
-#endif
-
+               if (_list.interpolation() == ControlList::Curved && after->coeff) {
+                               ControlEvent* ev = after;
+                               double x2 = x * x;
+                               return ev->coeff[0] + (ev->coeff[1] * x) + (ev->coeff[2] * x2) + (ev->coeff[3] * x2 * x);
+               } else {
+                       return before->value + (vdelta * (tdelta / trange));
+               }
        }
 
        /* x is a control point in the data */
index 51ccda583efd2bad5ac143f7a1323b49f4e702e2..dc3512a0f6673d620048aa68746a523344304787 100644 (file)
@@ -71,6 +71,28 @@ SMF::seek_to_track(int track)
        }
 }
 
+/** Attempt to open the SMF file just to see if it is valid.
+ *
+ * \return  true on success
+ *          false on failure
+ */
+bool
+SMF::test(const std::string& path)
+{
+       PBD::StdioFileDescriptor d (path, "r");
+       FILE* f = d.allocate ();
+       if (f == 0) {
+               return false;
+       }
+
+       smf_t* test_smf;
+       if ((test_smf = smf_load (f)) == NULL) {
+               return false;
+       }
+       smf_delete (test_smf);
+       return true;
+}
+
 /** Attempt to open the SMF file for reading and/or writing.
  *
  * \return  0 on success
index 69931b8e0d221afbd438b0926d9ffe188bb17cd4..6e07776fdb3877d73c0e5bac8437cc0da5facfd1 100644 (file)
@@ -23,7 +23,7 @@ SequenceTest::preserveEventOrderingTest ()
        seq->start_write();
 
        for (Notes::const_iterator i = test_notes.begin(); i != test_notes.end(); ++i) {
-               uint8_t buffer[2];
+               uint8_t buffer[3];
                Event<Time>* event = new Event<Time>(
                                DummyTypeMap::CONTROL, (*i)->on_event().time(), 3, buffer, true
                );
index 12a20080b6d8fc4851d373a2fa5c2fb79c8df929..b2041d60310f3eea3132c6ee288807a85e323f23 100644 (file)
@@ -3,6 +3,10 @@
 #include <string.h>
 #include <vector>
 
+#include "pbd/pbd.h"
+#include "pbd/transmitter.h"
+#include "pbd/receiver.h"
+
 #include "ardour/filesystem_paths.h"
 #ifdef LXVST_SUPPORT
 #include "ardour/linux_vst_support.h"
 #endif
 #include "../ardour/filesystem_paths.cc"
 #include "../ardour/directory_names.cc"
-#include "../pbd/error.cc"
-#include "../pbd/basename.cc"
-#include "../pbd/search_path.cc"
-#include "../pbd/transmitter.cc"
-#include "../pbd/whitespace.cc"
+
 
 #ifdef LXVST_SUPPORT
 void
 vstfx_destroy_editor (VSTState* /*vstfx*/) { }
 #endif
 
+using namespace PBD;
+
+class DummyReceiver : public Receiver {
+       protected:
+               void receive (Transmitter::Channel chn, const char * str) {
+                       const char *prefix = "";
+                       switch (chn) {
+                               case Transmitter::Error:
+                                       prefix = "[ERROR]: ";
+                                       break;
+                               case Transmitter::Info:
+                                       /* ignore */
+                                       return;
+                               case Transmitter::Warning:
+                                       prefix = "[WARNING]: ";
+                                       break;
+                               case Transmitter::Fatal:
+                                       prefix = "[FATAL]: ";
+                                       break;
+                               case Transmitter::Throw:
+                                       abort ();
+                       }
+
+                       std::cerr << prefix << str << std::endl;
+
+                       if (chn == Transmitter::Fatal) {
+                               ::exit (1);
+                       }
+               }
+};
+
+DummyReceiver dummy_receiver;
+
 int main (int argc, char **argv) {
-       if (argc != 2) {
-               fprintf(stderr, "usage: %s <vst>\n", argv[0]);
+       char *dllpath = NULL;
+       if (argc == 3 && !strcmp("-f", argv[1])) {
+               dllpath = argv[2];
+               if (strstr (dllpath, ".so" ) || strstr(dllpath, ".dll")) {
+                       vstfx_remove_infofile(dllpath);
+                       vstfx_un_blacklist(dllpath);
+               }
+
+       }
+       else if (argc != 2) {
+               fprintf(stderr, "usage: %s [-f] <vst>\n", argv[0]);
                return EXIT_FAILURE;
+       } else {
+               dllpath = argv[1];
        }
 
-       char *dllpath = argv[1];
+       PBD::init();
+
+       dummy_receiver.listen_to (error);
+       dummy_receiver.listen_to (info);
+       dummy_receiver.listen_to (fatal);
+       dummy_receiver.listen_to (warning);
+
        std::vector<VSTInfo *> *infos = 0;
+
+       if (0) { }
 #ifdef LXVST_SUPPORT
-       if (strstr (dllpath, ".so")) {
+       else if (strstr (dllpath, ".so")) {
                infos = vstfx_get_info_lx(dllpath);
        }
 #endif
 
 #ifdef WINDOWS_VST_SUPPORT
-       if (strstr (dllpath, ".dll")) {
+       else if (strstr (dllpath, ".dll")) {
                infos = vstfx_get_info_fst(dllpath);
        }
 #endif
+       else {
+               fprintf(stderr, "'%s' is not a supported VST plugin.\n", dllpath);
+       }
+
+       PBD::cleanup();
 
        if (!infos || infos->empty()) {
                return EXIT_FAILURE;
@@ -60,4 +117,3 @@ int main (int argc, char **argv) {
                return EXIT_SUCCESS;
        }
 }
-
index 200308a254b3bdbafae9b32ed89af930eaf3b115..f4159c71e27b5fa3f9b39a730abb8fd507bf5860 100644 (file)
 #include <vector>
 #include <string>
 #include <list>
+#include <stack>
 #include <stdint.h>
 
+#include <boost/shared_ptr.hpp>
+
 #include <gtk/gtkaccelmap.h>
 #include <gtk/gtkuimanager.h>
 #include <gtk/gtkactiongroup.h>
@@ -232,6 +235,81 @@ ActionManager::get_all_actions (vector<string>& names, vector<string>& paths, ve
        }
 }
 
+struct ActionState {
+       GtkAction* action;
+       bool       sensitive;
+       ActionState (GtkAction* a, bool s) : action (a), sensitive (s) {}
+};
+
+typedef std::vector<ActionState> ActionStates;
+
+static std::stack<boost::shared_ptr<ActionStates> > state_stack;
+
+static boost::shared_ptr<ActionStates>
+get_action_state ()
+{
+       boost::shared_ptr<ActionStates> state = boost::shared_ptr<ActionStates>(new ActionStates);
+       
+       /* the C++ API for functions used here appears to be broken in
+          gtkmm2.6, so we fall back to the C level.
+       */
+
+       GList* list = gtk_ui_manager_get_action_groups (ActionManager::ui_manager->gobj());
+       GList* node;
+       GList* acts;
+
+       for (node = list; node; node = g_list_next (node)) {
+
+               GtkActionGroup* group = (GtkActionGroup*) node->data;
+
+               /* first pass: collect them all */
+
+               typedef std::list<Glib::RefPtr<Gtk::Action> > action_list;
+               action_list the_acts;
+
+               for (acts = gtk_action_group_list_actions (group); acts; acts = g_list_next (acts)) {
+                       GtkAction* action = (GtkAction*) acts->data;
+
+                       state->push_back (ActionState (action, gtk_action_get_sensitive (action)));
+               }
+       }
+       
+       return state;
+}
+
+void
+ActionManager::push_action_state ()
+{
+       state_stack.push (get_action_state());
+}
+
+void
+ActionManager::pop_action_state ()
+{
+       if (state_stack.empty()) {
+               warning << string_compose (_("programming error: %1"), X_("ActionManager::pop_action_state called with empty stack")) << endmsg;
+               return;
+       }
+
+       boost::shared_ptr<ActionStates> as = state_stack.top ();
+       state_stack.pop ();
+       
+       for (ActionStates::iterator i = as->begin(); i != as->end(); ++i) {
+               gtk_action_set_sensitive ((*i).action, (*i).sensitive);
+       }
+}
+
+void
+ActionManager::disable_all_actions ()
+{
+       push_action_state ();
+       boost::shared_ptr<ActionStates> as = state_stack.top ();
+       
+       for (ActionStates::iterator i = as->begin(); i != as->end(); ++i) {
+               gtk_action_set_sensitive ((*i).action, false);
+       }
+}
+
 void
 ActionManager::add_action_group (RefPtr<ActionGroup> grp)
 {
index 33841cd0beb6821c61a5927c2a848437e1eeb2f7..6a490e1bb055c6be634968ba750e7303a42e0031 100644 (file)
@@ -65,9 +65,12 @@ BaseUI::RequestType Gtkmm2ext::AddTimeout = BaseUI::new_request_type();
 
 #include "pbd/abstract_ui.cc"  /* instantiate the template */
 
+template class AbstractUI<Gtkmm2ext::UIRequest>;
+
 UI::UI (string namestr, int *argc, char ***argv)
        : AbstractUI<UIRequest> (namestr)
        , _receiver (*this)
+       , errors (0)
          
 {
        theMain = new Main (argc, argv);
@@ -122,6 +125,7 @@ UI::UI (string namestr, int *argc, char ***argv)
 UI::~UI ()
 {
        _receiver.hangup ();
+       delete (errors);
 }
 
 bool
index d92f85bb6eeeba8b58bfde385d8e7d9a1cd6a271..536bd326bec877282a05dec1c321e2bb7631d844 100644 (file)
@@ -91,6 +91,11 @@ namespace ActionManager {
        LIBGTKMM2EXT_API extern void check_toggleaction (std::string);
        LIBGTKMM2EXT_API extern void uncheck_toggleaction (std::string);
        LIBGTKMM2EXT_API extern void set_toggleaction_state (std::string, bool);
+
+       
+        LIBGTKMM2EXT_API extern void push_action_state ();
+        LIBGTKMM2EXT_API extern void pop_action_state ();
+        LIBGTKMM2EXT_API extern void disable_all_actions ();
 };
 
 #endif /* __libgtkmm2ext_actions_h__ */
index 2c70f8aad2ef79b55647563199448aaa7f507109..604abb7a71d567eff7dd26895429ea1a0fa76a58 100644 (file)
@@ -67,9 +67,10 @@ public:
 
        static void provide_background_for_cairo_widget (Gtk::Widget& w, const Gdk::Color& bg);
 
+       virtual void render (cairo_t *, cairo_rectangle_t*) = 0;
+
 protected:
        /** Render the widget to the given Cairo context */
-       virtual void render (cairo_t *, cairo_rectangle_t*) = 0;
        virtual bool on_expose_event (GdkEventExpose *);
        void on_size_allocate (Gtk::Allocation &);
        void on_state_changed (Gtk::StateType);
index 58e2f8f5bd066ff2415b3da1842e7f9e7ef528d3..fdab671759ba819e1580bc7993ed786195cf9425 100644 (file)
@@ -20,7 +20,9 @@
 #ifndef __gtkmm2ext_idle_adjustment_h__
 #define __gtkmm2ext_idle_adjustment_h__
 
+#include <stdint.h>
 #include <sys/time.h>
+
 #include <gtkmm/adjustment.h>
 
 #include "gtkmm2ext/visibility.h"
@@ -37,7 +39,7 @@ class LIBGTKMM2EXT_API IdleAdjustment : public sigc::trackable
 
   private:
        void underlying_adjustment_value_changed();
-       struct timeval last_vc;
+       int64_t last_vc;
        gint timeout_handler();
        bool timeout_queued;
 };
index edf5517fffc6807b49810594a09cf17c08a0bae2..030d717133b52d8f4eadaa9804366f1045dee075 100644 (file)
@@ -33,7 +33,7 @@ IdleAdjustment::IdleAdjustment (Gtk::Adjustment& adj)
 {
        adj.signal_value_changed().connect (mem_fun (*this, &IdleAdjustment::underlying_adjustment_value_changed));
        timeout_queued = 0;
-       gettimeofday (&last_vc, 0);
+       last_vc = g_get_monotonic_time();
 }
 
 IdleAdjustment::~IdleAdjustment ()
@@ -43,7 +43,7 @@ IdleAdjustment::~IdleAdjustment ()
 void
 IdleAdjustment::underlying_adjustment_value_changed ()
 {
-       gettimeofday (&last_vc, 0);
+       last_vc = g_get_monotonic_time();
        
        if (timeout_queued) {
                return;
@@ -56,16 +56,13 @@ IdleAdjustment::underlying_adjustment_value_changed ()
 gint
 IdleAdjustment::timeout_handler ()
 {
-       struct timeval now;
-       struct timeval tdiff;
+       int64_t now, tdiff;
+       now = g_get_monotonic_time();
+       tdiff = now - last_vc;
 
-       gettimeofday (&now, 0);
+       std::cerr << "timer elapsed, diff = " << tdiff << " usec" << std::endl;
 
-       timersub (&now, &last_vc, &tdiff);
-
-       std::cerr << "timer elapsed, diff = " << tdiff.tv_sec << " + " << tdiff.tv_usec << std::endl;
-
-       if (tdiff.tv_sec > 0 || tdiff.tv_usec > 250000) {
+       if (tdiff > 250000) {
                std::cerr << "send signal\n";
                value_changed ();
                timeout_queued = false;
index e4b95d1e032b1c5fa2fea87d25110ebec8e8da2f..97f468707b39291a0efead8aff14787aefb4379d 100644 (file)
@@ -25,8 +25,6 @@
 #include <glibmm.h>
 #include <gdkmm.h>
 
-#include "pbd/pathscanner.h"
-
 #include "gtkmm2ext/keyboard.h"
 #include "gtkmm2ext/selector.h"
 #include "gtkmm2ext/utils.h"
index 4c85f1928de5d74d203ab1a1da69752f87882db8..d3a593a68f3f490876e4ab2f25c86fe173efd821 100644 (file)
@@ -42,6 +42,7 @@ Gtkmm2ext::init (const char* localedir)
 {
 #ifdef ENABLE_NLS
        (void) bindtextdomain(PACKAGE, localedir);
+       (void) bind_textdomain_codeset (PACKAGE, "UTF-8");
 #endif
 }
 
index b92e686ce6b753bca71bea99b2b2be0b5fa70c98..0bdb359decc44e808e7e416ed7ad8560fdd313c9 100644 (file)
@@ -710,7 +710,7 @@ MachineControlCommand::fill_buffer (MachineControl* mmc, MIDI::byte* b) const
        if (_command == MachineControl::cmdLocate) {
                *b++ = 0x6; // byte count
                *b++ = 0x1; // "TARGET" subcommand
-               *b++ = _time.hours;
+               *b++ = _time.hours % 24;
                *b++ = _time.minutes;
                *b++ = _time.seconds;
                *b++ = _time.frames;
index d8c89eb011c3c921eb5923ff5c00ff0e0124f0db..52bf988e9be4debc1465efd85c832b722784d262 100644 (file)
@@ -160,7 +160,7 @@ MidnamTest::load_all_midnams_test ()
     Glib::PatternSpec pattern(string("*.midnam"));
     vector<std::string> result;
 
-    PBD::find_matching_files_in_directory (prefix, pattern, result);
+    PBD::find_files_matching_pattern (result, prefix, pattern);
 
     cout << "Loading " << result.size() << " MIDI patches from " << prefix << endl;
 
index 6c57c84239b62efcbbf288ec2225c26bc76ffc0f..d41933286b7d0d7500282ac3160ea7779bb15f7e 100644 (file)
                                RelativePath="..\cartesian.cc"
                                >
                        </File>
-                       <File
-                               RelativePath="..\clear_dir.cc"
-                               >
-                       </File>
                        <File
                                RelativePath="..\command.cc"
                                >
                                RelativePath="..\pathexpand.cc"
                                >
                        </File>
-                       <File
-                               RelativePath="..\pathscanner.cc"
-                               >
-                       </File>
                        <File
                                RelativePath="..\pbd.cc"
                                >
                                RelativePath="..\pbd\pathexpand.h"
                                >
                        </File>
-                       <File
-                               RelativePath="..\pbd\pathscanner.h"
-                               >
-                       </File>
                        <File
                                RelativePath="..\pbd\pbd.h"
                                >
diff --git a/libs/pbd/clear_dir.cc b/libs/pbd/clear_dir.cc
deleted file mode 100644 (file)
index 9d2d7ed..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
-    Copyright (C) 2012 Paul Davis 
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifdef COMPILER_MSVC
-#include <io.h>      // Microsoft's nearest equivalent to <unistd.h>
-using PBD::readdir;
-using PBD::opendir;
-using PBD::closedir;
-#else
-#include <dirent.h>
-#include <unistd.h>
-#endif
-
-#include <string>
-#include <sys/stat.h>
-#include <errno.h>
-#include <string.h>
-
-#include <glib/gstdio.h>
-#include <glibmm/miscutils.h>
-
-#include "pbd/error.h"
-#include "pbd/compose.h"
-#include "pbd/clear_dir.h"
-
-#include "i18n.h"
-
-using namespace PBD;
-using namespace std;
-
-int
-PBD::clear_directory (const string& dir, size_t* size, vector<string>* paths)
-{
-       struct dirent* dentry;
-       struct stat statbuf;
-       DIR* dead;
-        int ret = 0;
-
-        if ((dead = ::opendir (dir.c_str())) == 0) {
-                return -1;
-        }
-        
-        while ((dentry = ::readdir (dead)) != 0) {
-                
-                /* avoid '.' and '..' */
-                
-                if ((dentry->d_name[0] == '.' && dentry->d_name[1] == '\0') ||
-                    (dentry->d_name[2] == '\0' && dentry->d_name[0] == '.' && dentry->d_name[1] == '.')) {
-                        continue;
-                }
-                
-                string fullpath = Glib::build_filename (dir, dentry->d_name);
-
-                if (::stat (fullpath.c_str(), &statbuf)) {
-                        continue;
-                }
-                
-                if (!S_ISREG (statbuf.st_mode)) {
-                        continue;
-                }
-                
-                if (::g_unlink (fullpath.c_str())) {
-                        error << string_compose (_("cannot remove file %1 (%2)"), fullpath, strerror (errno))
-                              << endmsg;
-                        ret = 1;
-                }
-
-                if (paths) {
-                        paths->push_back (dentry->d_name);
-                }
-
-                if (size) {
-                        *size += statbuf.st_size;
-                }
-        }
-        
-        ::closedir (dead);
-
-        return ret;
-}
-
-// rm -rf <dir> -- used to remove saved plugin state
-void
-PBD::remove_directory (const std::string& dir) {
-       DIR* dead;
-       struct dirent* dentry;
-       struct stat statbuf;
-
-       if ((dead = ::opendir (dir.c_str())) == 0) {
-               return;
-       }
-
-       while ((dentry = ::readdir (dead)) != 0) {
-               if(!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, "..")) {
-                       continue;
-               }
-
-               string fullpath = Glib::build_filename (dir, dentry->d_name);
-               if (::stat (fullpath.c_str(), &statbuf)) {
-                       continue;
-               }
-
-               if (S_ISDIR (statbuf.st_mode)) {
-                       remove_directory(fullpath);
-                       continue;
-               }
-
-               if (::g_unlink (fullpath.c_str())) {
-                       error << string_compose (_("cannot remove file %1 (%2)"), fullpath, strerror (errno)) << endmsg;
-               }
-       }
-       if (::g_rmdir(dir.c_str())) {
-               error << string_compose (_("cannot remove directory %1 (%2)"), dir, strerror (errno)) << endmsg;
-       }
-}
index 2cfa63ae39c19aa387b12e79fbbfb550fcad7a61..4bc6974f4d20974a719863958e43d09ba7271476 100644 (file)
@@ -233,7 +233,7 @@ FdFileDescriptor::open ()
         * or it treats the file as a text stream and puts in
         * line endings in etc
         */
-#ifdef WIN32
+#ifdef PLATFORM_WINDOWS
 #define WRITE_FLAGS O_RDWR | O_CREAT | O_BINARY
 #define READ_FLAGS O_RDONLY | O_BINARY
 #else
index dd9805a3f334f73421870e08a9008cb1b785e738..311d22f9e0a4fc4c2fd9fcf1442e845aae939cd0 100644 (file)
@@ -1,5 +1,6 @@
 /*
-    Copyright (C) 2007 Tim Mayberry 
+    Copyright (C) 2007-2014 Tim Mayberry
+    Copyright (C) 1998-2014 Paul Davis
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 /* close(), read(), write() */
 #ifdef COMPILER_MSVC
 #include <io.h> // Microsoft's nearest equivalent to <unistd.h>
+#include <ardourext/misc.h>
 #else
 #include <unistd.h>
+#include <regex.h>
 #endif
 
 #include "pbd/compose.h"
+#include "pbd/file_manager.h"
 #include "pbd/file_utils.h"
 #include "pbd/debug.h"
 #include "pbd/error.h"
-#include "pbd/pathscanner.h"
+#include "pbd/pathexpand.h"
 #include "pbd/stl_delete.h"
 
 #include "i18n.h"
@@ -60,125 +64,237 @@ using namespace std;
 namespace PBD {
 
 void
-get_files_in_directory (const std::string& directory_path, vector<string>& result)
+run_functor_for_paths (vector<string>& result,
+                       const Searchpath& paths,
+                       bool (*functor)(const string &, void *),
+                       void *arg,
+                       bool pass_files_only,
+                       bool pass_fullpath, bool return_fullpath,
+                       bool recurse)
 {
-       if (!Glib::file_test (directory_path, Glib::FILE_TEST_IS_DIR)) return;
+       for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
+               string expanded_path = path_expand (*i);
+               DEBUG_TRACE (DEBUG::FileUtils,
+                               string_compose("Find files in expanded path: %1\n", expanded_path));
 
-       try
-       {
-               Glib::Dir dir(directory_path);
-               std::copy(dir.begin(), dir.end(), std::back_inserter(result));
-       }
-       catch (Glib::FileError& err)
-       {
-               warning << err.what() << endmsg;
-       }
-}
+               if (!Glib::file_test (expanded_path, Glib::FILE_TEST_IS_DIR)) continue;
 
-void
-find_matching_files_in_directory (const std::string& directory,
-                                  const Glib::PatternSpec& pattern,
-                                  vector<std::string>& result)
-{
-       vector<string> tmp_files;
+               try
+               {
+                       Glib::Dir dir(expanded_path);
+
+                       for (Glib::DirIterator di = dir.begin(); di != dir.end(); di++) {
+
+                               string fullpath = Glib::build_filename (expanded_path, *di);
+                               string basename = *di;
 
-       get_files_in_directory (directory, tmp_files);
-       result.reserve(tmp_files.size());
+                               bool is_dir = Glib::file_test (fullpath, Glib::FILE_TEST_IS_DIR);
 
-       for (vector<string>::iterator file_iter = tmp_files.begin();
-                       file_iter != tmp_files.end();
-                       ++file_iter)
-       {
-               if (!pattern.match(*file_iter)) continue;
+                               if (is_dir && recurse) {
+                                       DEBUG_TRACE (DEBUG::FileUtils,
+                                                       string_compose("Descending into directory:  %1\n",
+                                                               fullpath));
+                                       run_functor_for_paths (result, fullpath, functor, arg, pass_files_only,
+                                                              pass_fullpath, return_fullpath, recurse);
+                               }
 
-               std::string full_path(directory);
-               full_path = Glib::build_filename (full_path, *file_iter);
+                               if (is_dir && pass_files_only) {
+                                       continue;
+                               }
 
-               DEBUG_TRACE (
-                       DEBUG::FileUtils,
-                       string_compose("Found file %1\n", full_path)
-                       );
+                               string functor_str;
 
-               result.push_back(full_path);
+                               if (pass_fullpath) {
+                                       functor_str = fullpath;
+                               } else {
+                                       functor_str = basename;
+                               }
+
+                               DEBUG_TRACE (DEBUG::FileUtils,
+                                               string_compose("Run Functor using string: %1\n", functor_str));
+
+                               if (!functor(functor_str, arg)) {
+                                       continue;
+                               }
+
+                               DEBUG_TRACE (DEBUG::FileUtils,
+                                               string_compose("Found file %1 matching functor\n", functor_str));
+
+                               if (return_fullpath) {
+                                       result.push_back(fullpath);
+                               } else {
+                                       result.push_back(basename);
+                               }
+                       }
+               }
+               catch (Glib::FileError& err)
+               {
+                       warning << err.what() << endmsg;
+               }
        }
 }
 
+static
+bool accept_all_files (string const &, void *)
+{
+       return true;
+}
+
 void
-find_matching_files_in_directories (const vector<std::string>& paths,
-                                    const Glib::PatternSpec& pattern,
-                                    vector<std::string>& result)
+get_paths (vector<string>& result,
+           const Searchpath& paths,
+           bool files_only,
+           bool recurse)
 {
-       for (vector<std::string>::const_iterator path_iter = paths.begin();
-                       path_iter != paths.end();
-                       ++path_iter)
-       {
-               find_matching_files_in_directory (*path_iter, pattern, result);
-       }               
+       run_functor_for_paths (result, paths, accept_all_files, 0,
+                              files_only, true, true, recurse);
 }
 
 void
-find_matching_files_in_search_path (const Searchpath& search_path,
-                                    const Glib::PatternSpec& pattern,
-                                    vector<std::string>& result)
+get_files (vector<string>& result, const Searchpath& paths)
 {
-       find_matching_files_in_directories (search_path, pattern, result);    
+       return get_paths (result, paths, true, false);
 }
 
+static
 bool
-find_file_in_search_path(const Searchpath& search_path,
-                         const string& filename,
-                         std::string& result)
+pattern_filter (const string& str, void *arg)
+{
+       Glib::PatternSpec* pattern = (Glib::PatternSpec*)arg;
+       return pattern->match(str);
+}
+
+void
+find_files_matching_pattern (vector<string>& result,
+                             const Searchpath& paths,
+                             const Glib::PatternSpec& pattern)
+{
+       run_functor_for_paths (result, paths, pattern_filter,
+                              const_cast<Glib::PatternSpec*>(&pattern),
+                              true, false, true, false);
+}
+
+void
+find_files_matching_pattern (vector<string>& result,
+                             const Searchpath& paths,
+                             const string& pattern)
+{
+       Glib::PatternSpec tmp(pattern);
+       find_files_matching_pattern (result, paths, tmp);
+}
+
+bool
+find_file (const Searchpath& search_path,
+           const string& filename,
+           std::string& result)
 {
        vector<std::string> tmp;
-       Glib::PatternSpec tmp_pattern(filename);
 
-       find_matching_files_in_search_path (search_path, tmp_pattern, tmp);
+       find_files_matching_pattern (tmp, search_path, filename);
 
-       if (tmp.size() == 0)
-       {
-               DEBUG_TRACE (
-                       DEBUG::FileUtils,
-                       string_compose("No file matching %1 found in Path: %2\n", filename, search_path.to_string())
-                           );
+       if (tmp.size() == 0) {
+               DEBUG_TRACE (DEBUG::FileUtils,
+                            string_compose("No file matching %1 found in Path: %2\n",
+                            filename, search_path.to_string()));
                return false;
        }
 
-       if (tmp.size() != 1)
-       {
-               DEBUG_TRACE (
-                       DEBUG::FileUtils,
-                       string_compose("Found more that one file matching %1 in Path: %2\n", filename, search_path.to_string())
-                           );
+       if (tmp.size() != 1) {
+               DEBUG_TRACE (DEBUG::FileUtils,
+                            string_compose("Found more that one file matching %1 in Path: %2\n",
+                            filename, search_path.to_string()));
        }
 
        result = tmp.front();
 
-       DEBUG_TRACE (
-               DEBUG::FileUtils,
-               string_compose("Found file %1 in Path: %2\n", filename, search_path.to_string())
-                   );
+       DEBUG_TRACE (DEBUG::FileUtils,
+                    string_compose("Found file %1 in Path: %2\n",
+                    filename, search_path.to_string()));
 
        return true;
 }
 
+static
+bool
+regexp_filter (const string& str, void *arg)
+{
+       regex_t* pattern = (regex_t*)arg;
+       return regexec (pattern, str.c_str(), 0, 0, 0) == 0;
+}
+
+void
+find_files_matching_regex (vector<string>& result,
+                           const Searchpath& paths,
+                           const std::string& regexp)
+{
+       int err;
+       char msg[256];
+       regex_t compiled_pattern;
+
+       if ((err = regcomp (&compiled_pattern, regexp.c_str(),
+                           REG_EXTENDED|REG_NOSUB))) {
+
+               regerror (err, &compiled_pattern,
+                         msg, sizeof (msg));
+
+               error << "Cannot compile soundfile regexp for use ("
+                     << msg
+                     << ")"
+                     << endmsg;
+
+               return;
+       }
+
+       DEBUG_TRACE (DEBUG::FileUtils,
+                       string_compose("Matching files using regexp: %1\n", regexp));
+
+       find_files_matching_filter (result, paths,
+                                   regexp_filter, &compiled_pattern,
+                                   true, true, false);
+
+       regfree (&compiled_pattern);
+}
+
+void
+find_paths_matching_filter (vector<string>& result,
+                            const Searchpath& paths,
+                            bool (*filter)(const string &, void *),
+                            void *arg,
+                            bool pass_fullpath, bool return_fullpath,
+                            bool recurse)
+{
+       run_functor_for_paths (result, paths, filter, arg, false, pass_fullpath, return_fullpath, recurse);
+}
+
+void
+find_files_matching_filter (vector<string>& result,
+                            const Searchpath& paths,
+                            bool (*filter)(const string &, void *),
+                            void *arg,
+                            bool pass_fullpath, bool return_fullpath,
+                            bool recurse)
+{
+       run_functor_for_paths (result, paths, filter, arg, true, pass_fullpath, return_fullpath, recurse);
+}
+
 bool
 copy_file(const std::string & from_path, const std::string & to_path)
 {
        if (!Glib::file_test (from_path, Glib::FILE_TEST_EXISTS)) return false;
 
-       int fd_from = -1;
-       int fd_to = -1;
+       FdFileDescriptor from_file(from_path, false, 0444);
+       FdFileDescriptor to_file(to_path, true, 0666);
+
+       int fd_from = from_file.allocate ();
+       int fd_to = to_file.allocate ();
        char buf[4096]; // BUFSIZ  ??
        ssize_t nread;
 
-       fd_from = ::open(from_path.c_str(), O_RDONLY);
-       if (fd_from < 0) {
-               goto copy_error;
-       }
-
-       fd_to = ::open(to_path.c_str(), O_WRONLY | O_CREAT, 0666);
-       if (fd_to < 0) {
-               goto copy_error;
+       if ((fd_from < 0) || (fd_to < 0)) {
+               error << string_compose (_("Unable to Open files %1 to %2 for Copying(%3)"),
+                               from_path, to_path, g_strerror(errno))
+                     << endmsg;
+               return false;
        }
 
        while (nread = ::read(fd_from, buf, sizeof(buf)), nread > 0) {
@@ -189,55 +305,27 @@ copy_file(const std::string & from_path, const std::string & to_path)
                                nread -= nwritten;
                                out_ptr += nwritten;
                        } else if (errno != EINTR) {
-                               goto copy_error;
+                               error << string_compose (_("Unable to Copy files %1 to %2(%3)"),
+                                               from_path, to_path, g_strerror(errno))
+                                       << endmsg;
+                               return false;
                        }
                } while (nread > 0);
        }
 
-       if (nread == 0) {
-               if (::close(fd_to)) {
-                       fd_to = -1;
-                       goto copy_error;
-               }
-               ::close(fd_from);
-               return true;
-       }
-
-copy_error:
-       int saved_errno = errno;
-
-       if (fd_from >= 0) {
-               ::close(fd_from);
-       }
-       if (fd_to >= 0) {
-               ::close(fd_to);
-       }
-
-       error << string_compose (_("Unable to Copy file %1 to %2 (%3)"),
-                       from_path, to_path, strerror(saved_errno))
-               << endmsg;
-       return false;
-}
-
-static
-bool accept_all_files (string const &, void *)
-{
        return true;
 }
 
 void
 copy_files(const std::string & from_path, const std::string & to_dir)
 {
-       PathScanner scanner;
-       vector<string*>* files = scanner (from_path, accept_all_files, 0, true, false);
-
-       if (files) {
-               for (vector<string*>::iterator i = files->begin(); i != files->end(); ++i) {
-                       std::string from = Glib::build_filename (from_path, **i);
-                       std::string to = Glib::build_filename (to_dir, **i);
-                       copy_file (from, to);
-               }
-               vector_delete (files);
+       vector<string> files;
+       find_files_matching_filter (files, from_path, accept_all_files, 0, true, false);
+
+       for (vector<string>::iterator i = files.begin(); i != files.end(); ++i) {
+               std::string from = Glib::build_filename (from_path, *i);
+               std::string to = Glib::build_filename (to_dir, *i);
+               copy_file (from, to);
        }
 }
 
@@ -306,4 +394,53 @@ exists_and_writable (const std::string & p)
        return true;
 }
 
+int
+remove_directory_internal (const string& dir, size_t* size, vector<string>* paths,
+                           bool just_remove_files)
+{
+       vector<string> tmp_paths;
+       GStatBuf statbuf;
+       int ret = 0;
+
+       get_paths (tmp_paths, dir, just_remove_files, true);
+
+       for (vector<string>::const_iterator i = tmp_paths.begin();
+            i != tmp_paths.end(); ++i) {
+
+                if (g_stat (i->c_str(), &statbuf)) {
+                       continue;
+               }
+
+                if (::g_remove (i->c_str())) {
+                        error << string_compose (_("cannot remove path %1 (%2)"), *i, strerror (errno))
+                              << endmsg;
+                        ret = 1;
+                }
+
+                if (paths) {
+                        paths->push_back (Glib::path_get_basename(*i));
+                }
+
+                if (size) {
+                        *size += statbuf.st_size;
+                }
+
+       }
+
+        return ret;
+}
+
+int
+clear_directory (const string& dir, size_t* size, vector<string>* paths)
+{
+       return remove_directory_internal (dir, size, paths, true);
+}
+
+// rm -rf <dir> -- used to remove saved plugin state
+void
+remove_directory (const std::string& dir)
+{
+       remove_directory_internal (dir, 0, 0, false);
+}
+
 } // namespace PBD
diff --git a/libs/pbd/pathscanner.cc b/libs/pbd/pathscanner.cc
deleted file mode 100644 (file)
index 6d3cba8..0000000
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
-    Copyright (C) 1998-99 Paul Barton-Davis 
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-    $Id$
-*/
-
-#ifdef COMPILER_MSVC
-#include <stdlib.h>
-#include <stdio.h>
-using PBD::readdir;
-using PBD::opendir;
-using PBD::closedir;
-#else
-#include <dirent.h>
-#include <cstdlib>
-#include <cstdio>
-#endif
-#include <cstring>
-#include <vector>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <glibmm/miscutils.h>
-
-#include "pbd/error.h"
-#include "pbd/pathexpand.h"
-#include "pbd/pathscanner.h"
-#include "pbd/stl_delete.h"
-
-using namespace std;
-using namespace PBD;
-
-vector<string *> *
-PathScanner::operator() (const string &dirpath, const string &regexp,
-                        bool match_fullpath, bool return_fullpath, 
-                        long limit, bool recurse)
-
-{
-       int err;
-       char msg[256];
-
-       if ((err = regcomp (&compiled_pattern, regexp.c_str(),
-                           REG_EXTENDED|REG_NOSUB))) {
-               
-               regerror (err, &compiled_pattern,
-                         msg, sizeof (msg));
-               
-               error << "Cannot compile soundfile regexp for use (" 
-                     << msg 
-                     << ")" 
-                     << endmsg;
-               
-               return 0;
-       }
-       
-       return run_scan (dirpath, &PathScanner::regexp_filter, 
-                        (bool (*)(const string &, void *)) 0,
-                        0,
-                        match_fullpath,
-                        return_fullpath,
-                        limit, recurse);
-}      
-
-vector<string *> *
-PathScanner::run_scan (const string &dirpath, 
-                      bool (PathScanner::*memberfilter)(const string &),
-                      bool (*filter)(const string &, void *),
-                      void *arg,
-                      bool match_fullpath, bool return_fullpath,
-                      long limit,
-                      bool recurse)
-{
-       return run_scan_internal ((vector<string*>*) 0, dirpath, memberfilter, filter, arg, match_fullpath, return_fullpath, limit, recurse);
-}
-       
-vector<string *> *
-PathScanner::run_scan_internal (vector<string *> *result,
-                               const string &dirpath, 
-                               bool (PathScanner::*memberfilter)(const string &),
-                               bool (*filter)(const string &, void *),
-                               void *arg,
-                               bool match_fullpath, bool return_fullpath,
-                               long limit,
-                               bool recurse)
-{
-       DIR *dir;
-       struct dirent *finfo;
-       char *pathcopy = strdup (search_path_expand (dirpath).c_str());
-       char *thisdir;
-       string fullpath;
-       string search_str;
-       string *newstr;
-       long nfound = 0;
-
-       if ((thisdir = strtok (pathcopy, G_SEARCHPATH_SEPARATOR_S)) == 0 ||
-           strlen (thisdir) == 0) {
-               free (pathcopy);
-               return 0;
-       }
-
-       if (result == 0) {
-               result = new vector<string *>;
-       }
-
-       do {
-
-               if ((dir = opendir (thisdir)) == 0) {
-                       continue;
-               }
-               
-               while ((finfo = readdir (dir)) != 0) {
-
-                       if ((finfo->d_name[0] == '.' && finfo->d_name[1] == '\0') ||
-                           (finfo->d_name[0] == '.' && finfo->d_name[1] == '.' && finfo->d_name[2] == '\0')) {
-                               continue;
-                       }
-                        
-                        fullpath = Glib::build_filename (thisdir, finfo->d_name);
-
-                       struct stat statbuf;
-                       if (stat (fullpath.c_str(), &statbuf) < 0) {
-                               continue;
-                       }
-
-                       if (statbuf.st_mode & S_IFDIR && recurse) {
-                               run_scan_internal (result, fullpath, memberfilter, filter, arg, match_fullpath, return_fullpath, limit, recurse);
-                       } else {
-                               
-                               if (match_fullpath) {
-                                       search_str = fullpath;
-                               } else {
-                                       search_str = finfo->d_name;
-                               }
-                               
-                               /* handle either type of function ptr */
-                               
-                               if (memberfilter) {
-                                       if (!(this->*memberfilter)(search_str)) {
-                                               continue;
-                                       } 
-                               } else {
-                                       if (!filter(search_str, arg)) {
-                                               continue;
-                                       }
-                               }
-
-                               if (return_fullpath) {
-                                       newstr = new string (fullpath);
-                               } else {
-                                       newstr = new string (finfo->d_name);
-                               } 
-                               
-                               result->push_back (newstr);
-                               nfound++;
-                       }
-               }
-               closedir (dir);
-               
-       } while ((limit < 0 || (nfound < limit)) && (thisdir = strtok (0, G_SEARCHPATH_SEPARATOR_S)));
-
-       free (pathcopy);
-       return result;
-}
-
-string *
-PathScanner::find_first (const string &dirpath,
-                        const string &regexp,
-                        bool match_fullpath,
-                        bool return_fullpath)
-{
-       vector<string *> *res;
-       string *ret;
-       int err;
-       char msg[256];
-
-       if ((err = regcomp (&compiled_pattern, regexp.c_str(),
-                           REG_EXTENDED|REG_NOSUB))) {
-               
-               regerror (err, &compiled_pattern,
-                         msg, sizeof (msg));
-               
-               error << "Cannot compile soundfile regexp for use (" << msg << ")" << endmsg;
-
-               
-               return 0;
-       }
-       
-       res = run_scan (dirpath, 
-                       &PathScanner::regexp_filter,
-                       (bool (*)(const string &, void *)) 0,
-                       0,
-                       match_fullpath,
-                       return_fullpath, 
-                       1);
-       
-       if (res->size() == 0) {
-               ret = 0;
-       } else {
-               ret = res->front();
-       }
-       vector_delete (res);
-       delete res;
-       return ret;
-}
-
-string *
-PathScanner::find_first (const string &dirpath,
-                        bool (*filter)(const string &, void *),
-                        void * /*arg*/,
-                        bool match_fullpath,
-                        bool return_fullpath)
-{
-       vector<string *> *res;
-       string *ret;
-
-       res = run_scan (dirpath, 
-                       (bool (PathScanner::*)(const string &)) 0,
-                       filter,
-                       0,
-                       match_fullpath,
-                       return_fullpath, 1);
-       
-       if (res->size() == 0) {
-               ret = 0;
-       } else {
-               ret = res->front();
-       }
-       vector_delete (res);
-       delete res;
-       return ret;
-}
index 8841301afe50b1dc3098ac0ec74f73d05447b872..cf5292b9e6e7624d173f6ea015548b3dc48fe9bf 100644 (file)
@@ -211,6 +211,7 @@ AbstractUI<RequestObject>::handle_ui_requests ()
                                         if (vec.buf[0]->invalidation) {
                                                 vec.buf[0]->invalidation->requests.remove (vec.buf[0]);
                                         }
+                                        delete vec.buf[0];
                                         i->second->increment_read_ptr (1);
                                 }
                         } 
@@ -326,6 +327,7 @@ AbstractUI<RequestObject>::send_request (RequestObject *req)
                */
                DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 direct dispatch of request type %3\n", name(), pthread_name(), req->type));
                do_request (req);
+               delete req;
        } else {        
 
                /* If called from a different thread, we first check to see if
index 8e386aa4ebed3f6212093ff3d7623aa8d0d5de70..a8602c7d7877663b00081e04d03c5321a8f3aadc 100644 (file)
@@ -75,8 +75,20 @@ class ABSTRACT_UI_API AbstractUI : public BaseUI
         };
        typedef typename RequestBuffer::rw_vector RequestBufferVector;
 
+#if defined(COMPILER_MINGW) && defined(PTW32_VERSION)
+       struct pthread_cmp
+       {
+               bool operator() (const ptw32_handle_t& thread1, const ptw32_handle_t& thread2)
+               {
+                       return thread1.p < thread2.p;
+               }
+       };
+       typedef typename std::map<pthread_t,RequestBuffer*, pthread_cmp>::iterator RequestBufferMapIterator;
+       typedef std::map<pthread_t,RequestBuffer*, pthread_cmp> RequestBufferMap;
+#else
        typedef typename std::map<pthread_t,RequestBuffer*>::iterator RequestBufferMapIterator;
        typedef std::map<pthread_t,RequestBuffer*> RequestBufferMap;
+#endif
 
        RequestBufferMap request_buffers;
         static Glib::Threads::Private<RequestBuffer> per_thread_request_buffer;
diff --git a/libs/pbd/pbd/clear_dir.h b/libs/pbd/pbd/clear_dir.h
deleted file mode 100644 (file)
index f669b84..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
-    Copyright (C) 2012 Paul Davis 
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef __pbd_clear_dir_h__
-#define __pbd_clear_dir_h__
-
-#include <string>
-#include <vector>
-#include <sys/types.h>
-
-#include "pbd/libpbd_visibility.h"
-
-namespace PBD {
-        LIBPBD_API int clear_directory (const std::string&, size_t* = 0, std::vector<std::string>* = 0);
-        LIBPBD_API void remove_directory (const std::string& dir);
-}
-
-#endif /* __pbd_clear_dir_h__ */
index 01ff8606a722b0eda0d041812f569ed5ad60afe9..9ef5374a0a5e70c6da4ca9087f785ca119b2794e 100644 (file)
 namespace PBD {
 
 /**
- * Get a list of files in a directory.
- * @note You must join path with result to get the absolute path
- * to the file.
+ * Get a list of path entries in a directory or within a directory tree
+ * if recursing.
+ * @note paths in result will be absolute
  *
- * @param path An Absolute path to a directory
- * @param result A vector of filenames.
+ * @param result A vector of absolute paths to the directory entries in filename
+ * encoding.
+ * @param paths A Searchpath
+ * @param files_only Only include file entries in result
+ * @param recurse Recurse into child directories
  */
 LIBPBD_API void
-get_files_in_directory (const std::string& path,
-                        std::vector<std::string>& result);
+get_paths (std::vector<std::string>& result,
+           const Searchpath& paths,
+           bool files_only = true,
+           bool recurse = false);
 
 /**
- * Takes a directory path and returns all the files in the directory
- * matching a particular pattern.
+ * Get a list of files in a Searchpath.
+ * @note paths in result will be absolute.
  *
- * @param directory A directory path
- * @param pattern A Glib::PatternSpec used to match the files.
- * @param result A vector in which to place the resulting matches.
+ * @param path A Searchpath
+ * @param result A vector of paths to files.
  */
 LIBPBD_API void
-find_matching_files_in_directory (const std::string& directory,
-                                  const Glib::PatternSpec& pattern,
-                                  std::vector<std::string>& result);
+get_files (std::vector<std::string>& result,
+           const Searchpath& paths);
 
 /**
- * Takes a number of directory paths and returns all the files matching
- * a particular pattern.
+ * Takes a Searchpath and returns all the files contained in the
+ * directory paths that match a particular pattern.
  *
- * @param paths A vector containing the Absolute paths
- * @param pattern A Glib::PatternSpec used to match the files
  * @param result A vector in which to place the resulting matches.
+ * @param paths A Searchpath
+ * @param pattern A Glib::PatternSpec used to match the files.
  */
 LIBPBD_API void
-find_matching_files_in_directories (const std::vector<std::string>& directory_paths,
-                                    const Glib::PatternSpec& pattern,
-                                    std::vector<std::string>& result);
+find_files_matching_pattern (std::vector<std::string>& result,
+                             const Searchpath& paths,
+                             const Glib::PatternSpec& pattern);
 
 /**
- * Takes a Searchpath and puts a list of all the files in the search path
- * that match pattern into the result vector.
+ * Takes a Searchpath and returns all the files contained in the
+ * directory paths that match a particular pattern.
+ *
+ * This is a convenience method to avoid explicitly declaring
+ * temporary variables such as:
+ * find_files_matching_pattern (result, paths, string("*.ext"))
  *
- * @param search_path A Searchpath
- * @param pattern A Glib::PatternSpec used to match the files
  * @param result A vector in which to place the resulting matches.
+ * @param paths A Searchpath
+ * @param pattern A string representing the Glib::PatternSpec used
+ * to match the files.
  */
 LIBPBD_API void
-find_matching_files_in_search_path (const Searchpath& search_path,
-                                    const Glib::PatternSpec& pattern,
-                                    std::vector<std::string>& result);
+find_files_matching_pattern (std::vector<std::string>& result,
+                             const Searchpath& paths,
+                             const std::string& pattern);
 
 /**
- * Takes a search path and a file name and place the full path
+ * Takes a search path and a file name and places the full path
  * to that file in result if it is found within the search path.
  *
+ * @note the parameter order of this function doesn't match the
+ * others. At the time of writing it is the most widely used file
+ * utility function so I didn't change it but it may be worth
+ * doing at some point if it causes any confusion.
+ *
  * @return true If file is found within the search path.
  */
 LIBPBD_API bool
-find_file_in_search_path (const Searchpath& search_path,
-                          const std::string& filename,
-                          std::string& result);
+find_file (const Searchpath& search_path,
+           const std::string& filename,
+           std::string& result);
+
+
+/**
+ * Find files in paths that match a regular expression
+ * @note This function does not recurse.
+ *
+ * @param result A vector in which to place the resulting matches.
+ * @param paths A Searchpath
+ * @param regexp A regular expression
+ */
+LIBPBD_API void
+find_files_matching_regex (std::vector<std::string>& results,
+                           const Searchpath& paths,
+                           const std::string& regexp);
+
+/**
+ * Find paths in a Searchpath that match a supplied filter(functor)
+ * @note results include files and directories.
+ *
+ * @param result A vector in which to place the resulting matches.
+ * @param paths A Searchpath
+ * @param filter A functor to use to filter paths
+ * @param arg additonal argument to filter if required
+ * @param pass_fullpath pass the full path to the filter or just the basename
+ * @param return_fullpath put the full path in results or just the basename
+ * @param recurse Recurse into child directories to find paths.
+ */
+LIBPBD_API void
+find_paths_matching_filter (std::vector<std::string>& results,
+                            const Searchpath& paths,
+                            bool (*filter)(const std::string &, void *),
+                            void *arg,
+                            bool pass_fullpath,
+                            bool return_fullpath,
+                            bool recurse = false);
+
+/**
+ * Find paths in a Searchpath that match a supplied filter(functor)
+ * @note results include only files.
+ *
+ * @param result A vector in which to place the resulting matches.
+ * @param paths A Searchpath
+ * @param filter A functor to use to filter paths
+ * @param arg additonal argument to filter if required
+ * @param pass_fullpath pass the full path to the filter or just the basename
+ * @param return_fullpath put the full path in results or just the basename
+ * @param recurse Recurse into child directories to find files.
+ */
+LIBPBD_API void
+find_files_matching_filter (std::vector<std::string>& results,
+                            const Searchpath& paths,
+                            bool (*filter)(const std::string &, void *),
+                            void *arg,
+                            bool pass_fullpath,
+                            bool return_fullpath,
+                            bool recurse = false);
 
 /**
  * Attempt to copy the contents of the file from_path to a new file
@@ -131,6 +200,26 @@ LIBPBD_API bool equivalent_paths (const std::string &p1, const std::string &p2);
 /// @return true if path at p exists and is writable, false otherwise
 LIBPBD_API bool exists_and_writable(const std::string & p);
 
+/**
+ * Remove all the files in a directory recursively leaving the directory
+ * structure in place.
+ * @note dir will not be removed
+ *
+ * @param dir The directory to clear of files.
+ * @param size of removed files in bytes.
+ * @param list of files that were removed.
+ */
+LIBPBD_API int clear_directory (const std::string& dir, size_t* size = 0,
+                                std::vector<std::string>* removed_files = 0);
+
+/**
+ * Remove all the contents of a directory recursively.
+ * @note dir will not be removed
+ *
+ * @param dir The directory to remove files from.
+ */
+LIBPBD_API void remove_directory (const std::string& dir);
+
 } // namespace PBD
 
 #endif
index 532bb3ed12349dc9e3b99273cb572a4b1f3624af..2a4502481d8b139f8f77ea863c0f6f9978e581be 100644 (file)
@@ -7,7 +7,7 @@
 #define localtime_r( _clock, _result ) \
        ( *(_result) = *localtime( (_clock) ), (_result) )
 
-#elif defined __MINGW64__
+#elif defined COMPILER_MINGW
 
 #  ifdef localtime_r
 #  undef localtime_r
diff --git a/libs/pbd/pbd/pathscanner.h b/libs/pbd/pbd/pathscanner.h
deleted file mode 100644 (file)
index d62203c..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
-    Copyright (C) 2000-2007 Paul Davis 
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef __libmisc_pathscanner_h__
-#define __libmisc_pathscanner_h__
-
-#include <vector>
-#include <string>
-#ifdef COMPILER_MSVC
-#include <ardourext/misc.h>
-#else
-#include <regex.h>
-#endif
-
-#include "pbd/libpbd_visibility.h"
-
-class LIBPBD_API PathScanner
-
-{
-  public:
-       std::vector<std::string *> *operator() (const std::string &dirpath,
-                                               bool (*filter)(const std::string &, void *arg),
-                                               void *arg, 
-                                               bool match_fullpath = true,
-                                               bool return_fullpath = true,
-                                               long limit = -1,
-                                               bool recurse = false) {
-               return run_scan (dirpath,
-                                (bool (PathScanner::*)(const std::string &)) 0, 
-                                filter, 
-                                arg,
-                                match_fullpath,
-                                return_fullpath, 
-                                limit, recurse);
-       }
-
-       std::vector<std::string *> *operator() (const std::string &dirpath,
-                                               const std::string &regexp,
-                                               bool match_fullpath = true,
-                                               bool return_fullpath = true,
-                                               long limit = -1,
-                                               bool recurse = false);
-       
-       std::string *find_first (const std::string &dirpath,
-                                const std::string &regexp,
-                                bool match_fullpath = true,
-                                bool return_fullpath = true);
-       
-       std::string *find_first (const std::string &dirpath,
-                                bool (*filter)(const std::string &, void *),
-                                void *arg,
-                                bool match_fullpath = true,
-                                bool return_fullpath = true);
-       
-  private:
-       regex_t compiled_pattern;
-       
-       bool regexp_filter (const std::string &str) {
-               return regexec (&compiled_pattern, str.c_str(), 0, 0, 0) == 0;
-       }
-       
-       std::vector<std::string *> *run_scan (const std::string &dirpath,
-                                             bool (PathScanner::*mfilter) (const std::string &),
-                                             bool (*filter)(const std::string &, void *),
-                                             void *arg,
-                                             bool match_fullpath,
-                                             bool return_fullpath,
-                                             long limit,
-                                             bool recurse = false);
-
-       std::vector<std::string *> *run_scan_internal (std::vector<std::string*>*, 
-                                                      const std::string &dirpath,
-                                                      bool (PathScanner::*mfilter) (const std::string &),
-                                                      bool (*filter)(const std::string &, void *),
-                                                      void *arg,
-                                                      bool match_fullpath,
-                                                      bool return_fullpath,
-                                                      long limit,
-                                                      bool recurse = false);
-};
-
-#endif // __libmisc_pathscanner_h__
index 1232175fecc8192643c4bc2db22c3b5afbe56bc7..ce6e5a9c4f9367664107aa47b40340d8d39a89f0 100644 (file)
@@ -42,6 +42,8 @@
 #include <string>
 #include <pthread.h>
 #include <signal.h>
+#include <map>
+
 #ifdef NOPBD  /* unit-test outside ardour */
 #include <sigc++/bind.h>
 #include <sigc++/signal.h>
@@ -94,6 +96,23 @@ class LIBPBD_API SystemExec
                 *
                 */
                SystemExec (std::string c, char ** a);
+
+               /** similar to \ref SystemExec but expects a whole command line, and
+                * handles some simple escape sequences.
+                *
+                * @param command complete command-line to be executed
+                * @param subs a map of <char, std::string> listing the % substitutions to
+                *             be made.
+                *
+                * creates an argv array from the given command string, splitting into
+                * parameters at spaces.
+                * "\ " is non-splitting space, "\\" (and "\" at end of command) as "\",
+                * for "%<char>", <char> is looked up in subs and the corresponding string
+                * substituted. "%%" (and "%" at end of command)
+                * returns an argv array suitable for creating a new SystemExec with
+                */
+               SystemExec (std::string command, const std::map<char, std::string> subs);
+
                virtual ~SystemExec ();
 
                /** fork and execute the given program
@@ -182,6 +201,7 @@ class LIBPBD_API SystemExec
                int nicelevel; ///< process nice level - defaults to 0
 
                void make_argp(std::string);
+               void make_argp_escaped(std::string command, const std::map<char, std::string> subs);
                void make_envp();
 
                char **argp;
@@ -198,6 +218,7 @@ class LIBPBD_API SystemExec
 #else
                pid_t pid;
 #endif
+               void init ();
                pthread_mutex_t write_lock;
 
                int fdin; ///< file-descriptor for writing to child's STDIN. This variable is identical to pin[1] but also used as status check if the stdin pipe is open: <0 means closed.
index c441c01211af77200c61f6d1cece92de56265bf7..895bc5990946ddb001dee4eadb492a25782c690e 100644 (file)
@@ -52,9 +52,15 @@ Searchpath::Searchpath (const vector<std::string>& paths)
 void
 Searchpath::add_directory (const std::string& directory_path)
 {
-       if (!directory_path.empty()) {
-               push_back(directory_path);
+       if (directory_path.empty()) {
+               return;
        }
+       for (vector<std::string>::const_iterator i = begin(); i != end(); ++i) {
+               if (*i == directory_path) {
+                       return;
+               }
+       }
+       push_back(directory_path);
 }
 
 void
index 0102323505cf651336b2900987ef09e3868fc9f3..b11ef1ce0ef6fe2144c39cb9f940e9f93591cb9a 100644 (file)
 #include <sys/resource.h>
 #endif
 
+#include <glibmm/miscutils.h>
 
 #define USE_VFORK
 
+#include "pbd/file_utils.h"
+#include "pbd/search_path.h"
 #include "pbd/system_exec.h"
 
 using namespace std;
@@ -151,9 +154,8 @@ static int close_allv(const int except_fds[]) {
 }
 #endif /* not on windows, nor vfork */
 
-
-SystemExec::SystemExec (std::string c, std::string a)
-       : cmd(c)
+void
+SystemExec::init ()
 {
        pthread_mutex_init(&write_lock, NULL);
        thread_active=false;
@@ -161,12 +163,19 @@ SystemExec::SystemExec (std::string c, std::string a)
        pin[1] = -1;
        nicelevel = 0;
        envp = NULL;
-       argp = NULL;
 #ifdef PLATFORM_WINDOWS
        stdinP[0] = stdinP[1] = INVALID_HANDLE_VALUE;
        stdoutP[0] = stdoutP[1] = INVALID_HANDLE_VALUE;
        stderrP[0] = stderrP[1] = INVALID_HANDLE_VALUE;
 #endif
+}
+
+SystemExec::SystemExec (std::string c, std::string a)
+       : cmd(c)
+{
+       init ();
+
+       argp = NULL;
        make_envp();
        make_argp(a);
 }
@@ -174,21 +183,99 @@ SystemExec::SystemExec (std::string c, std::string a)
 SystemExec::SystemExec (std::string c, char **a)
        : cmd(c) , argp(a)
 {
-       pthread_mutex_init(&write_lock, NULL);
-       thread_active=false;
-       pid = 0;
-       pin[1] = -1;
-       nicelevel = 0;
-       envp = NULL;
+       init ();
+
 #ifdef PLATFORM_WINDOWS
-       stdinP[0] = stdinP[1] = INVALID_HANDLE_VALUE;
-       stdoutP[0] = stdoutP[1] = INVALID_HANDLE_VALUE;
-       stderrP[0] = stderrP[1] = INVALID_HANDLE_VALUE;
        make_wargs(a);
 #endif
        make_envp();
 }
 
+SystemExec::SystemExec (std::string command, const std::map<char, std::string> subs)
+{
+       init ();
+       make_argp_escaped(command, subs);
+       if (!find_file (Searchpath (Glib::getenv ("PATH")), argp[0], cmd)) {
+               // not found in path - use as-is
+               cmd = argp[0];
+       }
+
+       // Glib::find_program_in_path () is only available in Glib >= 2.28
+       // cmd = Glib::find_program_in_path (argp[0]);
+
+       make_envp();
+}
+
+void
+SystemExec::make_argp_escaped(std::string command, const std::map<char, std::string> subs)
+{
+
+       int inquotes = 0;
+       int n = 0;
+       size_t i = 0;
+       std::string arg = "";
+
+       argp = (char **) malloc(sizeof(char *));
+
+       for (i = 0; i <= command.length(); i++) { // include terminating '\0'
+               char c = command.c_str()[i];
+               if (inquotes) {
+                       if (c == '"') {
+                               inquotes = 0;
+                       } else {
+                               // still in quotes - just copy
+                               arg += c;
+                       }
+               } else switch (c) {
+                       case '%' :
+                               c = command.c_str()[++i];
+                               if (c == '%' || c == '\0') {
+                                       // "%%", "%" at end-of-string => "%"
+                                       arg += '%';
+                               } else {
+                                       // search subs for string to substitute for char
+                                       std::map<char, std::string>::const_iterator s = subs.find(c);
+                                       if (s != subs.end()) {
+                                               // found substitution
+                                               arg += s->second;
+                                       } else {
+                                               // not a valid substitution, just copy
+                                               arg += '%';
+                                               arg += c;
+                                       }
+                               }
+                               break;
+                       case '\\':
+                               c = command.c_str()[++i];
+                               switch (c) {
+                                       case ' ' :
+                                       case '"' : arg += c; break; // "\\", "\" at end-of-string => "\"
+                                       case '\0': 
+                                       case '\\': arg += '\\'; break;
+                                       default  : arg += '\\'; arg += c; break;
+                               }
+                               break;
+                       case '"' :
+                               inquotes = 1;
+                               break;
+                       case ' ' :
+                       case '\t':
+                       case '\0':
+                               if (arg.length() > 0) {
+                                       // if there wasn't already a space or tab, start a new parameter
+                                       argp = (char **) realloc(argp, (n + 2) * sizeof(char *));
+                                       argp[n++] = strdup (arg.c_str());
+                                       arg = "";
+                               }
+                               break;
+                       default :
+                               arg += c;
+                               break;
+               }
+       }
+       argp[n] = NULL;
+}
+
 SystemExec::~SystemExec ()
 {
        terminate ();
@@ -522,7 +609,7 @@ SystemExec::make_argp(std::string args) {
                        *cp2 = '\0';
                        argp[argn++] = strdup(cp1);
                        cp1 = cp2 + 1;
-           argp = (char **) realloc(argp, (argn + 1) * sizeof(char *));
+                       argp = (char **) realloc(argp, (argn + 1) * sizeof(char *));
                }
        }
        if (cp2 != cp1) {
@@ -546,7 +633,7 @@ SystemExec::terminate ()
        close_stdin();
 
        if (pid) {
-               ::usleep(50000);
+               ::usleep(200000);
                sched_yield();
                wait(WNOHANG);
        }
@@ -557,7 +644,7 @@ SystemExec::terminate ()
        
        if (pid) {
                ::kill(pid, SIGTERM);
-               usleep(50000);
+               ::usleep(250000);
                sched_yield();
                wait(WNOHANG);
        }
index 458105d1779286fd3364b6d2de28a19cb42b292a..59475ff644a411e3815e913f1f20f5fd4d12c75a 100644 (file)
@@ -1,9 +1,17 @@
+#include "filesystem_test.h"
+
 #include <unistd.h>
 #include <stdlib.h>
-#include "filesystem_test.h"
+
+#include <glibmm/miscutils.h>
+#include <glibmm/fileutils.h>
+
 #include "pbd/file_utils.h"
 
+#include "test_common.h"
+
 using namespace std;
+using namespace PBD;
 
 CPPUNIT_TEST_SUITE_REGISTRATION (FilesystemTest);
 
@@ -35,3 +43,160 @@ FilesystemTest::testPathIsWithin ()
 #endif
 }
 
+void
+FilesystemTest::testCopyFileASCIIFilename ()
+{
+       string testdata_path;
+       CPPUNIT_ASSERT (find_file (test_search_path (), "RosegardenPatchFile.xml", testdata_path));
+
+       string output_path = test_output_directory ("CopyFile");
+
+       output_path = Glib::build_filename (output_path, "RosegardenPatchFile.xml");
+
+       cerr << endl;
+       cerr << "CopyFile test output path: " << output_path << endl;
+
+       CPPUNIT_ASSERT (PBD::copy_file (testdata_path, output_path));
+}
+
+void
+FilesystemTest::testCopyFileUTF8Filename ()
+{
+       vector<string> i18n_files;
+
+       Searchpath i18n_path(test_search_path());
+       i18n_path.add_subdirectory_to_paths("i18n_test");
+
+       PBD::find_files_matching_pattern (i18n_files, i18n_path, "*.tst");
+
+       cerr << endl;
+       cerr << "Copying " << i18n_files.size() << " test files from: "
+            << i18n_path.to_string () << endl;
+
+       for (vector<string>::iterator i = i18n_files.begin(); i != i18n_files.end(); ++i) {
+               string input_path = *i;
+               string output_file = Glib::path_get_basename(*i);
+               string output_path = test_output_directory ("CopyFile");
+               output_path = Glib::build_filename (output_path, output_file);
+
+               cerr << "Copying test file: " << input_path
+                    << " To " << output_path << endl;
+
+               CPPUNIT_ASSERT (PBD::copy_file (input_path, output_path));
+       }
+}
+
+void
+FilesystemTest::testFindFilesMatchingPattern ()
+{
+       vector<string> patch_files;
+
+       PBD::find_files_matching_pattern (patch_files, test_search_path (), "*PatchFile*");
+
+       CPPUNIT_ASSERT(test_search_path ().size() == 1);
+
+       CPPUNIT_ASSERT(patch_files.size() == 2);
+}
+
+string
+create_test_directory (std::string test_dir)
+{
+       vector<string> test_files;
+       vector<string> i18n_files;
+
+       Searchpath spath(test_search_path());
+       PBD::get_files (test_files, spath);
+
+       spath.add_subdirectory_to_paths("i18n_test");
+
+       PBD::get_files (i18n_files, spath);
+
+       string output_dir = test_output_directory (test_dir);
+
+       CPPUNIT_ASSERT (test_search_path().size () != 0);
+
+       string test_dir_path = test_search_path()[0];
+
+       cerr << endl;
+       cerr << "Copying " << test_files.size() << " test files from: "
+            << test_dir_path << " to " << output_dir << endl;
+
+       PBD::copy_files (test_dir_path, output_dir);
+
+       vector<string> copied_files;
+
+       PBD::get_files (copied_files, output_dir);
+
+       CPPUNIT_ASSERT (copied_files.size() == test_files.size());
+
+       string subdir_path = Glib::build_filename (output_dir, "subdir");
+
+       CPPUNIT_ASSERT (g_mkdir_with_parents (subdir_path.c_str(), 0755) == 0);
+
+       cerr << endl;
+       cerr << "Copying " << i18n_files.size() << " i18n test files to: "
+            << subdir_path << endl;
+
+       for (vector<string>::iterator i = i18n_files.begin(); i != i18n_files.end(); ++i) {
+               string input_filepath = *i;
+               string output_filename = Glib::path_get_basename(*i);
+               string output_filepath = Glib::build_filename (subdir_path, output_filename);
+
+               CPPUNIT_ASSERT (PBD::copy_file (input_filepath, output_filepath));
+       }
+
+       copied_files.clear();
+       PBD::get_files (copied_files, subdir_path);
+
+       CPPUNIT_ASSERT (copied_files.size() == i18n_files.size());
+
+       return output_dir;
+}
+
+void
+FilesystemTest::testClearDirectory ()
+{
+       string output_dir_path = create_test_directory ("ClearDirectory");
+
+       vector<string> files_in_output_dir;
+
+       PBD::get_paths (files_in_output_dir, output_dir_path, true, true);
+
+       size_t removed_file_size = 0;
+       vector<string> removed_files;
+
+       CPPUNIT_ASSERT (PBD::clear_directory (output_dir_path, &removed_file_size, &removed_files) ==0);
+
+       cerr << "Removed " << removed_files.size() << " files of total size: "
+            << removed_file_size << endl;
+
+       CPPUNIT_ASSERT (removed_files.size () == files_in_output_dir.size ());
+
+       string subdir_path = Glib::build_filename (output_dir_path, "subdir");
+
+       // make sure the directory structure is still there
+       CPPUNIT_ASSERT (Glib::file_test (subdir_path, Glib::FILE_TEST_IS_DIR));
+}
+
+void
+FilesystemTest::testRemoveDirectory ()
+{
+       string output_dir_path = create_test_directory ("RemoveDirectory");
+
+       vector<string> files_in_output_dir;
+
+       PBD::get_paths (files_in_output_dir, output_dir_path, false, true);
+
+       CPPUNIT_ASSERT (files_in_output_dir.size () != 0);
+
+       PBD::remove_directory (output_dir_path);
+
+       // doesn't actually remove directory though...just contents
+       CPPUNIT_ASSERT (Glib::file_test (output_dir_path, Glib::FILE_TEST_IS_DIR));
+
+       files_in_output_dir.clear ();
+
+       PBD::get_paths (files_in_output_dir, output_dir_path, false, true);
+
+       CPPUNIT_ASSERT (files_in_output_dir.size () == 0);
+}
index 57f26631d01c910cfef8b64e6ce57716755491e0..45d55a673a46f594c3d0c93bdf2d8389ba6d66a7 100644 (file)
@@ -5,10 +5,19 @@ class FilesystemTest : public CppUnit::TestFixture
 {
        CPPUNIT_TEST_SUITE (FilesystemTest);
        CPPUNIT_TEST (testPathIsWithin);
+       CPPUNIT_TEST (testCopyFileASCIIFilename);
+       CPPUNIT_TEST (testCopyFileUTF8Filename);
+       CPPUNIT_TEST (testFindFilesMatchingPattern);
+       CPPUNIT_TEST (testClearDirectory);
+       CPPUNIT_TEST (testRemoveDirectory);
        CPPUNIT_TEST_SUITE_END ();
 
 public:
        void testPathIsWithin ();
-
+       void testCopyFileASCIIFilename ();
+       void testCopyFileUTF8Filename ();
+       void testFindFilesMatchingPattern ();
+       void testClearDirectory ();
+       void testRemoveDirectory ();
 };
 
diff --git a/libs/pbd/test/i18n_test/ardour.tst b/libs/pbd/test/i18n_test/ardour.tst
new file mode 100644 (file)
index 0000000..0c7afd2
--- /dev/null
@@ -0,0 +1,2 @@
+Ardour Test file
+Language: English
diff --git a/libs/pbd/test/i18n_test/žar.tst b/libs/pbd/test/i18n_test/žar.tst
new file mode 100644 (file)
index 0000000..5d65515
--- /dev/null
@@ -0,0 +1,2 @@
+Ardour Test file
+Language: Croatian
diff --git a/libs/pbd/test/i18n_test/пыл.tst b/libs/pbd/test/i18n_test/пыл.tst
new file mode 100644 (file)
index 0000000..fcc5b61
--- /dev/null
@@ -0,0 +1,2 @@
+Ardour Test file
+Language: Russian
diff --git a/libs/pbd/test/i18n_test/եռանդ.tst b/libs/pbd/test/i18n_test/եռանդ.tst
new file mode 100644 (file)
index 0000000..09b97a7
--- /dev/null
@@ -0,0 +1,2 @@
+Ardour Test file
+Language: Armenian
diff --git a/libs/pbd/test/i18n_test/ব্যগ্রতা.tst b/libs/pbd/test/i18n_test/ব্যগ্রতা.tst
new file mode 100644 (file)
index 0000000..86a4d2a
--- /dev/null
@@ -0,0 +1,2 @@
+Ardour Test file
+Language: Bengali
diff --git a/libs/pbd/test/i18n_test/ความกระตือรือร้น.tst b/libs/pbd/test/i18n_test/ความกระตือรือร้น.tst
new file mode 100644 (file)
index 0000000..7ee6e18
--- /dev/null
@@ -0,0 +1,2 @@
+Ardour Test file
+Language: Thai
diff --git a/libs/pbd/test/i18n_test/情熱.tst b/libs/pbd/test/i18n_test/情熱.tst
new file mode 100644 (file)
index 0000000..7d00918
--- /dev/null
@@ -0,0 +1,2 @@
+Ardour Test file
+Language: Japanese
diff --git a/libs/pbd/test/i18n_test/热情.tst b/libs/pbd/test/i18n_test/热情.tst
new file mode 100644 (file)
index 0000000..571ba35
--- /dev/null
@@ -0,0 +1,2 @@
+Ardour Test file
+Language: Chinese (Simplified)
index 2cfe5519c0cf85d894d9947f369866e874e2d9e2..fd845c8be1c0acab1ef6698d48fdbde006959da5 100644 (file)
@@ -64,11 +64,11 @@ SignalsTest::testDestruction ()
        CPPUNIT_ASSERT (true);
 }
 
-class Receiver : public PBD::ScopedConnectionList
+class AReceiver : public PBD::ScopedConnectionList
 {
 public:
-       Receiver (Emitter* e) {
-               e->Fred.connect_same_thread (*this, boost::bind (&Receiver::receiver, this));
+       AReceiver (Emitter* e) {
+               e->Fred.connect_same_thread (*this, boost::bind (&AReceiver::receiver, this));
        }
 
        void receiver () {
@@ -80,7 +80,7 @@ void
 SignalsTest::testScopedConnectionList ()
 {
        Emitter* e = new Emitter;
-       Receiver* r = new Receiver (e);
+       AReceiver* r = new AReceiver (e);
 
        N = 0;
        e->emit ();
index 6e099d2f3e7b95f727a2e8b5e104e8939e4ff647..d7d961ca9790406eb2d63fd84b2e06c1139ebd80 100644 (file)
     675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
+#include "test_common.h"
+
+#include <glibmm/fileutils.h>
 #include <glibmm/miscutils.h>
 
-#include "test_common.h"
+#include <sstream>
+
+using namespace std;
 
 /**
  * This allows tests to find the data files they require by looking
@@ -35,3 +40,34 @@ test_search_path ()
        return Glib::getenv("PBD_TEST_PATH");
 #endif
 }
+
+std::string
+test_output_directory (std::string prefix)
+{
+       std::string tmp_dir = Glib::build_filename (g_get_tmp_dir(), "pbd_test");
+       std::string dir_name;
+       std::string new_test_dir;
+       do {
+               ostringstream oss;
+               oss << prefix;
+               oss << g_random_int ();
+               dir_name = oss.str();
+               new_test_dir = Glib::build_filename (tmp_dir, dir_name);
+               if (Glib::file_test (new_test_dir, Glib::FILE_TEST_EXISTS)) continue;
+       } while (g_mkdir_with_parents (new_test_dir.c_str(), 0755) != 0);
+       return new_test_dir;
+}
+
+void
+get_utf8_test_strings (std::vector<std::string>& result)
+{
+       // These are all translations of "Ardour" from google translate
+       result.push_back ("Ardour"); // Reference
+       result.push_back ("\320\277\321\213\320\273"); // Russian
+       result.push_back ("\305\276ar"); // Croatian
+       result.push_back ("\340\270\204\340\270\247\340\270\262\340\270\241\340\270\201\340\270\243\340\270\260\340\270\225\340\270\267\340\270\255\340\270\243\340\270\267\340\270\255\340\270\243\340\271\211\340\270\231"); // Thai
+       result.push_back ("\325\245\325\274\325\241\325\266\325\244"); // Armenian
+       result.push_back ("\340\246\254\340\247\215\340\246\257\340\246\227\340\247\215\340\246\260\340\246\244\340\246\276"); // Bengali
+       result.push_back ("\346\203\205\347\206\261"); // Japanese
+       result.push_back ("\347\203\255\346\203\205"); // Chinese (Simplified)
+}
index 825c01fecbbcb30affe1221ec813b708b5e22afe..67aff40b38e25e1cbde1c735191d84f90095f067 100644 (file)
@@ -23,4 +23,8 @@
 
 PBD::Searchpath test_search_path ();
 
+std::string test_output_directory (std::string prefix);
+
+void get_utf8_test_strings (std::vector<std::string>& results);
+
 #endif
index ea8f0aa115a709528ec69c3e371a41774237e274..11f60f46a2ddc7b2962be3df5281157cf7936990 100644 (file)
@@ -7,11 +7,21 @@
 #include <glibmm/thread.h>
 #include "scalar_properties.h"
 
+#include "pbd/pbd.h"
+#include "pbd/error.h"
+#include "pbd/textreceiver.h"
 
 int
 main ()
 {
-       Glib::thread_init();
+       TextReceiver text_receiver ("pbd_test");
+
+       if (!PBD::init ()) return 1;
+
+       text_receiver.listen_to (PBD::error);
+       text_receiver.listen_to (PBD::info);
+       text_receiver.listen_to (PBD::fatal);
+       text_receiver.listen_to (PBD::warning);
 
        ScalarPropertiesTest::make_property_quarks ();
        
@@ -29,6 +39,8 @@ main ()
        
        CppUnit::CompilerOutputter compileroutputter (&collectedresults, std::cerr);
        compileroutputter.write ();
-       
+
+       PBD::cleanup ();
+
        return collectedresults.wasSuccessful () ? 0 : 1;
 }
index 8b80eec2c6286526ab69a573d96fee80eece27b1..4d6f260c2709a6687d7ac4268f22bb540a71172d 100644 (file)
@@ -18,7 +18,7 @@ XPathTest::testMisc ()
 //     cout << "Test 1: RosegardenPatchFile.xml: Find all banks in the file" << endl;
 
        std::string testdata_path;
-       CPPUNIT_ASSERT (find_file_in_search_path (test_search_path (), "RosegardenPatchFile.xml", testdata_path));
+       CPPUNIT_ASSERT (find_file (test_search_path (), "RosegardenPatchFile.xml", testdata_path));
 
        XMLTree  doc(testdata_path);
        // "//bank" gives as last element an empty element libxml bug????
@@ -51,7 +51,7 @@ XPathTest::testMisc ()
        
        // We have to allocate a new document here, or we get segfaults
        std::string testsession_path;
-       CPPUNIT_ASSERT (find_file_in_search_path (test_search_path (), "TestSession.ardour", testsession_path));
+       CPPUNIT_ASSERT (find_file (test_search_path (), "TestSession.ardour", testsession_path));
 
        XMLTree doc2(testsession_path);
        result = doc2.find("/Session/Sources/Source[contains(@captured-for, 'Guitar')]");
@@ -77,7 +77,7 @@ XPathTest::testMisc ()
 //     cout << endl << endl << "Test 5: ProtoolsPatchFile.midnam: Get Banks and Patches for 'Name Set 1'" << endl;
        
        std::string testmidnam_path;
-       CPPUNIT_ASSERT (find_file_in_search_path (test_search_path (), "ProtoolsPatchFile.midnam", testmidnam_path));
+       CPPUNIT_ASSERT (find_file (test_search_path (), "ProtoolsPatchFile.midnam", testmidnam_path));
 
        // We have to allocate a new document here, or we get segfaults
        XMLTree doc3(testmidnam_path);
index 3e7b423fc74133728de701451b0ccf387f92a87c..dc85775cbff74978a214140735f65fd3ef992d67 100644 (file)
@@ -37,7 +37,6 @@ libpbd_sources = [
     'convert.cc',
     'controllable.cc',
     'controllable_descriptor.cc',
-    'clear_dir.cc',
     'cpus.cc',
     'debug.cc',
     'enumwriter.cc',
@@ -57,7 +56,6 @@ libpbd_sources = [
     'mountpoint.cc',
     'openuri.cc',
     'pathexpand.cc',
-    'pathscanner.cc',
     'pbd.cc',
     'pool.cc',
     'property_list.cc',
@@ -106,7 +104,7 @@ def configure(conf):
     # Boost headers
     autowaf.check_header(conf, 'cxx', 'boost/shared_ptr.hpp')
     autowaf.check_header(conf, 'cxx', 'boost/weak_ptr.hpp')
-    if conf.env['WINDOWS_VST_SUPPORT'] == True and Options.options.dist_target == 'mingw':
+    if Options.options.dist_target == 'mingw':
         conf.check(compiler='cxx',
                    lib='ole32',
                    mandatory=True,
index da7340182e2f663bd65c557ac4fb1a87a6820290..63e6ad4201daf5c11a59e705d07fabda3eb47200 100644 (file)
@@ -154,7 +154,7 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol
        framepos_t     last_where;
        ARDOUR::gain_t last_track_gain;
        uint32_t       last_meter_fill;
-       struct timeval last_wheel_motion;
+       uint64_t       last_wheel_motion;
        int            last_wheel_dir;
 
        Glib::Mutex io_lock;
index bae6a4883757ec9f89b8d679906332ce1b440ba4..702d361965d6f0d385d1857daf1b63cf2e7edf57 100644 (file)
@@ -28,7 +28,7 @@
 #include "pbd/controllable_descriptor.h"
 #include "pbd/error.h"
 #include "pbd/failed_constructor.h"
-#include "pbd/pathscanner.h"
+#include "pbd/file_utils.h"
 #include "pbd/xml++.h"
 
 #include "midi++/port.h"
@@ -136,20 +136,19 @@ midi_map_filter (const string &str, void* /*arg*/)
 void
 GenericMidiControlProtocol::reload_maps ()
 {
-       vector<string *> *midi_maps;
-       PathScanner scanner;
+       vector<string> midi_maps;
        Searchpath spath (system_midi_map_search_path());
        spath += user_midi_map_directory ();
 
-       midi_maps = scanner (spath.to_string(), midi_map_filter, 0, false, true);
+       find_files_matching_filter (midi_maps, spath, midi_map_filter, 0, false, true);
 
-       if (!midi_maps) {
+       if (midi_maps.empty()) {
                cerr << "No MIDI maps found using " << spath.to_string() << endl;
                return;
        }
 
-       for (vector<string*>::iterator i = midi_maps->begin(); i != midi_maps->end(); ++i) {
-               string fullpath = *(*i);
+       for (vector<string>::iterator i = midi_maps.begin(); i != midi_maps.end(); ++i) {
+               string fullpath = *i;
 
                XMLTree tree;
 
@@ -170,8 +169,6 @@ GenericMidiControlProtocol::reload_maps ()
                
                map_info.push_back (mi);
        }
-
-       delete midi_maps;
 }
        
 void
index a26617fd676d276d89ffdf823dcef6936b27cb7e..1d96a073b0f971837ab63133326155ebf11239af 100644 (file)
@@ -169,7 +169,7 @@ MIDIControllable::control_to_midi (float val)
 
        float control_min = controllable->lower ();
        float control_max = controllable->upper ();
-       const float control_range = control_max - control_min;
+       float control_range = control_max - control_min;
 
        if (controllable->is_toggle()) {
                if (val >= (control_min + (control_range/2.0f))) {
@@ -177,6 +177,14 @@ MIDIControllable::control_to_midi (float val)
                } else {
                        return 0;
                }
+       } else {
+               AutomationControl *actl = dynamic_cast<AutomationControl*> (controllable);
+               if (actl) {
+                       control_min = actl->internal_to_interface(control_min);
+                       control_max = actl->internal_to_interface(control_max);
+                       control_range = control_max - control_min;
+                       val = actl->internal_to_interface(val);
+               }
        }
 
        return (val - control_min) / control_range * max_value_for_type ();
@@ -198,8 +206,17 @@ MIDIControllable::midi_to_control (int val)
 
        float control_min = controllable->lower ();
        float control_max = controllable->upper ();
-       const float control_range = control_max - control_min;
-
+       float control_range = control_max - control_min;
+
+       AutomationControl *actl = dynamic_cast<AutomationControl*> (controllable);
+       if (actl) {
+               if (fv == 0.f) return control_min;
+               if (fv == 1.f) return control_max;
+               control_min = actl->internal_to_interface(control_min);
+               control_max = actl->internal_to_interface(control_max);
+               control_range = control_max - control_min;
+               return actl->interface_to_internal((fv * control_range) + control_min);
+       }
        return (fv * control_range) + control_min;
 }
 
index 04cbfecc56d64f0c0908dc058c5108bffa70419a..2a953fd44c2edcd5ebf4d2a1c5c013b6a7c8437a 100644 (file)
@@ -23,8 +23,9 @@
 
 #include "pbd/xml++.h"
 #include "pbd/error.h"
-#include "pbd/pathscanner.h"
+#include "pbd/file_utils.h"
 #include "pbd/convert.h"
+#include "pbd/stl_delete.h"
 
 #include "ardour/filesystem_paths.h"
 
@@ -470,31 +471,23 @@ DeviceInfo::reload_device_info ()
 {
        DeviceInfo di;
        vector<string> s;
-       vector<string *> *devinfos;
-       PathScanner scanner;
+       vector<string> devinfos;
        Searchpath spath (devinfo_search_path());
 
-       devinfos = scanner (spath.to_string(), devinfo_filter, 0, false, true);
+       find_files_matching_filter (devinfos, spath, devinfo_filter, 0, false, true);
        device_info.clear ();
 
-       if (!devinfos) {
+       if (devinfos.empty()) {
                error << "No MCP device info files found using " << spath.to_string() << endmsg;
                std::cerr << "No MCP device info files found using " << spath.to_string() << std::endl;
                return;
        }
 
-       if (devinfos->empty()) {
-               error << "No MCP device info files found using " << spath.to_string() << endmsg;
-               std::cerr << "No MCP device info files found using " << spath.to_string() << std::endl;
-               return;
-       }
-
-       for (vector<string*>::iterator i = devinfos->begin(); i != devinfos->end(); ++i) {
-               string fullpath = *(*i);
+       for (vector<string>::iterator i = devinfos.begin(); i != devinfos.end(); ++i) {
+               string fullpath = *i;
 
                XMLTree tree;
 
-
                if (!tree.read (fullpath.c_str())) {
                        continue;
                }
@@ -508,8 +501,6 @@ DeviceInfo::reload_device_info ()
                        device_info[di.name()] = di;
                }
        }
-
-       delete devinfos;
 }
 
 std::ostream& operator<< (std::ostream& os, const Mackie::DeviceInfo& di)
index ddbb4782db779d5168a2744ad22a56683c768b93..320dd49297b338a946cba6a4fd64ebdd245e7487 100644 (file)
@@ -24,7 +24,8 @@
 
 #include "pbd/xml++.h"
 #include "pbd/error.h"
-#include "pbd/pathscanner.h"
+#include "pbd/file_utils.h"
+#include "pbd/stl_delete.h"
 #include "pbd/replace_all.h"
 
 #include "ardour/filesystem_paths.h"
@@ -89,25 +90,19 @@ DeviceProfile::reload_device_profiles ()
 {
        DeviceProfile dp;
        vector<string> s;
-       vector<string *> *devprofiles;
-       PathScanner scanner;
+       vector<string> devprofiles;
        Searchpath spath (devprofile_search_path());
 
-       devprofiles = scanner (spath.to_string(), devprofile_filter, 0, false, true);
+       find_files_matching_filter (devprofiles, spath, devprofile_filter, 0, false, true);
        device_profiles.clear ();
 
-       if (!devprofiles) {
+       if (devprofiles.empty()) {
                error << "No MCP device info files found using " << spath.to_string() << endmsg;
                return;
        }
 
-       if (devprofiles->empty()) {
-               error << "No MCP device info files found using " << spath.to_string() << endmsg;
-               return;
-       }
-
-       for (vector<string*>::iterator i = devprofiles->begin(); i != devprofiles->end(); ++i) {
-               string fullpath = *(*i);
+       for (vector<string>::iterator i = devprofiles.begin(); i != devprofiles.end(); ++i) {
+               string fullpath = *i;
 
                XMLTree tree;
 
@@ -125,8 +120,6 @@ DeviceProfile::reload_device_profiles ()
                        device_profiles[dp.name()] = dp;
                }
        }
-
-       delete devprofiles;
 }
 
 int
index 96d7210ff6ad0250ad89778aae70a3eb551fbb9c..0e19062911a48ed7c9c42d8d87c18e7c3fbe94c3 100644 (file)
@@ -50,17 +50,8 @@ public:
        */
        unsigned long start()
        {
-#ifdef _WIN32
-               _start = (unsigned long)::GetTickCount();
-#else
-               gettimeofday ( &_start, 0 );
-#endif
-               running = true;
-#ifdef _WIN32
-               return _start;
-#else
-               return ( _start.tv_sec * 1000000 + _start.tv_usec ) / 1000;
-#endif
+               _start = g_get_monotonic_time();
+               return _start / 1000;
        }
 
        /**
@@ -69,12 +60,7 @@ public:
        */
        unsigned long stop()
        {
-#ifdef _WIN32
-               _stop = (unsigned long)::GetTickCount();
-#else
-               gettimeofday ( &_stop, 0 );
-#endif
-               running = false;
+               _stop = g_get_monotonic_time();
                return elapsed();
        }
 
@@ -85,28 +71,12 @@ public:
        {
                if ( running )
                {
-#ifdef _WIN32
-                       DWORD current = ::GetTickCount();
-                       return current - _start;
-#else
-                       struct timeval current;
-                       gettimeofday ( &current, 0 );
-                       return (
-                               ( current.tv_sec * 1000000 + current.tv_usec ) - ( _start.tv_sec * 1000000 + _start.tv_usec )
-                       ) / 1000
-                       ;
-#endif
+                       uint64_t now = g_get_monotonic_time();
+                       return (now - _start) / 1000;
                }
                else
                {
-#ifdef _WIN32
-                       return _stop - _start;
-#else
-                       return (
-                               ( _stop.tv_sec * 1000000 + _stop.tv_usec ) - ( _start.tv_sec * 1000000 + _start.tv_usec )
-                       ) / 1000
-                       ;
-#endif
+                       return (_stop - _start) / 1000;
                }
        }
        
@@ -121,13 +91,8 @@ public:
        }
 
 private:
-#ifdef _WIN32
-       unsigned long _start;
-       unsigned long _stop;
-#else
-       struct timeval _start;
-       struct timeval _stop;
-#endif
+       uint64_t _start;
+       uint64_t _stop;
        bool running;
 };
 
index 8bc791a1ee5d762bc8115f8bf0e8941ebe6b3fba..648dc4ea1912e6aa493ad0b9aa17658a39f081d4 100644 (file)
@@ -205,7 +205,7 @@ OSC::start ()
 
        std::string url_file;
 
-       if (find_file_in_search_path (ardour_config_search_path(), "osc_url", url_file)) {
+       if (find_file (ardour_config_search_path(), "osc_url", url_file)) {
                
                _osc_url_file = url_file;
                ofstream urlfile;
index baa4ba079b90ab2753dd66658de6a211a3652093..3f15e060a2d64ee8ba1443bd07b5192d7c6cb040 100644 (file)
@@ -62,7 +62,7 @@ TranzportControlProtocol::datawheel ()
                        prev_track ();
                }
 
-               timerclear (&last_wheel_motion);
+               last_wheel_motion = 0;
                
        } else if ((buttonmask & ButtonPrev) || (buttonmask & ButtonNext)) {
 
@@ -72,7 +72,7 @@ TranzportControlProtocol::datawheel ()
                        prev_marker ();
                }
                
-               timerclear (&last_wheel_motion);
+               last_wheel_motion = 0;
                
        } else if (buttonmask & ButtonShift) {
                
@@ -104,7 +104,7 @@ TranzportControlProtocol::datawheel ()
                        }
                }
                
-               timerclear (&last_wheel_motion);
+               last_wheel_motion = 0;
                
        } else {
                
@@ -149,11 +149,10 @@ void
 TranzportControlProtocol::scrub ()
 {
        float speed;
-       struct timeval now;
-       struct timeval delta;
+       uint64_t now;
        int dir;
        
-       gettimeofday (&now, 0);
+       now = g_get_monotonic_time();
        
        if (_datawheel < WheelDirectionThreshold) {
                dir = 1;
@@ -165,13 +164,10 @@ TranzportControlProtocol::scrub ()
                /* changed direction, start over */
                speed = 0.1f;
        } else {
-               if (timerisset (&last_wheel_motion)) {
-
-                       timersub (&now, &last_wheel_motion, &delta);
-                       
+               if (last_wheel_motion != 0) {
                        /* 10 clicks per second => speed == 1.0 */
                        
-                       speed = 100000.0f / (delta.tv_sec * 1000000 + delta.tv_usec);
+                       speed = 100000.0f / (float) (now - last_wheel_motion)
                        
                } else {
                        
index 0ca89403d15c4802241a29fdbee6eeaa28451e64..77a23da0883081fe67d733637750dc5b28ce8d3c 100644 (file)
@@ -17,7 +17,8 @@ def configure(conf):
 def build(bld):
     bld.shlib (
                source   = [ 'src/time.cc', 'src/bbt_time.cc' ],
-               target   = 'libtimecode',
+               name     = 'libtimecode',
+               target   = 'timecode',
                includes = ['.'],
                export_includes = ['.'],
                defines  = [ 'LIBTIMECODE_DLL_EXPORTS' ]
index cba56d313f3a4ec15dbbfaec478475ad0e8b25a6..e1669fbae1fee13fb44905ea67ee4939f15f374c 100644 (file)
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2013-2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
 #include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
index 79fb5aafab2e4c7894219dc5335e8cf9d5b80d91..3ea4a806e9574f7b64b2417d9f937ce259d6f66b 100644 (file)
@@ -193,7 +193,7 @@ typedef _mode_t mode_t;
 
 // int64 abs()
 #ifdef __cplusplus // Normal 'C' doesn't permit over-ridden functions !!
-inline uint64_t abs(int64_t val)
+inline int64_t abs(int64_t val) throw()
 {
        if (val < 0)
                return val * (-1);
index 647fc88f4be96a379ccae21fa285c9c16564ef72..5ac450ed47e533b135de2598d070eefb143a9eb3 100755 (executable)
@@ -408,8 +408,11 @@ cp $BUILD_ROOT/libs/fst/ardour-vst-scanner* $APPLIB/ || true
 # vfork wrapper
 cp $BUILD_ROOT/libs/vfork/ardour-exec-wrapper $APPLIB/
 
+# ALSA device reservation tool (if available)
+cp $BUILD_ROOT/libs/ardouralsautil/ardour-request-device $APPLIB/
+
 OURLIBDIR=$BUILD_ROOT/libs
-OURLIBS=$OURLIBDIR/vamp-sdk:$OURLIBDIR/surfaces/control_protocol:$OURLIBDIR/ardour:$OURLIBDIR/midi++2:$OURLIBDIR/pbd:$OURLIBDIR/rubberband:$OURLIBDIR/soundtouch:$OURLIBDIR/gtkmm2ext:$OURLIBDIR/sigc++2:$OURLIBDIR/glibmm2:$OURLIBDIR/gtkmm2/atk:$OURLIBDIR/gtkmm2/pango:$OURLIBDIR/gtkmm2/gdk:$OURLIBDIR/gtkmm2/gtk:$OURLIBDIR/canvas:$OURLIBDIR/libsndfile:$OURLIBDIR/evoral:$OURLIBDIR/evoral/src/libsmf:$OURLIBDIR/audiographer:$OURLIBDIR/timecode:$OURLIBDIR/taglib:$OURLIBDIR/libltc:$OURLIBDIR/qm-dsp
+OURLIBS=$OURLIBDIR/vamp-sdk:$OURLIBDIR/surfaces/control_protocol:$OURLIBDIR/ardour:$OURLIBDIR/midi++2:$OURLIBDIR/pbd:$OURLIBDIR/rubberband:$OURLIBDIR/soundtouch:$OURLIBDIR/gtkmm2ext:$OURLIBDIR/sigc++2:$OURLIBDIR/glibmm2:$OURLIBDIR/gtkmm2/atk:$OURLIBDIR/gtkmm2/pango:$OURLIBDIR/gtkmm2/gdk:$OURLIBDIR/gtkmm2/gtk:$OURLIBDIR/canvas:$OURLIBDIR/libsndfile:$OURLIBDIR/evoral:$OURLIBDIR/evoral/src/libsmf:$OURLIBDIR/audiographer:$OURLIBDIR/timecode:$OURLIBDIR/taglib:$OURLIBDIR/libltc:$OURLIBDIR/qm-dsp:$OURLIBDIR/ardouralsautil
 
 echo $OURLIBS${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
 
index 31ac58e2a2bb1b44b18206a80d76a4d989ed0c9e..7726b3d9a287d2e6cf94c0ddf1e560742ab0eaf3 100755 (executable)
@@ -381,7 +381,8 @@ cp $ARDOURSTACK_ROOT/lib/suil-0/lib* $Frameworks
 cp $BUILD_ROOT/libs/fst/ardour-vst-scanner* $Frameworks/ || true
 
 # vfork wrapper
-cp $BUILD_ROOT/libs/vfork/ardour-exec-wrapper $Frameworks/
+mkdir $Frameworks/vfork
+cp $BUILD_ROOT/libs/vfork/ardour-exec-wrapper $Frameworks/vfork/
 
 
 while [ true ] ; do 
@@ -556,6 +557,7 @@ if test x$WITH_HARVID != x ; then
        echo "installing video tools.."
        HARVID_VERSION=$(curl -s -S http://ardour.org/files/video-tools/harvid_version.txt)
        XJADEO_VERSION=$(curl -s -S http://ardour.org/files/video-tools/xjadeo_version.txt)
+       MULTIARCH=osx
        echo "copying harvid and xjadeo ..."
 
        rsync -Pa \
index 8741d9c90155905831b572d8563e1fb0ef22dfc9..b667d76548e15b8361d5b84375c682b29671381b 100644 (file)
@@ -2,13 +2,17 @@
   name
   Memcheck:Leak
   fun:*alloc
+  ...
   obj:/usr/lib/*.so.*
+  ...
 }
 {
   name
   Memcheck:Leak
   fun:*alloc
+  ...
   obj:/usr/lib/*-linux-gnu/*.so.*
+  ...
 }
 {
   name
index bcee29deba12525341dd06b97343fdec9f9df1d0..ecfcaab6fd7b09819882f1b91b971668fb5a49fb 100755 (executable)
@@ -5,4 +5,4 @@
 . ./print-env.sh
 
 cd $BASE || exit 1
-./waf configure --prefix="/" --bindir="/" --configdir="/share" --noconfirm --no-lv2 --test --single-tests --dist-target=mingw "$@"
+./waf configure --prefix="/" --bindir="/" --configdir="/share" --noconfirm --test --single-tests --dist-target=mingw "$@"
index 501e8498605eeae38b9ef2816ed0b2f2c94383e9..5855f1fbeecedd1a2046ce526846e12849ad843c 100755 (executable)
@@ -9,4 +9,4 @@ export CXX="distcc $HOST-g++"
 . ./print-env.sh
 
 cd $BASE || exit 1
-./waf configure --prefix="/" --bindir="/" --configdir="/share" --noconfirm --no-lv2 --test --single-tests --dist-target=mingw "$@"
+./waf configure --prefix="/" --bindir="/" --configdir="/share" --noconfirm --test --single-tests --dist-target=mingw "$@"
index 19cabf9c446a55e1f4981b8d4ed31cf7052dd760..50d1ec37b5bf6d03d49ea42437c34ef141421fc9 100755 (executable)
@@ -9,4 +9,4 @@ export CXX="distcc $HOST-g++"
 . ./print-env.sh
 
 cd $BASE || exit 1
-./waf configure --prefix="/" --bindir="/" --configdir="/share" --optimize --noconfirm --no-lv2 --dist-target=mingw "$@"
+./waf configure --prefix="/" --bindir="/" --configdir="/share" --optimize --noconfirm --dist-target=mingw "$@"
index 2e943e00fe9eea5f6d034535720b979bd4fa743b..c6869e79d0a0d63b2519c93b159738aa70841fbf 100755 (executable)
@@ -5,4 +5,4 @@
 . ./print-env.sh
 
 cd $BASE || exit 1
-./waf configure --prefix="/" --bindir="/" --configdir="/share" --optimize --noconfirm --no-lv2 --dist-target=mingw "$@"
+./waf configure --prefix="/" --bindir="/" --configdir="/share" --optimize --noconfirm --dist-target=mingw "$@"
diff --git a/tools/windows_packaging/copydll-fedora.sh b/tools/windows_packaging/copydll-fedora.sh
new file mode 100755 (executable)
index 0000000..8ba3c0f
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+function copydll () {
+       if [ -f $MINGW_ROOT/bin/$1 ] ; then
+               cp $MINGW_ROOT/bin/$1 $2 || return 1
+               return 0
+       fi
+
+       echo "ERROR: File $1 does not exist"
+       return 1
+}
diff --git a/tools/windows_packaging/copydll-waves.sh b/tools/windows_packaging/copydll-waves.sh
new file mode 100755 (executable)
index 0000000..fe2b5ed
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/bash\r
+\r
+function copydll () {\r
+       if [ -f $GTK/bin/$1 ] ; then\r
+               echo "cp $GTK/bin/$1 $2"\r
+               cp $GTK/bin/$1 $2 || return 1\r
+               return 0\r
+       fi\r
+       \r
+       if [ -f $GTK/lib/$1 ] ; then\r
+               echo "cp $GTK/lib/$1 $2"\r
+               cp $GTK/lib/$1 $2 || return 1\r
+               return 0\r
+       fi\r
+       \r
+       if [ -f $A3/bin/$1 ] ; then\r
+               echo "cp $A3/bin/$1 $2"\r
+               cp $A3/bin/$1 $2 || return 1\r
+               return 0\r
+       fi\r
+\r
+       if [ -f $A3/lib/$1 ] ; then\r
+               echo "$A3/lib/$1 $2"\r
+               cp $A3/lib/$1 $2 || return 1\r
+               return 0\r
+       fi\r
+       if which $1 ; then        \r
+         echo "cp `which $1` $2"\r
+         cp `which $1` $2 || return 1\r
+         return 0\r
+       fi\r
+       \r
+       echo "there is no $1"\r
+       return 1\r
+}\r
index 983e6d0b9b382091a7f30d0270e515fbd7b5ec75..00145de2adddb6e46fd59eaf5684e1a88314640b 100755 (executable)
@@ -1,11 +1,23 @@
 #!/bin/bash
 
-BASE=$(readlink -f $0)
-BASE=$(dirname $BASE) # up one
-BASE=$(dirname $BASE) # up one more
-BASE=$(dirname $BASE) # up one more
+if [ -z "$ARCH" ]; then
+       echo "ARCH not set defaulting to win32"
+       ARCH=win32
+elif [ "$ARCH" == "win32" ]; then
+       echo "ARCH set to win32"
+elif [ "$ARCH" == "win64" ]; then
+       echo "ARCH set to win64"
+else
+       echo "ARCH set invalid value aborting..."
+       exit 1
+fi
+
+if [ "$ARCH" == "win32" ]; then
+       HOST=i686-w64-mingw32
+else
+       HOST=x86_64-w64-mingw32
+fi
 
-HOST=i686-w64-mingw32
 MINGW_ROOT=/usr/$HOST/sys-root/mingw
 
 export PKG_CONFIG_PREFIX=$MINGW_ROOT
@@ -22,6 +34,11 @@ export LINK_CXX=$HOST-g++
 export WINRC=$HOST-windres
 export STRIP=$HOST-strip
 
+BASE=$(readlink -f $0)
+BASE=$(dirname $BASE) # up one
+BASE=$(dirname $BASE) # up one more
+BASE=$(dirname $BASE) # up one more
+
 BUILD_DIR=$BASE/build
 BUILD_CACHE_FILE=$BUILD_DIR/c4che/_cache.py
 TOOLS_DIR=$BASE/tools/windows_packaging
@@ -35,9 +52,9 @@ then
 
        # Figure out the Build Type
        if [ x$DEBUG = xT ]; then
-               PACKAGE_DIR="$APPNAME-${release_version}-win32-dbg"
+               PACKAGE_DIR="$APPNAME-${release_version}-$ARCH-dbg"
        else
-               PACKAGE_DIR="$APPNAME-${release_version}-win32"
+               PACKAGE_DIR="$APPNAME-${release_version}-$ARCH"
        fi
 
        if grep -q "BUILD_TESTS = True" $BUILD_CACHE_FILE; then
diff --git a/tools/windows_packaging/package-f19.sh b/tools/windows_packaging/package-f19.sh
new file mode 100755 (executable)
index 0000000..ba7bfd5
--- /dev/null
@@ -0,0 +1,78 @@
+#!/bin/bash
+
+DLLS='
+jack-0.dll
+jackserver-0.dll
+libatk-1.0-0.dll
+libatkmm-1.6-1.dll
+libbz2-1.dll
+libcairo-2.dll
+libcairo-gobject-2.dll
+libcairomm-1.0-1.dll
+libcairo-script-interpreter-2.dll
+libcppunit-1-12-1.dll
+libcrypto-10.dll
+libcurl-4.dll
+libexpat-1.dll
+libfftw3-3.dll
+libfftw3f-3.dll
+libfontconfig-1.dll
+libfreetype-6.dll
+libgailutil-18.dll
+libgcc_s_sjlj-1.dll
+libgdkmm-2.4-1.dll
+libgdk_pixbuf-2.0-0.dll
+libgdk-win32-2.0-0.dll
+libgio-2.0-0.dll
+libgiomm-2.4-1.dll
+libglib-2.0-0.dll
+libglibmm-2.4-1.dll
+libglibmm_generate_extra_defs-2.4-1.dll
+libgmodule-2.0-0.dll
+libgnurx-0.dll
+libgobject-2.0-0.dll
+libgthread-2.0-0.dll
+libgtkmm-2.4-1.dll
+libgtk-win32-2.0-0.dll
+libharfbuzz-0.dll
+iconv.dll
+libFLAC-8.dll
+libogg-0.dll
+libvorbis-0.dll
+libvorbisenc-2.dll
+libffi-6.dll
+libidn-11.dll
+libintl-8.dll
+liblo-7.dll
+libpango-1.0-0.dll
+libpangocairo-1.0-0.dll
+libpangoft2-1.0-0.dll
+libpangomm-1.4-1.dll
+libpangowin32-1.0-0.dll
+libpixman-1-0.dll
+libpng15-15.dll
+rubberband-2.dll
+libsamplerate-0.dll
+libsigc-2.0-0.dll
+libsndfile-1.dll
+libssh2-1.dll
+libssl-10.dll
+libstdc++-6.dll
+libtag.dll
+libxml2-2.dll
+pthreadGC2.dll
+portaudio-2.dll
+vamp-hostsdk-3.dll
+vamp-sdk-2.dll
+zlib1.dll
+lilv-0.dll
+sratom-0-0.dll
+serd-0-0.dll
+sord-0-0.dll
+'
+
+WITH_JACK='TRUE'
+WITH_LV2='TRUE'
+
+. ./copydll-fedora.sh
+. ./package.sh
diff --git a/tools/windows_packaging/package-f20.sh b/tools/windows_packaging/package-f20.sh
new file mode 100755 (executable)
index 0000000..a60d53c
--- /dev/null
@@ -0,0 +1,78 @@
+#!/bin/bash
+
+DLLS='
+jack-0.dll
+jackserver-0.dll
+libatk-1.0-0.dll
+libatkmm-1.6-1.dll
+libbz2-1.dll
+libcairo-2.dll
+libcairo-gobject-2.dll
+libcairomm-1.0-1.dll
+libcairo-script-interpreter-2.dll
+libcppunit-1-12-1.dll
+libcrypto-10.dll
+libcurl-4.dll
+libexpat-1.dll
+libfftw3-3.dll
+libfftw3f-3.dll
+libfontconfig-1.dll
+libfreetype-6.dll
+libgailutil-18.dll
+libgcc_s_sjlj-1.dll
+libgdkmm-2.4-1.dll
+libgdk_pixbuf-2.0-0.dll
+libgdk-win32-2.0-0.dll
+libgio-2.0-0.dll
+libgiomm-2.4-1.dll
+libglib-2.0-0.dll
+libglibmm-2.4-1.dll
+libglibmm_generate_extra_defs-2.4-1.dll
+libgmodule-2.0-0.dll
+libgnurx-0.dll
+libgobject-2.0-0.dll
+libgthread-2.0-0.dll
+libgtkmm-2.4-1.dll
+libgtk-win32-2.0-0.dll
+libharfbuzz-0.dll
+iconv.dll
+libFLAC-8.dll
+libogg-0.dll
+libvorbis-0.dll
+libvorbisenc-2.dll
+libffi-6.dll
+libidn-11.dll
+libintl-8.dll
+liblo-7.dll
+libpango-1.0-0.dll
+libpangocairo-1.0-0.dll
+libpangoft2-1.0-0.dll
+libpangomm-1.4-1.dll
+libpangowin32-1.0-0.dll
+libpixman-1-0.dll
+libpng16-16.dll
+rubberband-2.dll
+libsamplerate-0.dll
+libsigc-2.0-0.dll
+libsndfile-1.dll
+libssh2-1.dll
+libssl-10.dll
+libstdc++-6.dll
+libtag.dll
+libxml2-2.dll
+libwinpthread-1.dll
+portaudio-2.dll
+vamp-hostsdk-3.dll
+vamp-sdk-2.dll
+zlib1.dll
+lilv-0.dll
+sratom-0-0.dll
+serd-0-0.dll
+sord-0-0.dll
+'
+
+WITH_JACK='TRUE'
+WITH_LV2='TRUE'
+
+. ./copydll-fedora.sh
+. ./package.sh
index dd1ec881196a09ee7db8defee11f0a66c9cec2ef..fda26e2f5e1fbc9175d2743fa0f3f02a0e91f55e 100755 (executable)
@@ -4,6 +4,11 @@
 
 . ./print-env.sh
 
+if [ -z "$DLLS" ]; then
+       echo "ERROR: DLLS variable is not defined..."
+       exit 1
+fi
+
 cd $BASE || exit 1
 
 if ! test -f $BUILD_CACHE_FILE; then
@@ -42,98 +47,40 @@ if test x$WITH_TESTS != x ; then
        cp -r $BASE/libs/ardour/test/data $PACKAGE_DIR/ardour_testdata
 fi
 
-echo "Copying mingw config files to $PACKAGE_DIR ..."
-# just copy it all for now
-cp -r $MINGW_ROOT/etc $PACKAGE_DIR
+echo "Copying config files to $PACKAGE_DIR ..."
+mkdir -p $PACKAGE_DIR/etc
+cp -RL $MINGW_ROOT/etc/fonts $PACKAGE_DIR/etc
+cp -RL $MINGW_ROOT/etc/gtk-2.0 $PACKAGE_DIR/etc
+cp -RL $MINGW_ROOT/etc/pango $PACKAGE_DIR/etc
 
-cp -r $MINGW_ROOT/lib/gtk-2.0 $PACKAGE_DIR/lib
-cp -r $MINGW_ROOT/lib/gdk-pixbuf-2.0 $PACKAGE_DIR/lib
+cp -R $MINGW_ROOT/lib/gtk-2.0 $PACKAGE_DIR/lib
+cp -R $MINGW_ROOT/lib/gdk-pixbuf-2.0 $PACKAGE_DIR/lib
 cp $TOOLS_DIR/loaders.cache $PACKAGE_DIR/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache
 
-mkdir -p $PACKAGE_DIR/lib/pango/1.6.0/modules
-cp -r $MINGW_ROOT/lib/pango/1.6.0/modules/*.dll $PACKAGE_DIR/lib/pango/1.6.0/modules
+mkdir -p $PACKAGE_DIR/lib/pango/1.8.0/modules
+cp -r $MINGW_ROOT/lib/pango/1.8.0/modules/*.dll $PACKAGE_DIR/lib/pango/1.8.0/modules
+
 cp $TOOLS_DIR/pango.modules $PACKAGE_DIR/etc/pango
 
 cp $TOOLS_DIR/README $PACKAGE_DIR
 
-DLLS='
-jack-0.dll
-jackserver-0.dll
-libart_lgpl_2-2.dll
-libatk-1.0-0.dll
-libatkmm-1.6-1.dll
-libbz2-1.dll
-libcairo-2.dll
-libcairo-gobject-2.dll
-libcairomm-1.0-1.dll
-libcairo-script-interpreter-2.dll
-libcppunit-1-12-1.dll
-libcrypto-10.dll
-libcurl-4.dll
-libexpat-1.dll
-libfftw3-3.dll
-libfftw3f-3.dll
-libfontconfig-1.dll
-libfreetype-6.dll
-libgailutil-18.dll
-libgcc_s_sjlj-1.dll
-libgdkmm-2.4-1.dll
-libgdk_pixbuf-2.0-0.dll
-libgdk-win32-2.0-0.dll
-libgio-2.0-0.dll
-libgiomm-2.4-1.dll
-libglib-2.0-0.dll
-libglibmm-2.4-1.dll
-libglibmm_generate_extra_defs-2.4-1.dll
-libgmodule-2.0-0.dll
-libgnomecanvas-2-0.dll
-libgnomecanvasmm-2.6-1.dll
-libgnurx-0.dll
-libgobject-2.0-0.dll
-libgthread-2.0-0.dll
-libgtkmm-2.4-1.dll
-libgtk-win32-2.0-0.dll
-libharfbuzz-0.dll
-libiconv-2.dll
-iconv.dll
-libFLAC-8.dll
-libogg-0.dll
-libvorbis-0.dll
-libvorbisenc-2.dll
-libffi-6.dll
-libidn-11.dll
-libintl-8.dll
-liblo-7.dll
-libpango-1.0-0.dll
-libpangocairo-1.0-0.dll
-libpangoft2-1.0-0.dll
-libpangomm-1.4-1.dll
-libpangowin32-1.0-0.dll
-libpixman-1-0.dll
-libpng15-15.dll
-libsamplerate-0.dll
-libsigc-2.0-0.dll
-libsndfile-1.dll
-libssh2-1.dll
-libssl-10.dll
-libstdc++-6.dll
-libxml2-2.dll
-pthreadGC2.dll
-zlib1.dll
-'
-
 echo "Copying mingw shared libraries to $PACKAGE_DIR ..."
 
 for i in $DLLS;
 do
-cp $MINGW_ROOT/bin/$i $PACKAGE_DIR
+       copydll "$i" "$PACKAGE_DIR" || exit 1
 done
 
-echo "Copying JACK server and drivers to $PACKAGE_DIR ..."
+if test x$WITH_JACK != x; then
+       echo "Copying JACK server and drivers to $PACKAGE_DIR ..."
+       cp $MINGW_ROOT/bin/jackd.exe $PACKAGE_DIR
+       cp -r $MINGW_ROOT/bin/jack $PACKAGE_DIR
+fi
 
-cp $MINGW_ROOT/bin/jackd.exe $PACKAGE_DIR
-cp -r $MINGW_ROOT/bin/jack $PACKAGE_DIR
-cp $MINGW_ROOT/bin/libportaudio-2.dll $PACKAGE_DIR
+if test x$WITH_LV2 != x; then
+       echo "Moving Bundled LV2 $PACKAGE_DIR ..."
+       mv $PACKAGE_DIR/lib/lv2 $PACKAGE_DIR/lib/ardour3/LV2
+fi
 
 SRC_DIRS='
 libs/ardour
@@ -156,9 +103,18 @@ if [ x$DEBUG = xT ]; then
        do
        cp -r -p $BASE/$i $PACKAGE_SRC_DIR/libs
        done
-       
-       echo "Copying JACK utility programs to $PACKAGE_DIR ..."
-       cp $MINGW_ROOT/bin/jack_*.exe $PACKAGE_DIR
+
+       if test x$WITH_JACK != x; then
+               echo "Copying JACK utility programs to $PACKAGE_DIR ..."
+               cp $MINGW_ROOT/bin/jack_*.exe $PACKAGE_DIR
+       fi
+
+       if test x$WITH_LV2 != x; then
+               echo "Copying LV2 utility programs to $PACKAGE_DIR ..."
+               cp $MINGW_ROOT/bin/lilv-bench.exe $PACKAGE_DIR
+               cp $MINGW_ROOT/bin/lv2info.exe $PACKAGE_DIR
+               cp $MINGW_ROOT/bin/lv2ls.exe $PACKAGE_DIR
+       fi
 
        #echo "Copying any debug files to $PACKAGE_DIR ..."
        #cp $MINGW_ROOT/bin/*.debug $PACKAGE_DIR
index 115c6a5fd896540d9eede9deeebf1ea935aa5f4c..5ea7516bd1ea9659f0ef386ad7a86b9dd53dc511 100644 (file)
@@ -1,35 +1,18 @@
-# Pango Modules file
-# Automatically generated file, do not edit
-#
-# ModulesPath = Z:\usr\i686-pc-mingw32\sys-root\mingw\lib\pango\1.6.0\modules
-#
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-thai-fc.dll" ThaiScriptEngineFc PangoEngineShape PangoRenderFc thai:* lao:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-basic-win32.dll" BasicScriptEngineWin32 PangoEngineShape PangoRenderWin32 common:
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-fc.dll" devaScriptEngineFc PangoEngineShape PangoRenderFc devanagari:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-fc.dll" bengScriptEngineFc PangoEngineShape PangoRenderFc bengali:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-fc.dll" guruScriptEngineFc PangoEngineShape PangoRenderFc gurmukhi:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-fc.dll" gujrScriptEngineFc PangoEngineShape PangoRenderFc gujarati:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-fc.dll" oryaScriptEngineFc PangoEngineShape PangoRenderFc oriya:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-fc.dll" tamlScriptEngineFc PangoEngineShape PangoRenderFc tamil:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-fc.dll" teluScriptEngineFc PangoEngineShape PangoRenderFc telugu:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-fc.dll" kndaScriptEngineFc PangoEngineShape PangoRenderFc kannada:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-fc.dll" mlymScriptEngineFc PangoEngineShape PangoRenderFc malayalam:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-fc.dll" sinhScriptEngineFc PangoEngineShape PangoRenderFc sinhala:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-syriac-fc.dll" SyriacScriptEngineFc PangoEngineShape PangoRenderFc syriac:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-khmer-fc.dll" KhmerScriptEngineFc PangoEngineShape PangoRenderFc khmer:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-arabic-lang.dll" ArabicScriptEngineLang PangoEngineLang PangoRenderNone arabic:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-basic-fc.dll" BasicScriptEngineFc PangoEngineShape PangoRenderFc latin:* cyrillic:* greek:* armenian:* georgian:* runic:* ogham:* bopomofo:* cherokee:* coptic:* deseret:* ethiopic:* gothic:* han:* hiragana:* katakana:* old-italic:* canadian-aboriginal:* yi:* braille:* cypriot:* limbu:* osmanya:* shavian:* linear-b:* ugaritic:* glagolitic:* cuneiform:* phoenician:* common:
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-arabic-fc.dll" ArabicScriptEngineFc PangoEngineShape PangoRenderFc arabic:* nko:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-lang.dll" devaIndicScriptEngineLang PangoEngineLang PangoRenderNone devanagari:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-lang.dll" bengIndicScriptEngineLang PangoEngineLang PangoRenderNone bengali:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-lang.dll" guruIndicScriptEngineLang PangoEngineLang PangoRenderNone gurmukhi:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-lang.dll" gujrIndicScriptEngineLang PangoEngineLang PangoRenderNone gujarati:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-lang.dll" oryaIndicScriptEngineLang PangoEngineLang PangoRenderNone oriya:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-lang.dll" tamlIndicScriptEngineLang PangoEngineLang PangoRenderNone tamil:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-lang.dll" teluIndicScriptEngineLang PangoEngineLang PangoRenderNone telugu:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-lang.dll" kndaIndicScriptEngineLang PangoEngineLang PangoRenderNone kannada:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-lang.dll" mlymIndicScriptEngineLang PangoEngineLang PangoRenderNone malayalam:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-lang.dll" sinhIndicScriptEngineLang PangoEngineLang PangoRenderNone sinhala:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-hebrew-fc.dll" HebrewScriptEngineFc PangoEngineShape PangoRenderFc hebrew:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-tibetan-fc.dll" TibetanScriptEngineFc PangoEngineShape PangoRenderFc tibetan:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-hangul-fc.dll" HangulScriptEngineFc PangoEngineShape PangoRenderFc hangul:*
+# Pango Modules file\r
+# Automatically generated file, do not edit\r
+#\r
+# ModulesPath = Z:\usr\i686-w64-mingw32\sys-root\mingw\lib\pango\1.8.0\modules\r
+#\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-arabic-lang.dll" ArabicScriptEngineLang PangoEngineLang PangoRenderNone arabic:*\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-basic-fc.dll" BasicScriptEngineFc PangoEngineShape PangoRenderFc common:\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-indic-lang.dll" devaIndicScriptEngineLang PangoEngineLang PangoRenderNone devanagari:*\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-indic-lang.dll" bengIndicScriptEngineLang PangoEngineLang PangoRenderNone bengali:*\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-indic-lang.dll" guruIndicScriptEngineLang PangoEngineLang PangoRenderNone gurmukhi:*\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-indic-lang.dll" gujrIndicScriptEngineLang PangoEngineLang PangoRenderNone gujarati:*\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-indic-lang.dll" oryaIndicScriptEngineLang PangoEngineLang PangoRenderNone oriya:*\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-indic-lang.dll" tamlIndicScriptEngineLang PangoEngineLang PangoRenderNone tamil:*\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-indic-lang.dll" teluIndicScriptEngineLang PangoEngineLang PangoRenderNone telugu:*\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-indic-lang.dll" kndaIndicScriptEngineLang PangoEngineLang PangoRenderNone kannada:*\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-indic-lang.dll" mlymIndicScriptEngineLang PangoEngineLang PangoRenderNone malayalam:*\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-indic-lang.dll" sinhIndicScriptEngineLang PangoEngineLang PangoRenderNone sinhala:*\r
+\r
index 1a3b82f4a37794d67040804d18b3c1c7e30e89d4..d92bae07f4a082adc97b428ef9290aca9a1df004 100644 (file)
@@ -4,13 +4,13 @@
 # Running Ardour requires these 3 variables to be set
 #
 
-export ARDOUR_DATA_PATH=@DATADIR@/ardour3
-export ARDOUR_CONFIG_PATH=@SYSCONFDIR@/ardour3
-export ARDOUR_DLL_PATH=@LIBDIR@/ardour3
+export ARDOUR_DATA_PATH=@DATADIR@
+export ARDOUR_CONFIG_PATH=@CONFDIR@
+export ARDOUR_DLL_PATH=@LIBDIR@
 
-export GTK_PATH=@LIBDIR@/ardour3${GTK_PATH:+:$GTK_PATH}
-export LD_LIBRARY_PATH=@LIBDIR@/ardour3${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
+export GTK_PATH=@LIBDIR@${GTK_PATH:+:$GTK_PATH}
+export LD_LIBRARY_PATH=@LIBDIR@${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
 
-exec wine @LIBDIR@/ardour3/ardour-@VERSION@-vst.exe.so "$@"
+exec wine @LIBDIR@/ardour-@VERSION@-vst.exe.so "$@"
 
 
diff --git a/wscript b/wscript
index 52d1ec6e28ad31fb9fc657aab65e170ff1e35712..bef9e29d10e2f95306928eef4ed9c0c29a82c110 100644 (file)
--- a/wscript
+++ b/wscript
@@ -30,13 +30,17 @@ else:
 
 #
 # rev is now of the form MAJOR.MINOR-rev-commit
+# or, if right at the same rev as a release, MAJOR.MINOR
 #
 
 parts = rev.split ('.')
 MAJOR = parts[0]
 other = parts[1].split ('-')
 MINOR = other[0]
-MICRO = other[1]
+if len(other) > 1:
+    MICRO = other[1]
+else:
+    MICRO = '0'
 
 V = MAJOR + '.' + MINOR + '.' + MICRO
 #
@@ -77,6 +81,7 @@ children = [
         # shared helper binaries (plugin-scanner, exec-wrapper)
         'libs/fst',
         'libs/vfork',
+        'libs/ardouralsautil',
 ]
 
 i18n_children = [
@@ -148,15 +153,6 @@ def set_compiler_flags (conf,opt):
     # waf adds -O0 -g itself. thanks waf!
     is_clang = conf.env['CXX'][0].endswith('clang++')
     
-    if conf.options.cxx11:
-        conf.check_cxx(cxxflags=["-std=c++11"])
-        cxx_flags.append('-std=c++11')
-        if platform == "darwin":
-            cxx_flags.append('-stdlib=libc++')
-            link_flags.append('-lc++')
-            # Prevents visibility issues in standard headers
-            conf.define("_DARWIN_C_SOURCE", 1)
-
     if conf.options.asan:
         conf.check_cxx(cxxflags=["-fsanitize=address", "-fno-omit-frame-pointer"], linkflags=["-fsanitize=address"])
         cxx_flags.append('-fsanitize=address')
@@ -186,8 +182,10 @@ def set_compiler_flags (conf,opt):
                 conf.env['build_target'] = 'snowleopard'
             elif re.search ("^11[.]", version) != None:
                 conf.env['build_target'] = 'lion'
-            else:
+            elif re.search ("^12[.]", version) != None:
                 conf.env['build_target'] = 'mountainlion'
+            else:
+                conf.env['build_target'] = 'mavericks' # 13.0.0
         else:
             match = re.search(
                     "(?P<cpu>i[0-6]86|x86_64|powerpc|ppc|ppc64|arm|s390x?)",
@@ -208,25 +206,16 @@ def set_compiler_flags (conf,opt):
         # 
         compiler_flags.append ('-U__STRICT_ANSI__')
 
-    if cpu == 'powerpc' and conf.env['build_target'] != 'none':
-        #
-        # Apple/PowerPC optimization options
-        #
-        # -mcpu=7450 does not reliably work with gcc 3.*
-        #
-        if opt.dist_target == 'panther' or opt.dist_target == 'tiger':
-            if platform == 'darwin':
-                # optimization_flags.extend ([ "-mcpu=7450", "-faltivec"])
-                # to support g3s but still have some optimization for above
-                compiler_flags.extend ([ "-mcpu=G3", "-mtune=7450"])
-            else:
-                compiler_flags.extend ([ "-mcpu=7400", "-maltivec", "-mabi=altivec"])
-        else:
-            compiler_flags.extend([ "-mcpu=750", "-mmultiple" ])
-        compiler_flags.extend (["-mhard-float", "-mpowerpc-gfxopt"])
-        optimization_flags.extend (["-Os"])
+    if conf.options.cxx11 or conf.env['build_target'] == 'mavericks':
+        conf.check_cxx(cxxflags=["-std=c++11"])
+        cxx_flags.append('-std=c++11')
+        if platform == "darwin":
+            cxx_flags.append('-stdlib=libc++')
+            linker_flags.append('-lc++')
+            # Prevents visibility issues in standard headers
+            conf.define("_DARWIN_C_SOURCE", 1)
 
-    elif ((re.search ("i[0-9]86", cpu) != None) or (re.search ("x86_64", cpu) != None)) and conf.env['build_target'] != 'none':
+    if ((re.search ("i[0-9]86", cpu) != None) or (re.search ("x86_64", cpu) != None)) and conf.env['build_target'] != 'none':
 
 
         #
@@ -358,23 +347,9 @@ def set_compiler_flags (conf,opt):
     if conf.env['DEBUG_DENORMAL_EXCEPTION']:
         compiler_flags.append('-DDEBUG_DENORMAL_EXCEPTION')
 
-    if opt.universal:
-        if opt.generic:
-            print ('Specifying Universal and Generic builds at the same time is not supported')
-            sys.exit (1)
-        else:
-            if not Options.options.nocarbon:
-                compiler_flags.extend(("-arch", "i386", "-arch", "ppc"))
-                linker_flags.extend(("-arch", "i386", "-arch", "ppc"))
-            else:
-                compiler_flags.extend(
-                        ("-arch", "x86_64", "-arch", "i386", "-arch", "ppc"))
-                linker_flags.extend(
-                        ("-arch", "x86_64", "-arch", "i386", "-arch", "ppc"))
-    else:
-        if opt.generic:
-            compiler_flags.extend(('-arch', 'i386'))
-            linker_flags.extend(('-arch', 'i386'))
+    if opt.generic:
+        compiler_flags.extend(('-arch', 'i386'))
+        linker_flags.extend(('-arch', 'i386'))
 
     #
     # warnings flags
@@ -434,6 +409,10 @@ def options(opt):
                     help='Whether to build for TRX')
     opt.add_option('--arch', type='string', action='store', dest='arch',
                     help='Architecture-specific compiler flags')
+    opt.add_option('--with-dummy', action='store_true', default=False, dest='build_dummy',
+                   help='Build the dummy backend (no audio/MIDI I/O, useful for profiling)')
+    opt.add_option('--with-alsabackend', action='store_true', default=False, dest='build_alsabackend',
+                   help='Build the ALSA backend')
     opt.add_option('--backtrace', action='store_true', default=True, dest='backtrace',
                     help='Compile with -rdynamic -- allow obtaining backtraces from within Ardour')
     opt.add_option('--no-carbon', action='store_true', default=False, dest='nocarbon',
@@ -443,7 +422,7 @@ def options(opt):
     opt.add_option('--depstack-root', type='string', default='~', dest='depstack_root',
                     help='Directory/folder where dependency stack trees (gtk, a3) can be found (defaults to ~)')
     opt.add_option('--dist-target', type='string', default='auto', dest='dist_target',
-                    help='Specify the target for cross-compiling [auto,none,x86,i386,i686,x86_64,powerpc,tiger,leopard,mingw]')
+                    help='Specify the target for cross-compiling [auto,none,x86,i386,i686,x86_64,tiger,leopard,mingw]')
     opt.add_option('--fpu-optimization', action='store_true', default=True, dest='fpu_optimization',
                     help='Build runtime checked assembler code (default)')
     opt.add_option('--no-fpu-optimization', action='store_false', dest='fpu_optimization')
@@ -490,8 +469,6 @@ def options(opt):
                     help="Build a single executable for each unit test")
     #opt.add_option('--tranzport', action='store_true', default=False, dest='tranzport',
     # help='Compile with support for Frontier Designs Tranzport (if libusb is available)')
-    opt.add_option('--universal', action='store_true', default=False, dest='universal',
-                    help='Compile as universal binary (OS X ONLY, requires that external libraries are universal)')
     opt.add_option('--generic', action='store_true', default=False, dest='generic',
                     help='Compile with -arch i386 (OS X ONLY)')
     opt.add_option('--versioned', action='store_true', default=False, dest='versioned',
@@ -665,6 +642,7 @@ def configure(conf):
 
     # executing a test program is n/a when cross-compiling
     if Options.options.dist_target != 'mingw':
+        conf.check_cc(function_name='dlopen', header_name='dlfcn.h', lib='dl', uselib_store='DL')
         conf.check_cxx(fragment = "#include <boost/version.hpp>\nint main(void) { return (BOOST_VERSION >= 103900 ? 0 : 1); }\n",
                   execute = "1",
                   mandatory = True,
@@ -672,6 +650,9 @@ def configure(conf):
                   okmsg = 'ok',
                   errmsg = 'too old\nPlease install boost version 1.39 or higher.')
 
+    if re.search ("linux", sys.platform) != None and Options.options.dist_target != 'mingw':
+        autowaf.check_pkg(conf, 'alsa', uselib_store='ALSA')
+
     autowaf.check_pkg(conf, 'glib-2.0', uselib_store='GLIB', atleast_version='2.2', mandatory=True)
     autowaf.check_pkg(conf, 'gthread-2.0', uselib_store='GTHREAD', atleast_version='2.2', mandatory=True)
     autowaf.check_pkg(conf, 'glibmm-2.4', uselib_store='GLIBMM', atleast_version='2.32.0', mandatory=True)
@@ -701,16 +682,6 @@ def configure(conf):
         # TODO put this only where it is needed
         conf.env.append_value('LIB', 'regex')
 
-    if Options.options.dist_target != 'mingw':
-        conf.check_cc(function_name='dlopen', header_name='dlfcn.h', lib='dl', uselib_store='DL')
-
-        conf.check_cxx(fragment = "#include <boost/version.hpp>\nint main(void) { return (BOOST_VERSION >= 103900 ? 0 : 1); }\n",
-                      execute = "1",
-                      mandatory = True,
-                      msg = 'Checking for boost library >= 1.39',
-                      okmsg = 'ok',
-                      errmsg = 'too old\nPlease install boost version 1.39 or higher.')
-
     # Tell everyone that this is a waf build
 
     conf.env.append_value('CFLAGS', '-DWAF_BUILD')
@@ -775,6 +746,10 @@ def configure(conf):
         conf.env['DEBUG_DENORMAL_EXCEPTION'] = True
     if opts.build_tests:
         autowaf.check_pkg(conf, 'cppunit', uselib_store='CPPUNIT', atleast_version='1.12.0', mandatory=True)
+    if opts.build_alsabackend:
+        conf.env['BUILD_ALSABACKEND'] = True
+    if opts.build_dummy:
+        conf.env['BUILD_DUMMYBACKEND'] = True
 
     set_compiler_flags (conf, Options.options)
 
@@ -813,6 +788,8 @@ const char* const ardour_config_info = "\\n\\
     write_config_text('Use External Libraries', conf.is_defined('USE_EXTERNAL_LIBS'))
     write_config_text('Library exports hidden', conf.is_defined('EXPORT_VISIBILITY_HIDDEN'))
 
+    write_config_text('ALSA Backend',          opts.build_alsabackend)
+    write_config_text('ALSA DBus Reservation', conf.is_defined('HAVE_DBUS'))
     write_config_text('Architecture flags',    opts.arch)
     write_config_text('Aubio',                 conf.is_defined('HAVE_AUBIO'))
     write_config_text('AudioUnits',            conf.is_defined('AUDIOUNIT_SUPPORT'))
@@ -820,6 +797,7 @@ const char* const ardour_config_info = "\\n\\
     write_config_text('Build target',          conf.env['build_target'])
     write_config_text('CoreAudio',             conf.is_defined('HAVE_COREAUDIO'))
     write_config_text('Debug RT allocations',  conf.is_defined('DEBUG_RT_ALLOC'))
+    write_config_text('Dummy backend',         opts.build_dummy)
     write_config_text('Process thread timing', conf.is_defined('PT_TIMING'))
     write_config_text('Denormal exceptions',   conf.is_defined('DEBUG_DENORMAL_EXCEPTION'))
     write_config_text('FLAC',                  conf.is_defined('HAVE_FLAC'))
@@ -836,7 +814,6 @@ const char* const ardour_config_info = "\\n\\
     write_config_text('Translation',           opts.nls)
 #    write_config_text('Tranzport',             opts.tranzport)
     write_config_text('Unit tests',            conf.env['BUILD_TESTS'])
-    write_config_text('Universal binary',      opts.universal)
     write_config_text('Generic x86 CPU',       opts.generic)
     write_config_text('Windows VST support',   opts.windows_vst)
     write_config_text('Wiimote support',       conf.is_defined('BUILD_WIIMOTE'))
@@ -891,7 +868,7 @@ def build(bld):
     for i in children:
         bld.recurse(i)
 
-    bld.install_files (bld.env['SYSCONFDIR'], 'ardour_system.rc')
+    bld.install_files (bld.env['CONFDIR'], 'ardour_system.rc')
 
     if bld.env['RUN_TESTS']:
         bld.add_post_fun(test)