diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2007-10-11 22:07:47 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2007-10-11 22:07:47 +0000 |
commit | f7f9d6fdc40248b190ec9c6e1a886261d55777ae (patch) | |
tree | 080723e9dc35a66013b37acbafc67a6afa929302 /gtk2_ardour | |
parent | aa1f736a651376534acaa2268b65d42a3786fff7 (diff) |
merge from 2.0-ongoing by hand, minus key binding editor
git-svn-id: svn://localhost/ardour2/trunk@2539 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'gtk2_ardour')
59 files changed, 5014 insertions, 1305 deletions
diff --git a/gtk2_ardour/SConscript b/gtk2_ardour/SConscript index b96498602e..12e3225f89 100644 --- a/gtk2_ardour/SConscript +++ b/gtk2_ardour/SConscript @@ -13,7 +13,7 @@ gtkmmtests = env.Copy() # this defines the version number of the GTK interface to ardour # -domain = 'gtk_ardour' +domain = 'gtk2_ardour' gtkardour.Append(DOMAIN=domain, MAJOR=1,MINOR=0,MICRO=2) gtkardour.Append(CCFLAGS="-DPACKAGE=\\\"" + domain + "\\\"") @@ -24,6 +24,9 @@ gtkardour.Append(CPPPATH="#/") # for top level svn_revision.h gtkardour.Append(PACKAGE=domain) gtkardour.Append(POTFILE=domain + '.pot') +if gtkardour['DIST_TARGET'] == 'panther' or gtkardour['DIST_TARGET'] == 'tiger': + gtkardour.Append (LINKFLAGS="-Xlinker -headerpad -Xlinker 2048") + gtkardour.Merge ([ libraries['ardour'], libraries['ardour_cp'], @@ -48,7 +51,8 @@ gtkardour.Merge ([ libraries['xslt'], libraries['soundtouch'], libraries['samplerate'], - libraries['jack'] + libraries['jack'], + libraries['sysaudio'] ]) gtkmmtests.Append(CXXFLAGS="-DLIBSIGC_DISABLE_DEPRECATED") @@ -80,6 +84,15 @@ audiounit_files=Split(""" au_pluginui.cc """) +gtkosx_files=Split(""" +sync-menu.c +cocoacarbon.c +""") + +x11_files=Split(""" +x11.cc +""") + gtkardour_files=Split(""" about.cc actions.cc @@ -143,6 +156,7 @@ editor_selection.cc editor_selection_list.cc editor_tempodisplay.cc editor_timefx.cc +engine_dialog.cc export_dialog.cc export_session_dialog.cc export_region_dialog.cc @@ -258,6 +272,12 @@ if env['CMT']: extra_sources += cmt_files gtkardour.Append (CCFLAGS="-DWITH_CMT") +if gtkardour['GTKOSX']: + extra_sources += gtkosx_files + gtkardour.Append (CCFLAGS="-DTOP_MENUBAR -DGTKOSX") +else: + extra_sources += x11_files + if gtkardour['AUDIOUNITS']: extra_sources += audiounit_files gtkardour.Append(CCFLAGS='-DHAVE_AUDIOUNITS') @@ -292,41 +312,43 @@ if gtkardour['DIST_TARGET'] == 'panther' or gtkardour['DIST_TARGET'] == 'tiger': # # OS X font rendering is different even with X11 # - my_font_dict['%FONT_TINY%'] = 'sans 7' - my_font_dict['%FONT_SMALLER%'] = 'sans 9' - my_font_dict['%FONT_SMALL%'] = 'sans 10' - my_font_dict['%FONT_NORMAL%'] = 'sans 11' - my_font_dict['%FONT_BIG%'] = 'sans 15' - my_font_dict['%FONT_BIGGER%'] = 'sans 16' - my_font_dict['%FONT_LARGE%'] = 'sans 20' - my_font_dict['%FONT_LARGER%'] = 'sans 28' - my_font_dict['%FONT_HUGER%'] = 'sans 36' - my_font_dict['%FONT_MASSIVE%'] = 'sans 60' - my_font_dict['%FONT_BOLD_TINY%'] = 'sans bold 7' - my_font_dict['%FONT_BOLD_SMALLER%'] = 'sans bold 9' - my_font_dict['%FONT_BOLD_SMALL%'] = 'sans bold 10' - my_font_dict['%FONT_BOLD_NORMAL%'] = 'sans bold 11' - my_font_dict['%FONT_BOLD_BIG%'] = 'sans bold 15' - my_font_dict['%FONT_BOLD_BIGGER%'] = 'sans bold 16' - my_font_dict['%FONT_BOLD_LARGE%'] = 'sans bold 20' - my_font_dict['%FONT_BOLD_LARGER%'] = 'sans bold 28' - my_font_dict['%FONT_BOLD_HUGER%'] = 'sans bold 36' - my_font_dict['%FONT_BOLD_MASSIVE%'] = 'sans bold 60' - my_font_dict['%FONT_ITALIC_TINY%'] = 'sans italic 7' - my_font_dict['%FONT_ITALIC_SMALLER%'] = 'sans italic 9' - my_font_dict['%FONT_ITALIC_SMALL%'] = 'sans italic 10' - my_font_dict['%FONT_ITALIC_NORMAL%'] = 'sans italic 11' - my_font_dict['%FONT_ITALIC_BIG%'] = 'sans italic 15' - my_font_dict['%FONT_ITALIC_BIGGER%'] = 'sans italic 16' - my_font_dict['%FONT_ITALIC_LARGE%'] = 'sans italic 20' - my_font_dict['%FONT_ITALIC_LARGER%'] = 'sans italic 28' - my_font_dict['%FONT_ITALIC_HUGER%'] = 'sans italic 36' - my_font_dict['%FONT_ITALIC_MASSIVE%'] = 'sans italic 60' + my_font_dict['%FONT_TINY%'] = 'Lucida Grande 7' + my_font_dict['%FONT_SMALLERER%'] = 'Lucida Grande 8' + my_font_dict['%FONT_SMALLER%'] = 'Lucida Grande 9' + my_font_dict['%FONT_SMALL%'] = 'Lucida Grande 10' + my_font_dict['%FONT_NORMAL%'] = 'Lucida Grande 11' + my_font_dict['%FONT_BIG%'] = 'Lucida Grande 12' + my_font_dict['%FONT_BIGGER%'] = 'Lucida Grande 14' + my_font_dict['%FONT_LARGE%'] = 'Lucida Grande 18' + my_font_dict['%FONT_LARGER%'] = 'Lucida Grande 28' + my_font_dict['%FONT_HUGER%'] = 'Lucida Grande 36' + my_font_dict['%FONT_MASSIVE%'] = 'Lucida Grande 60' + my_font_dict['%FONT_BOLD_TINY%'] = 'Lucida Grande bold 7' + my_font_dict['%FONT_BOLD_SMALLER%'] = 'Lucida Grande bold 9' + my_font_dict['%FONT_BOLD_SMALL%'] = 'Lucida Grande bold 10' + my_font_dict['%FONT_BOLD_NORMAL%'] = 'Lucida Grande bold 11' + my_font_dict['%FONT_BOLD_BIG%'] = 'Lucida Grande bold 13' + my_font_dict['%FONT_BOLD_BIGGER%'] = 'Lucida Grande bold 14' + my_font_dict['%FONT_BOLD_LARGE%'] = 'Lucida Grande bold 20' + my_font_dict['%FONT_BOLD_LARGER%'] = 'Lucida Grande bold 25' + my_font_dict['%FONT_BOLD_HUGER%'] = 'Lucida Grande bold 36' + my_font_dict['%FONT_BOLD_MASSIVE%'] = 'Lucida Grande bold 60' + my_font_dict['%FONT_ITALIC_TINY%'] = 'Lucida Grande italic 7' + my_font_dict['%FONT_ITALIC_SMALLER%'] = 'Lucida Grande italic 9' + my_font_dict['%FONT_ITALIC_SMALL%'] = 'Lucida Grande italic 10' + my_font_dict['%FONT_ITALIC_NORMAL%'] = 'Lucida Grande italic 11' + my_font_dict['%FONT_ITALIC_BIG%'] = 'Lucida Grande italic 15' + my_font_dict['%FONT_ITALIC_BIGGER%'] = 'Lucida Grande italic 16' + my_font_dict['%FONT_ITALIC_LARGE%'] = 'Lucida Grande italic 20' + my_font_dict['%FONT_ITALIC_LARGER%'] = 'Lucida Grande italic 28' + my_font_dict['%FONT_ITALIC_HUGER%'] = 'Lucida Grande italic 36' + my_font_dict['%FONT_ITALIC_MASSIVE%'] = 'Lucida Grande italic 60' else: # # Linux/X11 font rendering # my_font_dict['%FONT_TINY%'] = 'sans 4' + my_font_dict['%FONT_SMALLERER%'] = 'sans 6' my_font_dict['%FONT_SMALLER%'] = 'sans 6' my_font_dict['%FONT_SMALL%'] = 'sans 7' my_font_dict['%FONT_NORMAL%'] = 'sans 8' @@ -411,6 +433,7 @@ if env['NLS']: env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour2'), ardour_dark_theme)) env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour2'), ardour_light_theme)) env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour2'), 'ardour.menus')) +env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour2'), 'ardour-sae.menus')) env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour2'), 'ardour.bindings')) env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour2'), 'ardour2_ui_default.conf')) # data files @@ -428,7 +451,9 @@ env.Alias ('tarball', env.Distribute (env['DISTTREE'], 'ardev_common.sh.in', 'ardev', 'ardbg', 'ardour2_ui_dark.rc', 'ardour2_ui_light.rc', 'splash.png', - 'ardour.menus', 'ardour.bindings.in', 'ardour2_ui_default.conf', + 'ardour.menus', + 'ardour-sae.menus', + 'ardour.bindings.in', 'ardour2_ui_default.conf', 'editor_xpms' ] + gtkardour_files + @@ -437,6 +462,8 @@ env.Alias ('tarball', env.Distribute (env['DISTTREE'], icon_files + skipped_files + audiounit_files + + gtkosx_files + + x11_files + fft_analysis_files + glob.glob('po/*.po') + glob.glob('*.h'))) diff --git a/gtk2_ardour/ardbg b/gtk2_ardour/ardbg index ab99296f45..063b2b1b6d 100755 --- a/gtk2_ardour/ardbg +++ b/gtk2_ardour/ardbg @@ -2,4 +2,4 @@ dir=`dirname "$0"` . $dir/ardev_common.sh LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH -exec gdb $EXECUTABLE $* +exec gdb $EXECUTABLE "$@" diff --git a/gtk2_ardour/ardev b/gtk2_ardour/ardev index 5dd8fc9d13..7980c43d93 100755 --- a/gtk2_ardour/ardev +++ b/gtk2_ardour/ardev @@ -1,4 +1,4 @@ #!/bin/sh . `dirname "$0"`/ardev_common.sh LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH -exec $EXECUTABLE $* +exec $EXECUTABLE "$@" diff --git a/gtk2_ardour/ardour.menus b/gtk2_ardour/ardour.menus index 24adba9f7f..128ca81c27 100644 --- a/gtk2_ardour/ardour.menus +++ b/gtk2_ardour/ardour.menus @@ -13,13 +13,7 @@ <separator/> <menuitem action='AddTrackBus'/> <separator/> - <menu action='addExistingAudioFiles'> - <menuitem action='addExternalAudioAsRegion'/> - <menuitem action='addExternalAudioToTrack'/> - <separator/> - <menuitem action='addExternalAudioAsTrack'/> - <menuitem action='addExternalAudioAsTapeTrack'/> - </menu> + <menuitem action='addExistingAudioFiles'/> <separator/> <menu name='Export' action='Export'> <menuitem action='ExportSession'/> @@ -140,7 +134,6 @@ <menuitem action='set-mouse-mode-gain'/> <menuitem action='set-mouse-mode-zoom'/> <menuitem action='set-mouse-mode-timefx'/> - <menuitem action='set-mouse-mode-note'/> </menu> </menu> <menu name='View' action = 'View'> @@ -215,7 +208,7 @@ <menuitem action='ToggleMeasureVisibility'/> <separator/> <menuitem action='show-editor-mixer'/> - <menuitem action='show-editor-list'/> + <menuitem action='SyncEditorAndMixerTrackOrder'/> </menu> <menu name='JACK' action='JACK'> <menuitem action='JACKDisconnect'/> @@ -304,10 +297,6 @@ <menuitem action='UseSoftwareMonitoring'/> <menuitem action='UseExternalMonitoring'/> </menu> - <menu action='Plugins'> - <menuitem action='DisableAllPlugins'/> - <menuitem action='ABAllPlugins'/> - </menu> <menu action='Metering'> <menu action='MeteringFallOffRate'> <menuitem action='MeterFalloffOff'/> @@ -370,13 +359,15 @@ <menuitem action='GainReduceFastTransport'/> <menuitem action='PrimaryClockDeltaEditCursor'/> <menuitem action='SecondaryClockDeltaEditCursor'/> + <menuitem action='OnlyCopyImportedFiles'/> + <separator/> </menu> <menu name='Help' action='Help'> <menuitem action='About'/> </menu> </menubar> - <popup name='processormenu'> + <popup name='redirectmenu'> <menuitem action='newplugin'/> <menuitem action='newinsert'/> <menuitem action='newsend'/> @@ -399,9 +390,6 @@ <menuitem action='activate_all'/> <menuitem action='deactivate_all'/> <separator/> - <menuitem action='deactivate_plugins'/> - <menuitem action='a_b_plugins'/> - <separator/> <menuitem action='edit'/> </popup> diff --git a/gtk2_ardour/ardour.sh.in b/gtk2_ardour/ardour.sh.in index beed436cbe..2f1092c3fe 100644 --- a/gtk2_ardour/ardour.sh.in +++ b/gtk2_ardour/ardour.sh.in @@ -15,6 +15,6 @@ fi ulimit -c unlimited -exec %INSTALL_PREFIX%/%LIBDIR%/ardour2/ardour-%VERSION% $* +exec %INSTALL_PREFIX%/%LIBDIR%/ardour2/ardour-%VERSION% "$@" diff --git a/gtk2_ardour/ardour2_ui_dark.rc.in b/gtk2_ardour/ardour2_ui_dark.rc.in index 1420fc8660..ba8e5fe1e1 100644 --- a/gtk2_ardour/ardour2_ui_dark.rc.in +++ b/gtk2_ardour/ardour2_ui_dark.rc.in @@ -4,17 +4,17 @@ style "very_small_text" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" } style "small_text" { - font_name = "%FONT_NORMAL%" + font_name = "%FONT_SMALL%" } style "small_bold_text" { - font_name = "%FONT_BOLD_NORMAL%" + font_name = "%FONT_BOLD_SMALL%" } style "medium_bold_text" @@ -73,7 +73,7 @@ style "verbose_canvas_cursor" style "marker_text" { - font_name = "%FONT_NORMAL%" + font_name = "%FONT_SMALL%" } style "time_axis_view_item_name" @@ -159,7 +159,7 @@ style "default_buttons_menus" style "very_small_button" = "default_buttons_menus" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" ythickness = 0 xthickness = 0 } @@ -220,21 +220,21 @@ style "track_rec_enable_button_alternate" = "small_button" style "mixer_track_rec_enable_button" = "track_rec_enable_button" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" xthickness = 0 ythickness = 0 } style "mixer_track_rec_enable_button_alternate" = "track_rec_enable_button_alternate" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" xthickness = 0 ythickness = 0 } style "mixer_track_rec_enable_button_active" = "track_rec_enable_button_active" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" xthickness = 0 ythickness = 0 } @@ -272,20 +272,20 @@ style "solo_button_active" = "small_button" style "mixer_solo_button" = "solo_button" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } style "mixer_solo_button_alternate" = "solo_button_alternate" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } style "mixer_solo_button_active" = "solo_button_active" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } @@ -323,21 +323,21 @@ style "mute_button_active" = "small_button" style "mixer_mute_button" = "mute_button" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } style "mixer_mute_button_alternate" = "mute_button_alternate" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } style "mixer_mute_button_active" = "mute_button_active" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } @@ -351,7 +351,7 @@ style "multiline_combo" = "small_button" style "mixer_mute_button" = "mute_button" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" xthickness = 0 ythickness = 0 } @@ -454,7 +454,7 @@ style "ardour_progressbars" = "default_buttons_menus" bg[PRELIGHT] = { 0.00, 0.36, 0.40 } } -style "options_window" = "default_base" +style "preferences" = "default_base" { font_name = "%FONT_NORMAL%" fg[PRELIGHT] = { 0.80, 0.80, 0.80 } @@ -667,7 +667,7 @@ style "transport_clock_display_delta" = "transport_clock_display" style "tempo_meter_clock_display" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" fg[NORMAL] = { 1.0, 1.0, 1.0 } fg[ACTIVE] = { 1.0, 1.0, 0.0 } fg[SELECTED] = { 1.0, 0, 0 } @@ -694,31 +694,6 @@ style "editor_time_ruler" = "small_text" bg[NORMAL] = { 0.09, 0.09, 0.09 } } -style "time_bar_label_base" = "default_base" -{ - font_name = "sans 6" - fg[NORMAL] = { 0.77, 0.77, 0.72 } - bg[NORMAL] = { 0.28, 0.29, 0.34 } - bg[ACTIVE] = { 0.30, 0.30, 0.34 } - bg[PRELIGHT] = { 0.30, 0.30, 0.34 } - bg[INSENSITIVE] = { 0.30, 0.30, 0.34 } - bg[SELECTED] = { 0.30, 0.30, 0.34 } -} - -style "time_bar_padding" = "default_base" -{ - fg[NORMAL] = { 0.09, 0.09, 0.09 } - fg[ACTIVE] = { 0.09, 0.09, 0.09 } - fg[PRELIGHT] = { 0.09, 0.09, 0.09 } - fg[INSENSITIVE] = { 0.09, 0.09, 0.09 } - fg[SELECTED] = { 0.09, 0.09, 0.09 } - bg[NORMAL] = { 0.09, 0.09, 0.09 } - bg[ACTIVE] = { 0.09, 0.09, 0.09 } - bg[PRELIGHT] = { 0.09, 0.09, 0.09 } - bg[INSENSITIVE] = { 0.09, 0.09, 0.09 } - bg[SELECTED] = { 0.09, 0.09, 0.09 } -} - style "audio_bus_base" { font_name = "%FONT_SMALLER%" @@ -758,6 +733,38 @@ style "midi_track_base" = "default_base" bg[SELECTED] = { 0.20, 0.20, 0.20 } } +style "track_controls_inactive" +{ + bg[NORMAL] = { 0.60, 0.60, 0.66 } + bg[ACTIVE] = { 0.60, 0.60, 0.66 } + bg[INSENSITIVE] = { 0.60, 0.60, 0.66 } + bg[SELECTED] = { 0.60, 0.60, 0.66 } + bg[PRELIGHT] = { 0.60, 0.60, 0.66 } + + #font_name = "sans 18" + fg[NORMAL] = { 0.7, 0.8, 0.2 } +} + +style "audio_track_metrics" = "audio_track_base" +{ + font_name = "%FONT_TINY%" +} + +style "audio_bus_metrics" = "audio_bus_base" +{ + font_name = "%FONT_TINY%" +} + +style "audio_track_metrics_inactive" = "track_controls_inactive" +{ + font_name = "%FONT_TINY%" +} + +style "audio_bus_metrics_inactive" = "track_controls_inactive" +{ + font_name = "%FONT_TINY%" +} + style "track_name_display" { font_name = "%FONT_NORMAL%" @@ -864,11 +871,11 @@ style "treeview_display" = "small_bold_text" style "main_canvas_area" { - bg[NORMAL] = { 0.40, 0.40, 0.44 } - bg[ACTIVE] = { 0.40, 0.40, 0.44 } - bg[INSENSITIVE] = { 0.40, 0.40, 0.44 } - bg[SELECTED] = { 0.40, 0.40, 0.44 } - bg[PRELIGHT] = { 0.40, 0.40, 0.44 } + bg[NORMAL] = { 0.30, 0.30, 0.34 } + bg[ACTIVE] = { 0.30, 0.30, 0.34 } + bg[INSENSITIVE] = { 0.30, 0.30, 0.34 } + bg[SELECTED] = { 0.30, 0.30, 0.34 } + bg[PRELIGHT] = { 0.30, 0.30, 0.34 } } style "track_controls_inactive" @@ -1123,12 +1130,6 @@ style "tearoff_arrow" = "medium_bold_entry" bg[PRELIGHT] = { 0.80, 0.80, 0.80 } } -style "meter_metrics_strip" = "default_base" -{ - font_name = "%FONT_TINY%" - fg[NORMAL] = { 1.0, 0.8, 0.2 } -} - style "location_row_button" = "default_buttons_menus" { font_name = "%FONT_BIG%" @@ -1234,15 +1235,15 @@ widget "*MixerMonitorInputButton.*" style:highest "very_small_button" widget "*MixerIOButton" style:highest "very_small_button" widget "*MixerIOButtonLabel" style:highest "very_small_button" widget "*AddRouteDialogSpinner" style:highest "ardour_adjusters" -widget "*AddRouteDialogRadioButton*" style:highest "options_window" -widget "*OptionsNotebook" style:highest "options_window" -widget "*OptionEditorToggleButton*" style:highest "options_window" -widget "*OptionsLabel" style:highest "options_window" -widget "*OptionEditorAuditionerLabel" style:highest "options_window" +widget "*AddRouteDialogRadioButton*" style:highest "preferences" +widget "*OptionsNotebook" style:highest "preferences" +widget "*OptionEditorToggleButton*" style:highest "preferences" +widget "*OptionsLabel" style:highest "preferences" +widget "*OptionEditorAuditionerLabel" style:highest "preferences" widget "*OptionsEntry" style:highest "option_entry" -widget "*InspectorNotebook" style:highest "options_window" -widget "*NewSessionDialog" style:highest "options_window" -widget "*NewSessionDialogButton*" style:highest "options_window" +widget "*InspectorNotebook" style:highest "preferences" +widget "*NewSessionDialog" style:highest "preferences" +widget "*NewSessionDialogButton*" style:highest "preferences" widget "*MixerSendSwitch*" style:highest "very_small_red_active_and_selected_button" widget "*OptionEditorToggleButton" style:highest "small_red_active_and_selected_button" widget "*NewSessionDialogButton" style:highest "small_red_active_and_selected_button" @@ -1346,15 +1347,24 @@ widget "*BBTRuler" 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 "*AudioTrackStripBase" style:highest "audio_track_base" +widget "*AudioBusStripBase" style:highest "audio_bus_base" widget "*MidiTrackStripBase" style:highest "midi_track_base" -widget "*TimebarLabelBase" style:highest "time_bar_label_base" -widget "*TimebarPadding" style:highest "time_bar_padding" +widget "*AudioTrackStripBaseInactive" style:highest "track_controls_inactive" +widget "*AudioBusStripBaseInactive" style:highest "track_controls_inactive" +widget "*MidiTrackStripBaseInactive" style:highest "track_controls_inactive" +widget "*FaderMetricsStrip" style:highest "audio_track_metrics" +widget "*AudioTrackMetrics" style:highest "audio_track_metrics" +widget "*AudioBusMetrics" style:highest "audio_bus_metrics" +widget "*AudioTrackMetricsInactive" style:highest "audio_track_metrics_inactive" +widget "*AudioBusMetricsInactive" style:highest "audio_bus_metrics_inactive" + widget "*TimeAxisViewControlsBaseUnselected" style:highest "audio_track_base" widget "*AudioTrackControlsBaseUnselected" style:highest "audio_track_base" widget "*MidiTrackControlsBaseUnselected" style:highest "midi_track_base" widget "*AudioTrackFader" style:highest "gain_fader" -widget "*AudioBusStripBase" style:highest "audio_bus_base" + widget "*BusControlsBaseUnselected" style:highest "audio_bus_base" widget "*AudioBusFader" style:highest "gain_fader" widget "*TrackSeparator" style:highest "track_separator" @@ -1386,9 +1396,6 @@ widget "*AutomationTrackName" style:highest "automation_track_name" widget "*AudioTrackControlsBaseInactiveSelected" style:highest "track_controls_inactive" widget "*BusControlsBaseInactiveSelected" style:highest "track_controls_inactive" widget "*AutomationTrackControlsBaseInactiveSelected" style:highest "track_controls_inactive" -widget "*AudioTrackStripBaseInactive" style:highest "track_controls_inactive" -widget "*MidiTrackStripBaseInactive" style:highest "track_controls_inactive" -widget "*AudioBusStripBaseInactive" style:highest "track_controls_inactive" widget "*AudioTrackControlsBaseSelected" style:highest "edit_controls_base_selected" widget "*MidiTrackControlsBaseSelected" style:highest "edit_controls_base_selected" widget "*BusControlsBaseSelected" style:highest "edit_controls_base_selected" @@ -1485,8 +1492,7 @@ widget "*PluginSaveButton" style:highest "small_button" widget "*PluginSaveButton*" style:highest "small_button" widget "*PluginLoadButton" style:highest "small_button" widget "*PluginLoadButton*" style:highest "small_button" -widget "*FaderMetricsStrip" style:highest "meter_metrics_strip" -widget "*MeterMetricsStrip" style:highest "meter_metrics_strip" + widget "*MetricDialogFrame" style:highest "base_frame" widget "*MetricEntry" style:highest "medium_bold_entry" widget "*MetricButton" style:highest "default_buttons_menus" diff --git a/gtk2_ardour/ardour2_ui_default.conf b/gtk2_ardour/ardour2_ui_default.conf index e0d6ea2773..034dd75b0f 100644 --- a/gtk2_ardour/ardour2_ui_default.conf +++ b/gtk2_ardour/ardour2_ui_default.conf @@ -3,21 +3,21 @@ <Canvas> <Option name="waveform" value="000000cc"/> <Option name="clipped waveform" value="ff0000e5"/> - <Option name="region base" value="bfbfc172"/> - <Option name="selected region base" value="b591a8d0"/> - <Option name="audio track base" value="b6c3cb88"/> - <Option name="audio bus base" value="cbc1de78"/> - <Option name="midi track base" value="ffd0d850"/> - <Option name="midi bus base" value="ffceea40"/> + <Option name="region base" value="bfbfc1aa"/> + <Option name="selected region base" value="b591a8ff"/> + <Option name="audio track base" value="c6d3d868"/> + <Option name="audio bus base" value="dbd1ea68"/> + <Option name="midi track base" value="ff8f8f3d"/> + <Option name="midi bus base" value="ff0000ee"/> <Option name="time-stretch-fill" value="e2b5b596"/> <Option name="time-stretch-outline" value="63636396"/> <Option name="automation line" value="44bc59ff"/> <Option name="processor automation line" value="7aa3f9ff"/> - <Option name="control point fill" value="ffffff66"/> - <Option name="control point outline" value="ffffffaa"/> + <Option name="control point fill" value="000000ff"/> + <Option name="control point outline" value="000000ff"/> <Option name="entered control point outline" value="ff0000ee"/> <Option name="entered control point selected" value="ff3535ff"/> - <Option name="entered control point" value="ffffffaa"/> + <Option name="entered control point" value="000000cc"/> <Option name="control point selected" value="00ff00ff"/> <Option name="control point" value="ff0000ff"/> <Option name="automation track fill" value="a0a0ce68"/> @@ -40,7 +40,7 @@ <Option name="location punch" value="7c3a3aff"/> <Option name="verbose canvas cursor" value="f4f214bc"/> <Option name="marker label" value="000000ff"/> - <Option name="marker bar separator" value="aaaaaa77"/> + <Option name="marker bar separator" value="30303088"/> <Option name="tempo bar" value="72727fff"/> <Option name="meterbar" value="666672ff"/> <Option name="markerbar" value="7f7f8cff"/> @@ -60,8 +60,8 @@ <Option name="EnteredMarker" value="dd6363ff"/> <Option name="MeterMarker" value="f2425bff"/> <Option name="TempoMarker" value="f2425bff"/> - <Option name="MeasureLineBeat" value="b5b5b576"/> - <Option name="MeasureLineBar" value="d9d9d99c"/> + <Option name="MeasureLineBeat" value="72727266"/> + <Option name="MeasureLineBar" value="8c8c988c"/> <Option name="GhostTrackBase" value="44007c7f"/> <Option name="GhostTrackWave" value="02fd004c"/> <Option name="GhostTrackWaveClip" value="ff000000"/> @@ -74,8 +74,8 @@ <Option name="RecordingRect" value="e5c6c6ff"/> <Option name="SelectionRect" value="e8f4d377"/> <Option name="Selection" value="636363b2"/> - <Option name="VestigialFrame" value="0000000f"/> - <Option name="TimeAxisFrame" value="0000000f"/> + <Option name="VestigialFrame" value="44007c0f"/> + <Option name="TimeAxisFrame" value="44007c0f"/> <Option name="NameHighlightFill" value="0000ffff"/> <Option name="NameHighlightOutline" value="7c00ff96"/> <Option name="FrameHandle" value="7c00ff96"/> @@ -83,15 +83,6 @@ <Option name="TrimHandle" value="1900ff44"/> <Option name="EditCursor" value="0000ffff"/> <Option name="PlayHead" value="ff0000ff"/> - <Option name="MidiSelectRectOutline" value="5555ffff"/> - <Option name="MidiSelectRectFill" value="8888ff88"/> - <Option name="MidiNoteOutlineMin" value="22ff22b0"/> - <Option name="MidiNoteOutlineMid" value="ffff22b0"/> - <Option name="MidiNoteOutlineMax" value="ff2222b0"/> - <Option name="MidiNoteFillMin" value="33ee338a"/> - <Option name="MidiNoteFillMid" value="eeee338a"/> - <Option name="MidiNoteFillMax" value="ee33338a"/> - <Option name="MidiNoteSelectedOutline" value="5566ffee"/> </Canvas> </Ardour> diff --git a/gtk2_ardour/ardour2_ui_light.rc.in b/gtk2_ardour/ardour2_ui_light.rc.in index 4172b73a5b..b8c3683e91 100644 --- a/gtk2_ardour/ardour2_ui_light.rc.in +++ b/gtk2_ardour/ardour2_ui_light.rc.in @@ -4,17 +4,17 @@ style "very_small_text" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" } style "small_text" { - font_name = "%FONT_NORMAL%" + font_name = "%FONT_SMALL%" } style "small_bold_text" { - font_name = "%FONT_BOLD_NORMAL%" + font_name = "%FONT_BOLD_SMALL%" } style "medium_bold_text" @@ -162,7 +162,7 @@ style "default_buttons_menus" style "very_small_button" = "default_buttons_menus" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" ythickness = 0 xthickness = 0 } @@ -223,21 +223,21 @@ style "track_rec_enable_button_alternate" = "small_button" style "mixer_track_rec_enable_button" = "track_rec_enable_button" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" xthickness = 0 ythickness = 0 } style "mixer_track_rec_enable_button_alternate" = "track_rec_enable_button_alternate" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" xthickness = 0 ythickness = 0 } style "mixer_track_rec_enable_button_active" = "track_rec_enable_button_active" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" xthickness = 0 ythickness = 0 } @@ -275,20 +275,20 @@ style "solo_button_active" = "small_button" style "mixer_solo_button" = "solo_button" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } style "mixer_solo_button_alternate" = "solo_button_alternate" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } style "mixer_solo_button_active" = "solo_button_active" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } @@ -326,21 +326,21 @@ style "mute_button_active" = "small_button" style "mixer_mute_button" = "mute_button" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } style "mixer_mute_button_alternate" = "mute_button_alternate" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } style "mixer_mute_button_active" = "mute_button_active" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } @@ -354,7 +354,7 @@ style "multiline_combo" = "small_button" style "mixer_mute_button" = "mute_button" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } @@ -457,7 +457,7 @@ style "ardour_progressbars" = "default_buttons_menus" bg[PRELIGHT] = { 0.00, 0.36, 0.40 } } -style "options_window" = "default_base" +style "preferences" = "default_base" { font_name = "%FONT_NORMAL%" fg[PRELIGHT] = { 0.80, 0.80, 0.80 } @@ -672,7 +672,7 @@ style "transport_clock_display_delta" = "transport_clock_display" style "tempo_meter_clock_display" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" fg[NORMAL] = { 1.0, 1.0, 1.0 } fg[ACTIVE] = { 1.0, 1.0, 0.0 } fg[SELECTED] = { 1.0, 0, 0 } @@ -737,6 +737,38 @@ style "midi_track_base" = "default_base" bg[SELECTED] = { 0.70, 0.70, 0.80 } } +style "track_controls_inactive" +{ + bg[NORMAL] = { 0.60, 0.60, 0.66 } + bg[ACTIVE] = { 0.60, 0.60, 0.66 } + bg[INSENSITIVE] = { 0.60, 0.60, 0.66 } + bg[SELECTED] = { 0.60, 0.60, 0.66 } + bg[PRELIGHT] = { 0.60, 0.60, 0.66 } + + #font_name = "sans 18" + fg[NORMAL] = { 0.7, 0.8, 0.2 } +} + +style "audio_track_metrics" = "audio_track_base" +{ + font_name = "%FONT_TINY%" +} + +style "audio_bus_metrics" = "audio_bus_base" +{ + font_name = "%FONT_TINY%" +} + +style "audio_track_metrics_inactive" = "track_controls_inactive" +{ + font_name = "%FONT_TINY%" +} + +style "audio_bus_metrics_inactive" = "track_controls_inactive" +{ + font_name = "%FONT_TINY%" +} + style "track_name_display" { font_name = "%FONT_NORMAL%" @@ -1102,15 +1134,9 @@ style "tearoff_arrow" = "medium_bold_entry" bg[PRELIGHT] = { 0.30, 0.30, 0.30 } } -style "meter_metrics_strip" = "default_base" -{ - font_name = "%FONT_TINY%" - fg[NORMAL] = { 1.0, 0.8, 0.2 } -} - style "location_row_button" = "default_buttons_menus" { - font_name = "%FONT_BIG%" + font_name = "%FONT_SMALL%" } style "location_rows_clock" = "default_clock_display" @@ -1213,15 +1239,15 @@ widget "*MixerMonitorInputButton.*" style:highest "very_small_button" widget "*MixerIOButton" style:highest "very_small_button" widget "*MixerIOButtonLabel" style:highest "very_small_button" widget "*AddRouteDialogSpinner" style:highest "ardour_adjusters" -widget "*AddRouteDialogRadioButton*" style:highest "options_window" -widget "*OptionsNotebook" style:highest "options_window" -widget "*OptionEditorToggleButton*" style:highest "options_window" -widget "*OptionsLabel" style:highest "options_window" -widget "*OptionEditorAuditionerLabel" style:highest "options_window" +widget "*AddRouteDialogRadioButton*" style:highest "preferences" +widget "*OptionsNotebook" style:highest "preferences" +widget "*OptionEditorToggleButton*" style:highest "preferences" +widget "*OptionsLabel" style:highest "preferences" +widget "*OptionEditorAuditionerLabel" style:highest "preferences" widget "*OptionsEntry" style:highest "option_entry" -widget "*InspectorNotebook" style:highest "options_window" -widget "*NewSessionDialog" style:highest "options_window" -widget "*NewSessionDialogButton*" style:highest "options_window" +widget "*InspectorNotebook" style:highest "preferences" +widget "*NewSessionDialog" style:highest "preferences" +widget "*NewSessionDialogButton*" style:highest "preferences" widget "*MixerSendSwitch*" style:highest "very_small_red_active_and_selected_button" widget "*OptionEditorToggleButton" style:highest "small_red_active_and_selected_button" widget "*NewSessionDialogButton" style:highest "small_red_active_and_selected_button" @@ -1325,11 +1351,24 @@ widget "*BBTRuler" 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 "*AudioTrackStripBase*" style:highest "audio_track_base" + +widget "*AudioTrackStripBase" style:highest "audio_track_base" +widget "*AudioBusStripBase" style:highest "audio_bus_base" +widget "*MidiTrackStripBase" style:highest "midi_track_base" +widget "*AudioTrackStripBaseInactive" style:highest "track_controls_inactive" +widget "*AudioBusStripBaseInactive" style:highest "track_controls_inactive" +widget "*MidiTrackStripBaseInactive" style:highest "track_controls_inactive" +widget "*FaderMetricsStrip" style:highest "audio_track_metrics" +widget "*AudioTrackMetrics" style:highest "audio_track_metrics" +widget "*AudioBusMetrics" style:highest "audio_bus_metrics" +widget "*AudioTrackMetricsInactive" style:highest "audio_track_metrics_inactive" +widget "*AudioBusMetricsInactive" style:highest "audio_bus_metrics_inactive" + widget "*TimeAxisViewControlsBaseUnselected" style:highest "audio_track_base" widget "*AudioTrackControlsBaseUnselected" style:highest "audio_track_base" +widget "*MidiTrackControlsBaseUnselected" style:highest "midi_track_base" widget "*AudioTrackFader" style:highest "gain_fader" -widget "*AudioBusStripBase" style:highest "audio_bus_base" + widget "*BusControlsBaseUnselected" style:highest "audio_bus_base" widget "*AudioBusFader" style:highest "gain_fader" widget "*TrackSeparator" style:highest "track_separator" @@ -1361,9 +1400,8 @@ widget "*AutomationTrackName" style:highest "automation_track_name" widget "*AudioTrackControlsBaseInactiveSelected" style:highest "track_controls_inactive" widget "*BusControlsBaseInactiveSelected" style:highest "track_controls_inactive" widget "*AutomationTrackControlsBaseInactiveSelected" style:highest "track_controls_inactive" -widget "*AudioTrackStripBaseInactive" style:highest "track_controls_inactive" -widget "*AudioBusStripBaseInactive" style:highest "track_controls_inactive" widget "*AudioTrackControlsBaseSelected" style:highest "edit_controls_base_selected" +widget "*MidiTrackControlsBaseSelected" style:highest "edit_controls_base_selected" widget "*BusControlsBaseSelected" style:highest "edit_controls_base_selected" widget "*AutomationTrackControlsBase" style:highest "automation_track_controls_base" widget "*AutomationTrackControlsBaseSelected" style:highest "edit_controls_base_selected" @@ -1458,8 +1496,7 @@ widget "*PluginSaveButton" style:highest "small_button" widget "*PluginSaveButton*" style:highest "small_button" widget "*PluginLoadButton" style:highest "small_button" widget "*PluginLoadButton*" style:highest "small_button" -widget "*FaderMetricsStrip" style:highest "meter_metrics_strip" -widget "*MeterMetricsStrip" style:highest "meter_metrics_strip" + widget "*MetricDialogFrame" style:highest "base_frame" widget "*MetricEntry" style:highest "medium_bold_entry" widget "*MetricButton" style:highest "default_buttons_menus" diff --git a/gtk2_ardour/ardour_dialog.cc b/gtk2_ardour/ardour_dialog.cc index c5162919d4..795b924075 100644 --- a/gtk2_ardour/ardour_dialog.cc +++ b/gtk2_ardour/ardour_dialog.cc @@ -34,6 +34,15 @@ ArdourDialog::ArdourDialog (string title, bool modal, bool use_seperator) set_type_hint(Gdk::WINDOW_TYPE_HINT_DIALOG); } +ArdourDialog::ArdourDialog (Gtk::Window& parent, string title, bool modal, bool use_seperator) + : Dialog (title, parent, modal, use_seperator) +{ + session = 0; + + set_type_hint(Gdk::WINDOW_TYPE_HINT_DIALOG); + set_position (Gtk::WIN_POS_CENTER_ON_PARENT); +} + ArdourDialog::~ArdourDialog () { } diff --git a/gtk2_ardour/ardour_dialog.h b/gtk2_ardour/ardour_dialog.h index 069768c143..13248e14de 100644 --- a/gtk2_ardour/ardour_dialog.h +++ b/gtk2_ardour/ardour_dialog.h @@ -37,6 +37,7 @@ class ArdourDialog : public Gtk::Dialog { public: ArdourDialog (std::string title, bool modal = false, bool use_separator = false); + ArdourDialog (Gtk::Window& parent, std::string title, bool modal = false, bool use_separator = false); ~ArdourDialog(); bool on_enter_notify_event (GdkEventCrossing*); diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index cac63dc752..962ada5035 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -36,6 +36,7 @@ #include <gtkmm/accelmap.h> #include <pbd/error.h> +#include <pbd/misc.h> #include <pbd/compose.h> #include <pbd/failed_constructor.h> #include <pbd/enumwriter.h> @@ -50,8 +51,7 @@ #include <gtkmm2ext/popup.h> #include <gtkmm2ext/window_title.h> -#include <midi++/port.h> -#include <midi++/mmc.h> +#include <midi++/manager.h> #include <ardour/ardour.h> #include <ardour/profile.h> @@ -157,12 +157,18 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) auditioning_alert_button (_("AUDITION")), solo_alert_button (_("SOLO")), - shown_flag (false) + shown_flag (false), + error_log_button (_("Errors")) + { using namespace Gtk::Menu_Helpers; Gtkmm2ext::init(); + +#ifdef TOP_MENUBAR + _auto_display_errors = false; +#endif about = 0; if (theArdourUI == 0) { @@ -180,6 +186,9 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) session_selector_window = 0; last_key_press_time = 0; connection_editor = 0; + _will_create_new_session_automatically = false; + new_session_dialog = 0; + loading_dialog = 0; add_route_dialog = 0; route_params = 0; option_editor = 0; @@ -190,7 +199,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) session_loaded = false; last_speed_displayed = -1.0f; ab_direction = true; - + sys::path key_bindings_file; find_file_in_search_path (ardour_search_path() + system_config_search_path(), @@ -225,31 +234,80 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) ARDOUR::Session::AskAboutPendingState.connect (mem_fun(*this, &ARDOUR_UI::pending_state_dialog)); - /* have to wait for AudioEngine and Configuration before proceeding */ + /* lets get this party started */ + + try { + ARDOUR::init (ARDOUR_COMMAND_LINE::use_vst, ARDOUR_COMMAND_LINE::try_hw_optimization); + setup_gtk_ardour_enums (); + Config->set_current_owner (ConfigVariableBase::Interface); + setup_profile (); + + } catch (failed_constructor& err) { + error << _("could not initialize Ardour.") << endmsg; + // pass it on up + throw err; + } + + /* we like keyboards */ + + keyboard = new Keyboard; + + starting.connect (mem_fun(*this, &ARDOUR_UI::startup)); + stopping.connect (mem_fun(*this, &ARDOUR_UI::shutdown)); + + platform_specific (); } -void -ARDOUR_UI::set_engine (AudioEngine& e) +int +ARDOUR_UI::create_engine () { - engine = &e; + // this gets called every time by new_session() + + if (engine) { + return 0; + } + + try { + engine = new ARDOUR::AudioEngine (ARDOUR_COMMAND_LINE::jack_client_name); + + } catch (...) { + + return -1; + } engine->Stopped.connect (mem_fun(*this, &ARDOUR_UI::engine_stopped)); engine->Running.connect (mem_fun(*this, &ARDOUR_UI::engine_running)); engine->Halted.connect (mem_fun(*this, &ARDOUR_UI::engine_halted)); engine->SampleRateChanged.connect (mem_fun(*this, &ARDOUR_UI::update_sample_rate)); - ActionManager::init (); - new_session_dialog = new NewSessionDialog(); + post_engine (); - _tooltips.enable(); + return 0; +} - keyboard = new Keyboard; +void +ARDOUR_UI::post_engine () +{ + extern int setup_midi (); + + /* Things to be done once we create the AudioEngine + */ + + MIDI::Manager::instance()->set_api_data (engine->jack()); + setup_midi (); + + check_memory_locking(); + + ActionManager::init (); + _tooltips.enable(); if (setup_windows ()) { throw failed_constructor (); } - if (GTK_ARDOUR::show_key_actions) { + /* this is the first point at which all the keybindings are available */ + + if (ARDOUR_COMMAND_LINE::show_key_actions) { vector<string> names; vector<string> paths; vector<string> keys; @@ -266,9 +324,6 @@ ARDOUR_UI::set_engine (AudioEngine& e) exit (0); } - /* start with timecode, metering enabled - */ - blink_timeout_tag = -1; /* the global configuration object is now valid */ @@ -287,15 +342,26 @@ ARDOUR_UI::set_engine (AudioEngine& e) /* start the time-of-day-clock */ +#ifndef GTKOSX + /* OS X provides an always visible wallclock, so don't be stupid */ update_wall_clock (); Glib::signal_timeout().connect (mem_fun(*this, &ARDOUR_UI::update_wall_clock), 60000); +#endif update_disk_space (); update_cpu_load (); update_sample_rate (engine->frame_rate()); - starting.connect (mem_fun(*this, &ARDOUR_UI::startup)); - stopping.connect (mem_fun(*this, &ARDOUR_UI::shutdown)); + /* now start and maybe save state */ + + if (do_engine_start () == 0) { + if (session && _session_is_new) { + /* we need to retain initial visual + settings for a new session + */ + session->save_state (""); + } + } } ARDOUR_UI::~ARDOUR_UI () @@ -317,6 +383,11 @@ ARDOUR_UI::~ARDOUR_UI () if (add_route_dialog) { delete add_route_dialog; } + + + if (new_session_dialog) { + delete new_session_dialog; + } } gint @@ -436,7 +507,7 @@ ARDOUR_UI::save_ardour_state () if (session) { session->add_instant_xml (enode); - session->add_instant_xml (mnode); + session->add_instant_xml (mnode); } else { Config->add_instant_xml (enode); Config->add_instant_xml (mnode); @@ -479,9 +550,151 @@ ARDOUR_UI::update_autosave () } void +ARDOUR_UI::backend_audio_error (bool we_set_params, Gtk::Window* toplevel) +{ + string title; + if (we_set_params) { + title = _("Ardour could not start JACK"); + } else { + title = _("Ardour could not connect to JACK."); + } + + MessageDialog win (title, + false, + Gtk::MESSAGE_INFO, + Gtk::BUTTONS_NONE); + + if (we_set_params) { + win.set_secondary_text(_("There are several possible reasons:\n\ +\n\ +1) You requested audio parameters that are not supported..\n\ +2) JACK is running as another user.\n\ +\n\ +Please consider the possibilities, and perhaps try different parameters.")); + } else { + win.set_secondary_text(_("There are several possible reasons:\n\ +\n\ +1) JACK is not running.\n\ +2) JACK is running as another user, perhaps root.\n\ +3) There is already another client called \"ardour\".\n\ +\n\ +Please consider the possibilities, and perhaps (re)start JACK.")); + } + + if (toplevel) { + win.set_transient_for (*toplevel); + } + + if (we_set_params) { + win.add_button (Stock::OK, RESPONSE_CLOSE); + } else { + win.add_button (Stock::QUIT, RESPONSE_CLOSE); + } + + win.set_default_response (RESPONSE_CLOSE); + + win.show_all (); + win.set_position (Gtk::WIN_POS_CENTER); + + if (!ARDOUR_COMMAND_LINE::no_splash) { + hide_splash (); + } + + /* we just don't care about the result, but we want to block */ + + win.run (); +} + +void ARDOUR_UI::startup () { - check_memory_locking(); + string name, path; + bool isnew; + + new_session_dialog = new NewSessionDialog(); + + /* If no session name is given: we're not loading a session yet, nor creating a new one */ + + if (ARDOUR_COMMAND_LINE::session_name.length()) { + + /* Load session or start the new session dialog */ + + if (find_session (ARDOUR_COMMAND_LINE::session_name, path, name, isnew)) { + error << string_compose(_("could not load command line session \"%1\""), + ARDOUR_COMMAND_LINE::session_name) << endmsg; + return; + } + + if (!ARDOUR_COMMAND_LINE::new_session) { + + /* Supposed to be loading an existing session, but the session doesn't exist */ + + if (isnew) { + error << string_compose (_("\n\nNo session named \"%1\" exists.\n" + "To create it from the command line, start ardour as \"ardour --new %1"), path) + << endmsg; + return; + } + } + + new_session_dialog->set_session_name (name); + new_session_dialog->set_session_folder (Glib::path_get_basename (path)); + _session_is_new = isnew; + } + + hide_splash (); + + bool have_backend = EngineControl::engine_running(); + bool need_nsd; + bool load_needed = false; + + if (have_backend) { + + /* backend audio is working */ + + if (ARDOUR_COMMAND_LINE::session_name.empty() || ARDOUR_COMMAND_LINE::new_session) { + /* need NSD to get session name and other info */ + need_nsd = true; + } else { + need_nsd = false; + } + + } else { + + XMLNode* audio_setup = Config->extra_xml ("AudioSetup"); + + if (audio_setup) { + new_session_dialog->engine_control.set_state (*audio_setup); + } + + /* no backend audio, must bring up NSD to check configuration */ + + need_nsd = true; + } + + if (need_nsd) { + + if (!get_session_parameters (ARDOUR_COMMAND_LINE::session_name, have_backend, ARDOUR_COMMAND_LINE::new_session)) { + return; + } + + } else { + + if (create_engine ()) { + backend_audio_error (false); + exit (1); + } + + load_needed = true; + } + + if (load_needed) { + if (load_session (ARDOUR_COMMAND_LINE::session_name, name)) { + return; + } + } + + show (); } void @@ -679,6 +892,8 @@ ARDOUR_UI::every_point_one_seconds () gint ARDOUR_UI::every_point_zero_one_seconds () { + // august 2007: actual update frequency: 40Hz, not 100Hz + SuperRapidScreenUpdate(); /* EMIT_SIGNAL */ return TRUE; } @@ -1001,6 +1216,7 @@ ARDOUR_UI::open_session () open_session_selector = new Gtk::FileChooserDialog (_("open session"), FILE_CHOOSER_ACTION_OPEN); open_session_selector->add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); open_session_selector->add_button (Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT); + open_session_selector->set_default_response(Gtk::RESPONSE_ACCEPT); FileFilter session_filter; session_filter.add_pattern ("*.ardour"); @@ -1524,21 +1740,6 @@ ARDOUR_UI::setup_theme () theme_manager->setup_theme(); } -gint -ARDOUR_UI::start_engine () -{ - if (do_engine_start () == 0) { - if (session && _session_is_new) { - /* we need to retain initial visual - settings for a new session - */ - session->save_state (""); - } - } - - return FALSE; -} - void ARDOUR_UI::update_clocks () { @@ -1832,30 +2033,34 @@ ARDOUR_UI::save_template () } bool -ARDOUR_UI::new_session (std::string predetermined_path) +ARDOUR_UI::get_session_parameters (Glib::ustring predetermined_path, bool have_engine, bool should_be_new) { string session_name; string session_path; + string template_name; - if (!check_audioengine()) { - return false; + if (!loading_dialog) { + loading_dialog = new MessageDialog (*new_session_dialog, + "", + false, + Gtk::MESSAGE_INFO, + Gtk::BUTTONS_NONE); } - + int response = Gtk::RESPONSE_NONE; new_session_dialog->set_modal(true); new_session_dialog->set_name (predetermined_path); new_session_dialog->reset_recent(); - new_session_dialog->show(); + new_session_dialog->set_position (WIN_POS_CENTER); new_session_dialog->set_current_page (0); do { - response = new_session_dialog->run (); + new_session_dialog->set_have_engine (have_engine); - if (!check_audioengine()) { - new_session_dialog->hide (); - return false; - } + new_session_dialog->show(); + new_session_dialog->present (); + response = new_session_dialog->run (); _session_is_new = false; @@ -1869,65 +2074,119 @@ ARDOUR_UI::new_session (std::string predetermined_path) } else if (response == Gtk::RESPONSE_NONE) { - /* Clear was pressed */ - new_session_dialog->reset(); + /* Clear was pressed */ + new_session_dialog->reset(); + continue; + } - } else if (response == Gtk::RESPONSE_YES) { + /* first things first ... if we're here to help set up audio parameters + this is where want to do that. + */ - /* YES == OPEN, but there's no enum for that */ + if (!have_engine) { + if (new_session_dialog->engine_control.setup_engine ()) { + new_session_dialog->hide (); + return false; + } + } - session_name = new_session_dialog->session_name(); +#ifdef GTKOSX + /* X11 users will always have fontconfig info around, but new GTK-OSX users + may not and it can take a while to build it. Warn them. + */ + Glib::ustring fontconfig = Glib::build_filename (Glib::get_home_dir(), ".fontconfig"); + + if (!Glib::file_test (fontconfig, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_DIR)) { + MessageDialog msg (*new_session_dialog, + _("Welcome to Ardour.\n\n" + "The program will take a bit longer to start up\n" + "while the system fonts are checked.\n\n" + "This will only be done once, and you will\n" + "not see this message again\n"), + true, + Gtk::MESSAGE_INFO, + Gtk::BUTTONS_OK); + msg.show_all (); + msg.present (); + msg.run (); + } +#endif + loading_dialog->set_message (_("Starting audio engine")); + loading_dialog->show_all (); + flush_pending (); + + if (create_engine ()) { + backend_audio_error (!have_engine, new_session_dialog); + loading_dialog->hide (); + flush_pending (); + /* audio setup page */ + new_session_dialog->set_current_page (2); + /* try again */ + response = Gtk::RESPONSE_NONE; + continue; + } + + have_engine = true; + + /* now handle possible affirmative responses */ + + if (response == Gtk::RESPONSE_YES) { + + /* YES == OPEN from the session selector */ + + session_name = new_session_dialog->session_name(); + if (session_name.empty()) { response = Gtk::RESPONSE_NONE; continue; } if (session_name[0] == '/' || - (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') || - (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) { + (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') || + (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) { load_session (Glib::path_get_dirname (session_name), session_name); } else { session_path = new_session_dialog->session_folder(); load_session (session_path, session_name); } - + } else if (response == Gtk::RESPONSE_OK) { - session_name = new_session_dialog->session_name(); - - if (!new_session_dialog->on_new_session_page ()) { - - /* XXX this is a bit of a hack.. - i really want the new sesion dialog to return RESPONSE_YES - if we're on page 1 (the load page) - Unfortunately i can't see how atm.. - */ + /* OK == OPEN button */ - if (session_name.empty()) { - response = Gtk::RESPONSE_NONE; - continue; - } + session_name = new_session_dialog->session_name(); + + if (session_name.empty()) { + response = Gtk::RESPONSE_NONE; + continue; + } + + switch (new_session_dialog->get_current_page()) { + case 1: /* recent session selector */ + case 2: /* audio engine control */ if (session_name[0] == '/' || - (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') || - (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) { + (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') || + (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) { load_session (Glib::path_get_dirname (session_name), session_name); } else { session_path = new_session_dialog->session_folder(); load_session (session_path, session_name); } + break; - } else { + case 0: /* nominally the "new" session creator, but could be in use for an old session */ - if (session_name.empty()) { - response = Gtk::RESPONSE_NONE; - continue; - } + if (new_session_dialog->get_current_page() == 0 && ARDOUR_COMMAND_LINE::session_name.empty()) { + should_be_new = true; + } + + /* handle what appear to be paths rather than just a name */ if (session_name[0] == '/' || - (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') || - (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) { + (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') || + (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) { session_path = Glib::path_get_dirname (session_name); session_name = Glib::path_get_basename (session_name); @@ -1937,108 +2196,144 @@ ARDOUR_UI::new_session (std::string predetermined_path) session_path = new_session_dialog->session_folder(); } - + //XXX This is needed because session constructor wants a //non-existant path. hopefully this will be fixed at some point. - + session_path = Glib::build_filename (session_path, session_name); + + if (!should_be_new) { - if (g_file_test (session_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) { + load_session (session_path, session_name); + continue; /* leaves while() loop because response != NONE */ + + } else if (Glib::file_test (session_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) { Glib::ustring str = string_compose (_("This session\n%1\nalready exists. Do you want to open it?"), session_path); MessageDialog msg (str, - false, - Gtk::MESSAGE_WARNING, - Gtk::BUTTONS_YES_NO, - true); + false, + Gtk::MESSAGE_WARNING, + Gtk::BUTTONS_YES_NO, + true); msg.set_name (X_("CleanupDialog")); msg.set_wmclass (X_("existing_session"), "Ardour"); msg.set_position (Gtk::WIN_POS_MOUSE); - + switch (msg.run()) { - case RESPONSE_YES: - load_session (session_path, session_name); - goto done; - break; - default: - response = RESPONSE_NONE; - new_session_dialog->reset (); - continue; + case RESPONSE_YES: + new_session_dialog->hide (); + goto_editor_window (); + flush_pending (); + load_session (session_path, session_name); + goto done; + break; + default: + response = RESPONSE_NONE; + new_session_dialog->reset (); + continue; } - } + } - _session_is_new = true; + _session_is_new = true; + + if (new_session_dialog->use_session_template()) { - std::string template_name = new_session_dialog->session_template_name(); + template_name = new_session_dialog->session_template_name(); - if (new_session_dialog->use_session_template()) { + new_session_dialog->hide (); + goto_editor_window (); + flush_pending (); load_session (session_path, session_name, &template_name); - + } else { uint32_t cchns; uint32_t mchns; AutoConnectOption iconnect; AutoConnectOption oconnect; + uint32_t nphysin; + uint32_t nphysout; + + if (Profile->get_sae()) { - if (new_session_dialog->create_control_bus()) { - cchns = (uint32_t) new_session_dialog->control_channel_count(); - } else { cchns = 0; - } + mchns = 2; + iconnect = AutoConnectPhysical; + oconnect = AutoConnectMaster; + nphysin = 0; // use all available + nphysout = 0; // use all available - if (new_session_dialog->create_master_bus()) { - mchns = (uint32_t) new_session_dialog->master_channel_count(); } else { - mchns = 0; - } - if (new_session_dialog->connect_inputs()) { - iconnect = AutoConnectPhysical; - } else { - iconnect = AutoConnectOption (0); + /* get settings from advanced section of NSD */ + + if (new_session_dialog->create_control_bus()) { + cchns = (uint32_t) new_session_dialog->control_channel_count(); + } else { + cchns = 0; + } + + if (new_session_dialog->create_master_bus()) { + mchns = (uint32_t) new_session_dialog->master_channel_count(); + } else { + mchns = 0; + } + + if (new_session_dialog->connect_inputs()) { + iconnect = AutoConnectPhysical; + } else { + iconnect = AutoConnectOption (0); + } + + /// @todo some minor tweaks. + + if (new_session_dialog->connect_outs_to_master()) { + oconnect = AutoConnectMaster; + } else if (new_session_dialog->connect_outs_to_physical()) { + oconnect = AutoConnectPhysical; + } else { + oconnect = AutoConnectOption (0); + } + + nphysin = (uint32_t) new_session_dialog->input_limit_count(); + nphysout = (uint32_t) new_session_dialog->output_limit_count(); } - /// @todo some minor tweaks. - - if (new_session_dialog->connect_outs_to_master()) { - oconnect = AutoConnectMaster; - } else if (new_session_dialog->connect_outs_to_physical()) { - oconnect = AutoConnectPhysical; - } else { - oconnect = AutoConnectOption (0); - } - - uint32_t nphysin = (uint32_t) new_session_dialog->input_limit_count(); - uint32_t nphysout = (uint32_t) new_session_dialog->output_limit_count(); - - if (!build_session (session_path, - session_name, - cchns, - mchns, - iconnect, - oconnect, - nphysin, - nphysout, - engine->frame_rate() * 60 * 5)) { - + new_session_dialog->hide (); + goto_editor_window (); + flush_pending (); + + if (build_session (session_path, + session_name, + cchns, + mchns, + iconnect, + oconnect, + nphysin, + nphysout, + engine->frame_rate() * 60 * 5)) { + response = Gtk::RESPONSE_NONE; new_session_dialog->reset (); continue; } } + break; + + default: + break; } } - + } while (response == Gtk::RESPONSE_NONE); done: show(); - new_session_dialog->get_window()->set_cursor(); + loading_dialog->hide (); new_session_dialog->hide(); return true; } @@ -2050,21 +2345,32 @@ ARDOUR_UI::close_session() return; } - unload_session(); - new_session (); + unload_session (true); + + get_session_parameters ("", true, false); } int ARDOUR_UI::load_session (const string & path, const string & snap_name, string* mix_template) { Session *new_session; + int unload_status; + int retval = -1; + session_loaded = false; - + if (!check_audioengine()) { return -1; } - if(!unload_session ()) return -1; + unload_status = unload_session (); + + if (unload_status < 0) { + goto out; + } else if (unload_status > 0) { + retval = 0; + goto out; + } /* if it already exists, we must have write access */ @@ -2072,17 +2378,23 @@ ARDOUR_UI::load_session (const string & path, const string & snap_name, string* MessageDialog msg (*editor, _("You do not have write access to this session.\n" "This prevents the session from being loaded.")); msg.run (); - return -1; + goto out; + } + + if (loading_dialog) { + loading_dialog->set_markup (_("Please wait while Ardour loads your session")); + flush_pending (); } + disable_screen_updates (); + try { new_session = new Session (*engine, path, snap_name, mix_template); } catch (...) { - error << string_compose(_("Session \"%1 (snapshot %2)\" did not load successfully"), path, snap_name) << endmsg; - return -1; + goto out; } connect_to_session (new_session); @@ -2098,10 +2410,15 @@ ARDOUR_UI::load_session (const string & path, const string & snap_name, string* } editor->edit_cursor_position (true); - return 0; + enable_screen_updates (); + flush_pending (); + retval = 0; + + out: + return retval; } -bool +int ARDOUR_UI::build_session (const string & path, const string & snap_name, uint32_t control_channels, uint32_t master_channels, @@ -2112,14 +2429,21 @@ ARDOUR_UI::build_session (const string & path, const string & snap_name, nframes_t initial_length) { Session *new_session; + int x; if (!check_audioengine()) { - return false; + return -1; } session_loaded = false; - if (!unload_session ()) return false; + x = unload_session (); + + if (x < 0) { + return -1; + } else if (x > 0) { + return 0; + } _session_is_new = true; @@ -2132,13 +2456,13 @@ ARDOUR_UI::build_session (const string & path, const string & snap_name, MessageDialog msg (string_compose(_("Could not create session in \"%1\""), path)); msg.run (); - return false; + return -1; } connect_to_session (new_session); session_loaded = true; - return true; + return 0; } void @@ -2616,8 +2940,10 @@ ARDOUR_UI::cmdline_new_session (string path) path = str; } - new_session (path); - + get_session_parameters (path, false, true); + + _will_create_new_session_automatically = false; /* done it */ + return FALSE; /* don't call it again */ } @@ -2868,6 +3194,12 @@ ARDOUR_UI::setup_profile () if (gdk_screen_width() < 1200) { Profile->set_small_screen (); } + + + if (getenv ("ARDOUR_SAE")) { + Profile->set_sae (); + Profile->set_single_package (); + } } void diff --git a/gtk2_ardour/ardour_ui.h b/gtk2_ardour/ardour_ui.h index 39bd8db4a6..427d40dffe 100644 --- a/gtk2_ardour/ardour_ui.h +++ b/gtk2_ardour/ardour_ui.h @@ -50,6 +50,7 @@ #include <gtkmm/togglebutton.h> #include <gtkmm/treeview.h> #include <gtkmm/menubar.h> +#include <gtkmm/textbuffer.h> #include <gtkmm/adjustment.h> #include <gtkmm2ext/gtk_ui.h> #include <gtkmm2ext/click_box.h> @@ -111,8 +112,8 @@ class ARDOUR_UI : public Gtkmm2ext::UI int load_session (const string & path, const string & snapshot, string* mix_template = 0); bool session_loaded; - /// @return true if building the session was successful - bool build_session (const string & path, const string & snapshot, + /// @return zero if building the session was successful + int build_session (const string & path, const string & snapshot, uint32_t ctl_chns, uint32_t master_chns, ARDOUR::AutoConnectOption input_connect, @@ -124,11 +125,20 @@ class ARDOUR_UI : public Gtkmm2ext::UI ARDOUR::Session* the_session() { return session; } - bool new_session(std::string path = string()); + bool will_create_new_session_automatically() const { + return _will_create_new_session_automatically; + } + + void set_will_create_new_session_automatically (bool yn) { + _will_create_new_session_automatically = yn; + } + + bool get_session_parameters (Glib::ustring path, bool have_engine = false, bool should_be_new = false); + gint cmdline_new_session (string path); /// @return true if session was successfully unloaded. - bool unload_session (); + int unload_session (bool hide_stuff = false); void close_session(); int save_state_canfail (string state_name = ""); @@ -204,8 +214,8 @@ class ARDOUR_UI : public Gtkmm2ext::UI session_add_midi_route (false); }*/ - void set_engine (ARDOUR::AudioEngine&); - gint start_engine (); + int create_engine (); + void post_engine (); gint exit_on_main_window_close (GdkEventAny *); @@ -221,6 +231,8 @@ class ARDOUR_UI : public Gtkmm2ext::UI void setup_profile (); void setup_theme (); + void set_shuttle_fract (double); + protected: friend class PublicEditor; @@ -293,6 +305,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI static ARDOUR_UI *theArdourUI; + void backend_audio_error (bool we_set_params, Gtk::Window* toplevel = 0); void startup (); void shutdown (); @@ -438,7 +451,6 @@ class ARDOUR_UI : public Gtkmm2ext::UI gint shuttle_box_expose (GdkEventExpose*); gint mouse_shuttle (double x, bool force); void use_shuttle_fract (bool force); - void set_shuttle_fract (double); bool shuttle_grabbed; double shuttle_fract; @@ -507,6 +519,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI Gtk::EventBox menu_bar_base; Gtk::HBox menu_hbox; + void use_menubar_as_top_menubar (); void build_menu_bar (); void build_control_surface_menu (); @@ -541,6 +554,8 @@ class ARDOUR_UI : public Gtkmm2ext::UI gint session_menu (GdkEventButton *); + bool _will_create_new_session_automatically; + NewSessionDialog* new_session_dialog; void open_session (); @@ -683,6 +698,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI void set_remote_model (ARDOUR::RemoteModel); void set_denormal_model (ARDOUR::DenormalModel); + void toggle_sync_order_keys (); void toggle_StopPluginsWithTransport(); void toggle_DoNotRunPluginsWhileRecording(); void toggle_VerifyRemoveLastCapture(); @@ -696,16 +712,19 @@ class ARDOUR_UI : public Gtkmm2ext::UI void toggle_RegionEquivalentsOverlap (); void toggle_PrimaryClockDeltaEditCursor (); void toggle_SecondaryClockDeltaEditCursor (); + void toggle_only_copy_imported_files (); void mtc_port_changed (); void map_solo_model (); void map_monitor_model (); void map_denormal_model (); + void map_denormal_protection (); void map_remote_model (); void map_file_header_format (); void map_file_data_format (); void map_input_auto_connect (); void map_output_auto_connect (); + void map_only_copy_imported_files (); void parameter_changed (const char*); void set_meter_hold (ARDOUR::MeterHold); @@ -725,6 +744,16 @@ class ARDOUR_UI : public Gtkmm2ext::UI bool ab_direction; void disable_all_plugins (); void ab_all_plugins (); + + void audioengine_setup (); + + void display_message (const char *prefix, gint prefix_len, + Glib::RefPtr<Gtk::TextBuffer::Tag> ptag, Glib::RefPtr<Gtk::TextBuffer::Tag> mtag, const char *msg); + Gtk::Label status_bar_label; + Gtk::ToggleButton error_log_button; + Gtk::MessageDialog* loading_dialog; + + void platform_specific (); }; #endif /* __ardour_gui_h__ */ diff --git a/gtk2_ardour/ardour_ui2.cc b/gtk2_ardour/ardour_ui2.cc index 489728286c..8f755a3d93 100644 --- a/gtk2_ardour/ardour_ui2.cc +++ b/gtk2_ardour/ardour_ui2.cc @@ -78,14 +78,54 @@ ARDOUR_UI::setup_windows () theme_manager->signal_unmap().connect (bind (sigc::ptr_fun(&ActionManager::uncheck_toggleaction), X_("<Actions>/Common/ToggleThemeManager"))); - top_packer.pack_start (menu_bar_base, false, false); - top_packer.pack_start (transport_frame, false, false); + top_packer.pack_start (transport_frame, false, false); + +#ifdef TOP_MENUBAR + HBox* status_bar_packer = manage (new HBox); + + status_bar_label.set_size_request (300, -1); + status_bar_packer->pack_start (status_bar_label, true, true, 6); + status_bar_packer->pack_start (error_log_button, false, false); + + error_log_button.signal_clicked().connect (mem_fun (*this, &UI::toggle_errors)); + + editor->get_status_bar_packer().pack_start (*status_bar_packer, true, true); + editor->get_status_bar_packer().pack_start (menu_bar_base, false, false, 6); +#else + top_packer.pack_start (menu_bar_base, false, false); +#endif editor->add_toplevel_controls (top_packer); return 0; } + void +ARDOUR_UI::display_message (const char *prefix, gint prefix_len, RefPtr<TextBuffer::Tag> ptag, RefPtr<TextBuffer::Tag> mtag, const char *msg) +{ + ustring text; + + UI::display_message (prefix, prefix_len, ptag, mtag, msg); +#ifdef TOP_MENUBAR + + if (strcmp (prefix, _("[ERROR]: ")) == 0) { + text = "<span color=\"red\" weight=\"bold\">"; + } else if (strcmp (prefix, _("[WARNING]: ")) == 0) { + text = "<span color=\"yellow\" weight=\"bold\">"; + } else if (strcmp (prefix, _("[INFO]: ")) == 0) { + text = "<span color=\"green\" weight=\"bold\">"; + } else { + text = "<span color=\"blue\" weight=\"bold\">???"; + } + + text += prefix; + text += "</span>"; + text += msg; + + status_bar_label.set_markup (text); +#endif +} + void ARDOUR_UI::transport_stopped () { diff --git a/gtk2_ardour/ardour_ui_dependents.cc b/gtk2_ardour/ardour_ui_dependents.cc index 725180e0ab..6464c52967 100644 --- a/gtk2_ardour/ardour_ui_dependents.cc +++ b/gtk2_ardour/ardour_ui_dependents.cc @@ -47,7 +47,8 @@ void ARDOUR_UI::shutdown () { if (session) { - delete session; + /* we're exiting cleanly, so remove any auto-save data */ + session->remove_pending_capture_state (); session = 0; } @@ -109,11 +110,13 @@ void ARDOUR_UI::goto_editor_window () { editor->show_window (); + editor->present (); } void ARDOUR_UI::goto_mixer_window () { mixer->show_window (); + mixer->present (); } gint diff --git a/gtk2_ardour/ardour_ui_dialogs.cc b/gtk2_ardour/ardour_ui_dialogs.cc index c6353df782..9a71443489 100644 --- a/gtk2_ardour/ardour_ui_dialogs.cc +++ b/gtk2_ardour/ardour_ui_dialogs.cc @@ -162,21 +162,26 @@ ARDOUR_UI::connect_to_session (Session *s) point_zero_one_second_connection = Glib::signal_timeout().connect (mem_fun(*this, &ARDOUR_UI::every_point_zero_one_seconds), 40); } -bool -ARDOUR_UI::unload_session () +int +ARDOUR_UI::unload_session (bool hide_stuff) { if (session && session->dirty()) { switch (ask_about_saving_session (_("close"))) { case -1: // cancel - return false; + return 1; case 1: session->save_state (""); break; } } - editor->hide (); + + if (hide_stuff) { + editor->hide (); + mixer->hide (); + } + second_connection.disconnect (); point_one_second_connection.disconnect (); point_oh_five_second_connection.disconnect (); @@ -204,16 +209,12 @@ ARDOUR_UI::unload_session () option_editor->set_session (0); } - if (mixer) { - mixer->hide (); - } - delete session; session = 0; update_buffer_load (); - return true; + return 0; } int diff --git a/gtk2_ardour/ardour_ui_ed.cc b/gtk2_ardour/ardour_ui_ed.cc index a043128700..c850d72048 100644 --- a/gtk2_ardour/ardour_ui_ed.cc +++ b/gtk2_ardour/ardour_ui_ed.cc @@ -34,8 +34,10 @@ #include "ardour_ui.h" #include "public_editor.h" #include "audio_clock.h" +#include "engine_dialog.h" #include "editor.h" #include "actions.h" +#include "sync-menu.h" #include <ardour/session.h> #include <ardour/profile.h> @@ -80,6 +82,8 @@ ARDOUR_UI::install_actions () /* menus + submenus that need action items */ ActionManager::register_action (main_actions, X_("Session"), _("Session")); + ActionManager::register_action (main_actions, X_("Files"), _("Files")); + ActionManager::register_action (main_actions, X_("Regions"), _("Regions")); ActionManager::register_action (main_actions, X_("Cleanup"), _("Cleanup")); ActionManager::register_action (main_actions, X_("Sync"), _("Sync")); ActionManager::register_action (main_actions, X_("Options"), _("Options")); @@ -98,7 +102,7 @@ ARDOUR_UI::install_actions () /* the real actions */ - act = ActionManager::register_action (main_actions, X_("New"), _("New"), hide_return (bind (mem_fun(*this, &ARDOUR_UI::new_session), string ()))); + act = ActionManager::register_action (main_actions, X_("New"), _("New"), hide_return (bind (mem_fun(*this, &ARDOUR_UI::get_session_parameters), string (), true, true))); ActionManager::register_action (main_actions, X_("Open"), _("Open"), mem_fun(*this, &ARDOUR_UI::open_session)); ActionManager::register_action (main_actions, X_("Recent"), _("Recent"), mem_fun(*this, &ARDOUR_UI::open_recent_session)); @@ -191,7 +195,7 @@ ARDOUR_UI::install_actions () ActionManager::register_action (common_actions, X_("goto-editor"), _("Show Editor"), mem_fun(*this, &ARDOUR_UI::goto_editor_window)); ActionManager::register_action (common_actions, X_("goto-mixer"), _("Show Mixer"), mem_fun(*this, &ARDOUR_UI::goto_mixer_window)); - ActionManager::register_toggle_action (common_actions, X_("ToggleOptionsEditor"), _("Options Editor"), mem_fun(*this, &ARDOUR_UI::toggle_options_window)); + ActionManager::register_toggle_action (common_actions, X_("ToggleOptionsEditor"), _("Preferences"), mem_fun(*this, &ARDOUR_UI::toggle_options_window)); act = ActionManager::register_toggle_action (common_actions, X_("ToggleInspector"), _("Track/Bus Inspector"), mem_fun(*this, &ARDOUR_UI::toggle_route_params_window)); ActionManager::session_sensitive_actions.push_back (act); act = ActionManager::register_toggle_action (common_actions, X_("ToggleConnections"), _("Connections"), mem_fun(*this, &ARDOUR_UI::toggle_connection_editor)); @@ -200,7 +204,6 @@ ARDOUR_UI::install_actions () ActionManager::session_sensitive_actions.push_back (act); act = ActionManager::register_toggle_action (common_actions, X_("ToggleBigClock"), _("Big Clock"), mem_fun(*this, &ARDOUR_UI::toggle_big_clock_window)); ActionManager::session_sensitive_actions.push_back (act); - act = ActionManager::register_action (common_actions, X_("About"), _("About"), mem_fun(*this, &ARDOUR_UI::show_splash)); act = ActionManager::register_toggle_action (common_actions, X_("ToggleThemeManager"), _("Theme Manager"), mem_fun(*this, &ARDOUR_UI::toggle_theme_manager)); ActionManager::session_sensitive_actions.push_back (act); act = ActionManager::register_action (common_actions, X_("AddAudioTrack"), _("Add Audio Track"), bind (mem_fun(*this, &ARDOUR_UI::session_add_audio_track), 1, 1, ARDOUR::Normal, 1)); @@ -216,6 +219,8 @@ ARDOUR_UI::install_actions () act = ActionManager::register_action (common_actions, X_("RemoveLastCapture"), _("Remove Last Capture"), mem_fun(*this, &ARDOUR_UI::remove_last_capture)); ActionManager::session_sensitive_actions.push_back (act); + ActionManager::register_action (common_actions, X_("About"), _("About"), mem_fun(*this, &ARDOUR_UI::show_splash)); + Glib::RefPtr<ActionGroup> transport_actions = ActionGroup::create (X_("Transport")); /* do-nothing action for the "transport" menu bar item */ @@ -402,6 +407,7 @@ ARDOUR_UI::install_actions () act->set_sensitive (false); #endif + ActionManager::register_toggle_action (option_actions, X_("SyncEditorAndMixerTrackOrder"), _("Sync Editor and Mixer track order"), mem_fun (*this, &ARDOUR_UI::toggle_sync_order_keys)); ActionManager::register_toggle_action (option_actions, X_("StopPluginsWithTransport"), _("Stop plugins with transport"), mem_fun (*this, &ARDOUR_UI::toggle_StopPluginsWithTransport)); ActionManager::register_toggle_action (option_actions, X_("VerifyRemoveLastCapture"), _("Verify remove last capture"), mem_fun (*this, &ARDOUR_UI::toggle_VerifyRemoveLastCapture)); ActionManager::register_toggle_action (option_actions, X_("PeriodicSafetyBackups"), _("Make periodic safety backups"), mem_fun (*this, &ARDOUR_UI::toggle_PeriodicSafetyBackups)); @@ -412,26 +418,45 @@ ARDOUR_UI::install_actions () ActionManager::register_toggle_action (option_actions, X_("RegionEquivalentsOverlap"), _("Region equivalents overlap"), mem_fun (*this, &ARDOUR_UI::toggle_RegionEquivalentsOverlap)); ActionManager::register_toggle_action (option_actions, X_("PrimaryClockDeltaEditCursor"), _("Primary Clock delta to edit cursor"), mem_fun (*this, &ARDOUR_UI::toggle_PrimaryClockDeltaEditCursor)); ActionManager::register_toggle_action (option_actions, X_("SecondaryClockDeltaEditCursor"), _("Secondary Clock delta to edit cursor"), mem_fun (*this, &ARDOUR_UI::toggle_SecondaryClockDeltaEditCursor)); + ActionManager::register_toggle_action (option_actions, X_("OnlyCopyImportedFiles"), _("Always copy imported files"), mem_fun (*this, &ARDOUR_UI::toggle_only_copy_imported_files)); RadioAction::Group denormal_group; ActionManager::register_toggle_action (option_actions, X_("DenormalProtection"), _("Use DC bias"), mem_fun (*this, &ARDOUR_UI::toggle_denormal_protection)); - - FPU fpu; - ActionManager::register_radio_action (option_actions, denormal_group, X_("DenormalNone"), _("No processor handling"), bind (mem_fun (*this, &ARDOUR_UI::set_denormal_model), DenormalNone)); - act = ActionManager::register_radio_action (option_actions, denormal_group, X_("DenormalFTZ"), _("Use FlushToZero"), bind (mem_fun (*this, &ARDOUR_UI::set_denormal_model), DenormalFTZ)); - if (!fpu.has_flush_to_zero()) { + // as of September 10th 2007, Valgrind cannot handle various FPU flag setting instructions + // so avoid them + + if (getenv ("ARDOUR_RUNNING_UNDER_VALGRIND")) { + + /* we still need these actions to exist, but make them all insensitive */ + + act = ActionManager::register_radio_action (option_actions, denormal_group, X_("DenormalFTZ"), _("Use FlushToZero"), bind (mem_fun (*this, &ARDOUR_UI::set_denormal_model), DenormalFTZ)); act->set_sensitive (false); - } - act = ActionManager::register_radio_action (option_actions, denormal_group, X_("DenormalDAZ"), _("Use DenormalsAreZero"), bind (mem_fun (*this, &ARDOUR_UI::set_denormal_model), DenormalDAZ)); - if (!fpu.has_denormals_are_zero()) { + act = ActionManager::register_radio_action (option_actions, denormal_group, X_("DenormalDAZ"), _("Use DenormalsAreZero"), bind (mem_fun (*this, &ARDOUR_UI::set_denormal_model), DenormalDAZ)); act->set_sensitive (false); - } - act = ActionManager::register_radio_action (option_actions, denormal_group, X_("DenormalFTZDAZ"), _("Use FlushToZero & DenormalsAreZero"), bind (mem_fun (*this, &ARDOUR_UI::set_denormal_model), DenormalFTZDAZ)); - if (!fpu.has_flush_to_zero() || !fpu.has_denormals_are_zero()) { + act = ActionManager::register_radio_action (option_actions, denormal_group, X_("DenormalFTZDAZ"), _("Use FlushToZero & DenormalsAreZero"), bind (mem_fun (*this, &ARDOUR_UI::set_denormal_model), DenormalFTZDAZ)); act->set_sensitive (false); + + } else { + + FPU fpu; + + act = ActionManager::register_radio_action (option_actions, denormal_group, X_("DenormalFTZ"), _("Use FlushToZero"), bind (mem_fun (*this, &ARDOUR_UI::set_denormal_model), DenormalFTZ)); + if (!fpu.has_flush_to_zero()) { + act->set_sensitive (false); + } + + act = ActionManager::register_radio_action (option_actions, denormal_group, X_("DenormalDAZ"), _("Use DenormalsAreZero"), bind (mem_fun (*this, &ARDOUR_UI::set_denormal_model), DenormalDAZ)); + if (!fpu.has_denormals_are_zero()) { + act->set_sensitive (false); + } + + act = ActionManager::register_radio_action (option_actions, denormal_group, X_("DenormalFTZDAZ"), _("Use FlushToZero & DenormalsAreZero"), bind (mem_fun (*this, &ARDOUR_UI::set_denormal_model), DenormalFTZDAZ)); + if (!fpu.has_flush_to_zero() || !fpu.has_denormals_are_zero()) { + act->set_sensitive (false); + } } act = ActionManager::register_toggle_action (option_actions, X_("DoNotRunPluginsWhileRecording"), _("Do not run plugins while recording"), mem_fun (*this, &ARDOUR_UI::toggle_DoNotRunPluginsWhileRecording)); @@ -696,7 +721,9 @@ ARDOUR_UI::build_control_surface_menu () void ARDOUR_UI::build_menu_bar () { - build_control_surface_menu (); + if (!Profile->get_sae()) { + build_control_surface_menu (); + } menu_bar = dynamic_cast<MenuBar*> (ActionManager::get_widget (X_("/Main"))); menu_bar->set_name ("MainMenuBar"); @@ -731,11 +758,20 @@ ARDOUR_UI::build_menu_bar () sample_rate_box.set_name ("SampleRate"); sample_rate_label.set_name ("SampleRate"); - menu_hbox.pack_start (*menu_bar, true, true); - if (!Profile->get_small_screen()) { - menu_hbox.pack_end (wall_clock_box, false, false, 2); - menu_hbox.pack_end (disk_space_box, false, false, 4); +#ifndef TOP_MENUBAR + menu_hbox.pack_start (*menu_bar, true, true); +#else + use_menubar_as_top_menubar (); +#endif + + if (!Profile->get_small_screen()) { +#ifndef GTKOSX + // OSX provides its own wallclock, thank you very much + menu_hbox.pack_end (wall_clock_box, false, false, 2); +#endif + menu_hbox.pack_end (disk_space_box, false, false, 4); } + menu_hbox.pack_end (cpu_load_box, false, false, 4); menu_hbox.pack_end (buffer_load_box, false, false, 4); menu_hbox.pack_end (sample_rate_box, false, false, 4); @@ -745,12 +781,23 @@ ARDOUR_UI::build_menu_bar () } void +ARDOUR_UI::use_menubar_as_top_menubar () +{ +#ifdef GTKOSX + ige_mac_menu_set_menu_bar ((GtkMenuShell*) menu_bar->gobj()); + // ige_mac_menu_set_quit_menu_item (some_item->gobj()); +#endif +} + + +void ARDOUR_UI::setup_clock () { ARDOUR_UI::Clock.connect (bind (mem_fun (big_clock, &AudioClock::set), false)); big_clock_window = new Window (WINDOW_TOPLEVEL); + big_clock_window->set_keep_above (true); big_clock_window->set_border_width (0); big_clock_window->add (big_clock); @@ -761,9 +808,5 @@ ARDOUR_UI::setup_clock () big_clock_window->signal_realize().connect (bind (sigc::ptr_fun (set_decoration), big_clock_window, (Gdk::DECOR_BORDER|Gdk::DECOR_RESIZEH))); big_clock_window->signal_unmap().connect (bind (sigc::ptr_fun(&ActionManager::uncheck_toggleaction), X_("<Actions>/Common/ToggleBigClock"))); - if (editor) { - editor->ensure_float (*big_clock_window); - } - manage_window (*big_clock_window); } diff --git a/gtk2_ardour/ardour_ui_options.cc b/gtk2_ardour/ardour_ui_options.cc index 4a6bef6b5f..d013f98e5b 100644 --- a/gtk2_ardour/ardour_ui_options.cc +++ b/gtk2_ardour/ardour_ui_options.cc @@ -82,6 +82,13 @@ ARDOUR_UI::toggle_denormal_protection () } void +ARDOUR_UI::toggle_only_copy_imported_files () +{ + ActionManager::toggle_config_state ("options", "OnlyCopyImportedFiles", &Configuration::set_only_copy_imported_files, &Configuration::get_only_copy_imported_files); +} + + +void ARDOUR_UI::set_native_file_header_format (HeaderFormat hf) { const char *action = 0; @@ -459,6 +466,12 @@ ARDOUR_UI::toggle_StopRecordingOnXrun() } void +ARDOUR_UI::toggle_sync_order_keys () +{ + ActionManager::toggle_config_state ("options", "SyncEditorAndMixerTrackOrder", &Configuration::set_sync_all_route_ordering, &Configuration::get_sync_all_route_ordering); +} + +void ARDOUR_UI::toggle_StopTransportAtEndOfSession() { ActionManager::toggle_config_state ("options", "StopTransportAtEndOfSession", &Configuration::set_stop_at_session_end, &Configuration::get_stop_at_session_end); @@ -577,6 +590,19 @@ ARDOUR_UI::map_monitor_model () } void +ARDOUR_UI::map_denormal_protection () +{ + Glib::RefPtr<Action> act = ActionManager::get_action ("options", X_("DenormalProtection")); + if (act) { + Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act); + + if (tact && !tact->get_active()) { + tact->set_active (Config->get_denormal_protection()); + } + } +} + +void ARDOUR_UI::map_denormal_model () { const char* on = 0; @@ -762,6 +788,21 @@ ARDOUR_UI::map_output_auto_connect () } void +ARDOUR_UI::map_only_copy_imported_files () +{ + Glib::RefPtr<Action> act = ActionManager::get_action ("options", X_("OnlyCopyImportedFiles")); + if (act) { + Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act); + + if (tact && !tact->get_active()) { + tact->set_active (Config->get_only_copy_imported_files()); + } + } + +} + + +void ARDOUR_UI::map_meter_falloff () { const char* action = X_("MeterFalloffMedium"); @@ -995,12 +1036,16 @@ ARDOUR_UI::parameter_changed (const char* parameter_name) ActionManager::map_some_state ("options", "PeriodicSafetyBackups", &Configuration::get_periodic_safety_backups); } else if (PARAM_IS ("stop-recording-on-xrun")) { ActionManager::map_some_state ("options", "StopRecordingOnXrun", &Configuration::get_stop_recording_on_xrun); + } else if (PARAM_IS ("sync-all-route-ordering")) { + ActionManager::map_some_state ("options", "SyncEditorAndMixerTrackOrder", &Configuration::get_sync_all_route_ordering); } else if (PARAM_IS ("stop-at-session-end")) { ActionManager::map_some_state ("options", "StopTransportAtEndOfSession", &Configuration::get_stop_at_session_end); } else if (PARAM_IS ("monitoring-model")) { map_monitor_model (); } else if (PARAM_IS ("denormal-model")) { map_denormal_model (); + } else if (PARAM_IS ("denormal-protection")) { + map_denormal_protection (); } else if (PARAM_IS ("remote-model")) { map_remote_model (); } else if (PARAM_IS ("use-video-sync")) { @@ -1062,8 +1107,9 @@ ARDOUR_UI::parameter_changed (const char* parameter_name) ActionManager::map_some_state ("options", "PrimaryClockDeltaEditCursor", &Configuration::get_primary_clock_delta_edit_cursor); } else if (PARAM_IS ("secondary-clock-delta-edit-cursor")) { ActionManager::map_some_state ("options", "SecondaryClockDeltaEditCursor", &Configuration::get_secondary_clock_delta_edit_cursor); - } - + } else if (PARAM_IS ("only-copy-imported-files")) { + map_only_copy_imported_files (); + } #undef PARAM_IS } diff --git a/gtk2_ardour/arprof b/gtk2_ardour/arprof index 05a469cb17..47c11cdb99 100755 --- a/gtk2_ardour/arprof +++ b/gtk2_ardour/arprof @@ -6,4 +6,4 @@ if [ gprofhelper.c -nt gprofhelper.so ] ; then fi . ardev_common.sh -LDPRELOAD=./gprofhelper.so $EXECUTABLE $* +LDPRELOAD=./gprofhelper.so $EXECUTABLE "$@" diff --git a/gtk2_ardour/arval b/gtk2_ardour/arval index 920e7cb1a8..5661b4cd11 100755 --- a/gtk2_ardour/arval +++ b/gtk2_ardour/arval @@ -1,4 +1,4 @@ #!/bin/sh . ardev_common.sh export ARDOUR_RUNNING_UNDER_VALGRIND=TRUE -exec valgrind --num-callers=50 --tool=memcheck $EXECUTABLE --novst $* +exec valgrind --num-callers=50 --tool=memcheck $EXECUTABLE --novst "$@" diff --git a/gtk2_ardour/audio_region_view.cc b/gtk2_ardour/audio_region_view.cc index dc6de8d0f6..4bc10e93ec 100644 --- a/gtk2_ardour/audio_region_view.cc +++ b/gtk2_ardour/audio_region_view.cc @@ -28,6 +28,8 @@ #include <ardour/audioregion.h> #include <ardour/audiosource.h> #include <ardour/audio_diskstream.h> +#include <ardour/profile.h> + #include <pbd/memento_command.h> #include <pbd/stacktrace.h> @@ -177,7 +179,9 @@ AudioRegionView::init (Gdk::Color& basic_color, bool wfd) line_name += ':'; line_name += "gain"; - gain_line = new AudioRegionGainLine (line_name, trackview.session(), *this, *group, audio_region()->envelope()); + if (!Profile->get_sae()) { + gain_line = new AudioRegionGainLine (line_name, trackview.session(), *this, *group, audio_region()->envelope()); + } if (!(_flags & EnvelopeVisible)) { gain_line->hide (); @@ -814,6 +818,8 @@ AudioRegionView::create_waves () if (audio_region()->audio_source(n)->peaks_ready (bind (mem_fun(*this, &AudioRegionView::peaks_ready_handler), n), data_ready_connection)) { create_one_wave (n, true); } else { + // we'll get a PeaksReady signal from the source in the future + // and will call create_one_wave(n) then. } } else { create_one_wave (n, true); diff --git a/gtk2_ardour/audio_streamview.cc b/gtk2_ardour/audio_streamview.cc index bbe573dab2..22f8fa6239 100644 --- a/gtk2_ardour/audio_streamview.cc +++ b/gtk2_ardour/audio_streamview.cc @@ -74,6 +74,7 @@ AudioStreamView::AudioStreamView (AudioTimeAxisView& tv) use_rec_regions = tv.editor.show_waveforms_recording (); + } AudioStreamView::~AudioStreamView () diff --git a/gtk2_ardour/axis_view.h b/gtk2_ardour/axis_view.h index 51f744c4e5..e64ef99b16 100644 --- a/gtk2_ardour/axis_view.h +++ b/gtk2_ardour/axis_view.h @@ -62,6 +62,9 @@ class AxisView : public virtual Selectable sigc::signal<void> Hiding; sigc::signal<void> GoingAway; + void set_old_order_key (uint32_t ok) { _old_order_key = ok; } + uint32_t old_order_key() const { return _old_order_key; } + protected: AxisView (ARDOUR::Session& sess); @@ -84,7 +87,8 @@ class AxisView : public virtual Selectable Gtk::Label name_label; bool _marked_for_display; - + uint32_t _old_order_key; + }; /* class AxisView */ #endif /* __ardour_gtk_axis_view_h__ */ diff --git a/gtk2_ardour/editing.h b/gtk2_ardour/editing.h index d7f8f6ece8..93bbb603ee 100644 --- a/gtk2_ardour/editing.h +++ b/gtk2_ardour/editing.h @@ -34,6 +34,8 @@ #define ZOOMFOCUS(a) /*empty*/ #define DISPLAYCONTROL(a) /*empty*/ #define IMPORTMODE(a) /*empty*/ +#define IMPORTPOSITION(a) +#define IMPORTDISPOSITION(a) namespace Editing { @@ -135,6 +137,7 @@ DisplayControl str2displaycontrol (const std::string &); #undef DISPLAYCONTROL #define DISPLAYCONTROL(a) /*empty*/ + // IMPORTMODE #undef IMPORTMODE #define IMPORTMODE(a) a, @@ -142,13 +145,29 @@ enum ImportMode { #include "editing_syms.h" }; -extern const char *importmodestrs[]; -inline const char* enum2str(ImportMode m) {return importmodestrs[m];} -ImportMode str2importmode (const std::string &); - #undef IMPORTMODE #define IMPORTMODE(a) /*empty*/ +// IMPORTPOSITION +#undef IMPORTPOSITION +#define IMPORTPOSITION(a) a, +enum ImportPosition { + #include "editing_syms.h" +}; + +#undef IMPORTPOSITION +#define IMPORTPOSITION(a) /*empty*/ + +// IMPORTDISPOSITION +#undef IMPORTDISPOSITION +#define IMPORTDISPOSITION(a) a, +enum ImportDisposition { + #include "editing_syms.h" +}; + +#undef IMPORTDISPOSITION +#define IMPORTDISPOSITION(a) /*empty*/ + ///////////////////// // These don't need their state saved. yet... enum CutCopyOp { diff --git a/gtk2_ardour/editing_syms.h b/gtk2_ardour/editing_syms.h index a057f6dd64..a520b0d318 100644 --- a/gtk2_ardour/editing_syms.h +++ b/gtk2_ardour/editing_syms.h @@ -83,3 +83,15 @@ IMPORTMODE(ImportAsRegion=0) IMPORTMODE(ImportToTrack=1) IMPORTMODE(ImportAsTrack=2) IMPORTMODE(ImportAsTapeTrack=3) + +// if this is changed, remember to update the string table in sfdb_ui.cc +IMPORTPOSITION(ImportAtTimestamp=0) +IMPORTPOSITION(ImportAtEditCursor=1) +IMPORTPOSITION(ImportAtPlayhead=2) +IMPORTPOSITION(ImportAtStart=3) + +// if this is changed, remember to update the string table in sfdb_ui.cc +IMPORTDISPOSITION(ImportDistinctFiles=0) +IMPORTDISPOSITION(ImportMergeFiles=1) +IMPORTDISPOSITION(ImportSerializeFiles=2) +IMPORTDISPOSITION(ImportDistinctChannels=3) diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 031262c41e..f0a2bfa809 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -25,6 +25,8 @@ #include <string> #include <algorithm> +#include <boost/none.hpp> + #include <sigc++/bind.h> #include <pbd/convert.h> @@ -56,6 +58,7 @@ #include <ardour/session_state_utils.h> #include <ardour/tempo.h> #include <ardour/utils.h> +#include <ardour/profile.h> #include <control_protocol/control_protocol.h> @@ -77,6 +80,7 @@ #include "crossfade_edit.h" #include "canvas_impl.h" #include "actions.h" +#include "sfdb_ui.h" #include "gui_thread.h" #ifdef FFT_ANALYSIS @@ -139,8 +143,8 @@ static const gchar *_zoom_focus_strings[] = { N_("Left"), N_("Right"), N_("Center"), - N_("Play"), - N_("Edit"), + N_("Playhead"), + N_("Edit Cursor"), 0 }; @@ -267,6 +271,7 @@ Editor::Editor () autoscroll_active = false; autoscroll_timeout_tag = -1; interthread_progress_window = 0; + logo_item = 0; #ifdef FFT_ANALYSIS analysis_window = 0; @@ -278,6 +283,7 @@ Editor::Editor () _show_waveforms_recording = true; first_action_message = 0; export_dialog = 0; + export_range_markers_dialog = 0; show_gain_after_trim = false; ignore_route_list_reorder = false; no_route_list_redisplay = false; @@ -302,6 +308,8 @@ Editor::Editor () new_transport_marker_menu = 0; editor_mixer_strip_width = Wide; show_editor_mixer_when_tracks_arrive = false; + region_edit_menu_split_multichannel_item = 0; + region_edit_menu_split_item = 0; temp_location = 0; leftmost_frame = 0; ignore_mouse_mode_toggle = false; @@ -321,6 +329,17 @@ Editor::Editor () _dragging_playhead = false; _dragging_hscrollbar = false; + _scrubbing = false; + mouse_direction = 1; + mouse_speed_update = -1; + mouse_speed_size = 16; + mouse_speed = new double[mouse_speed_size]; + memset (mouse_speed, 0, sizeof(double) * mouse_speed_size); + mouse_speed_entries = 0; + + sfbrowser = 0; + ignore_route_order_sync = false; + location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get(); location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get(); location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get(); @@ -345,7 +364,7 @@ Editor::Editor () edit_controls_vbox.set_spacing (0); horizontal_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::canvas_horizontally_scrolled)); - vertical_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::tie_vertical_scrolling)); + vertical_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::tie_vertical_scrolling), true); track_canvas.set_hadjustment (horizontal_adjustment); track_canvas.set_vadjustment (vertical_adjustment); @@ -688,6 +707,9 @@ Editor::Editor () set_name ("EditorWindow"); add_accel_group (ActionManager::ui_manager->get_accel_group()); + status_bar_hpacker.show (); + + vpacker.pack_end (status_bar_hpacker, false, false); vpacker.pack_end (global_hpacker, true, true); /* register actions now so that set_state() can find them and set toggles/checks etc */ @@ -756,6 +778,7 @@ Editor::Editor () ControlProtocol::ScrollTimeline.connect (mem_fun (*this, &Editor::control_scroll)); Config->ParameterChanged.connect (mem_fun (*this, &Editor::parameter_changed)); + Route::SyncOrderKeys.connect (mem_fun (*this, &Editor::sync_order_keys)); constructed = true; instant_save (); @@ -854,9 +877,27 @@ void Editor::tie_vertical_scrolling () { double y1 = vertical_adjustment.get_value(); + + playhead_cursor->set_y_axis (y1); + edit_cursor->set_y_axis (y1); + if (logo_item) { + logo_item->property_y() = y1; + } + controls_layout.get_vadjustment()->set_value (y1); - playhead_cursor->set_y_axis(y1); - edit_cursor->set_y_axis(y1); + +#ifdef GTKOSX + /* the way idle updates and immediate window flushing work on GTK-Quartz + requires that we force an immediate redraw right here. The controls + layout will do the same all by itself, as does the canvas widget, but + most of the time, the canvas itself hasn't updated itself because its + idle handler hasn't run. consequently, the call that its layout makes + to gdk_window_process_updates() finds nothing to do. here, we force + the update to happen, then request a flush of the new window state. + */ + track_canvas.update_now (); + gdk_window_process_updates (GTK_LAYOUT(track_canvas.gobj())->bin_window, true); +#endif } void @@ -912,40 +953,65 @@ Editor::control_scroll (float fraction) } double step = fraction * current_page_frames(); - nframes_t target; - if ((fraction < 0.0f) && (session->transport_frame() < (nframes_t) fabs(step))) { - target = 0; - } else if ((fraction > 0.0f) && (max_frames - session->transport_frame() < step)) { - target = (max_frames - (current_page_frames()*2)); // allow room for slop in where the PH is on the screen + /* + _control_scroll_target is an optional<T> + + it acts like a pointer to an nframes_t, with + a operator conversion to boolean to check + that it has a value could possibly use + playhead_cursor->current_frame to store the + value and a boolean in the class to know + when it's out of date + */ + + if (!_control_scroll_target) { + _control_scroll_target = session->transport_frame(); + _dragging_playhead = true; + } + + if ((fraction < 0.0f) && (*_control_scroll_target < (nframes_t) fabs(step))) { + *_control_scroll_target = 0; + } else if ((fraction > 0.0f) && (max_frames - *_control_scroll_target < step)) { + *_control_scroll_target = max_frames - (current_page_frames()*2); // allow room for slop in where the PH is on the screen } else { - target = (session->transport_frame() + (nframes_t) floor ((fraction * current_page_frames()))); + *_control_scroll_target += (nframes_t) floor (step); } /* move visuals, we'll catch up with it later */ - playhead_cursor->set_position (target); - - if (target > (current_page_frames() / 2)) { + playhead_cursor->set_position (*_control_scroll_target); + UpdateAllTransportClocks (*_control_scroll_target); + + if (*_control_scroll_target > (current_page_frames() / 2)) { /* try to center PH in window */ - reset_x_origin (target - (current_page_frames()/2)); + reset_x_origin (*_control_scroll_target - (current_page_frames()/2)); } else { reset_x_origin (0); } - /* cancel the existing */ + /* + Now we do a timeout to actually bring the session to the right place + according to the playhead. This is to avoid reading disk buffers on every + call to control_scroll, which is driven by ScrollTimeline and therefore + probably by a control surface wheel which can generate lots of events. + */ + /* cancel the existing timeout */ control_scroll_connection.disconnect (); - /* add the next one */ + /* add the next timeout */ - control_scroll_connection = Glib::signal_timeout().connect (bind (mem_fun (*this, &Editor::deferred_control_scroll), target), 50); + control_scroll_connection = Glib::signal_timeout().connect (bind (mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250); } bool Editor::deferred_control_scroll (nframes_t target) { - session->request_locate (target); + session->request_locate (*_control_scroll_target, session->transport_rolling()); + // reset for next stream + _control_scroll_target = boost::none; + _dragging_playhead = false; return false; } @@ -1153,6 +1219,10 @@ Editor::connect_to_session (Session *t) session->locations()->StateChanged.connect (mem_fun(*this, &Editor::refresh_location_display_s)); session->locations()->end_location()->changed.connect (mem_fun(*this, &Editor::end_location_changed)); + if (sfbrowser) { + sfbrowser->set_session (session); + } + handle_new_duration (); redisplay_regions (); @@ -1271,12 +1341,18 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i } items.push_back (SeparatorElem()); - - items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear))); - items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast))); - items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogB))); - items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogA))); - items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Slow))); + + if (Profile->get_sae()) { + items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear))); + items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast))); + } else { + items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear))); + items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast))); + items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogB))); + items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogA))); + items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Slow))); + } + break; case FadeOutItem: @@ -1289,11 +1365,16 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear))); - items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow))); - items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogA))); - items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogB))); - items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Fast))); + if (Profile->get_sae()) { + items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear))); + items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow))); + } else { + items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear))); + items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow))); + items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogA))); + items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogB))); + items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Fast))); + } break; @@ -1486,6 +1567,7 @@ void Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items) { using namespace Menu_Helpers; + sigc::connection fooc; Menu *region_menu = manage (new Menu); MenuList& items = region_menu->items(); region_menu->set_name ("ArdourContextMenu"); @@ -1515,14 +1597,52 @@ Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items) items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Lock"), bind (mem_fun (*this, &Editor::set_region_lock), true))); - items.push_back (MenuElem (_("Unlock"), bind (mem_fun (*this, &Editor::set_region_lock), false))); - items.push_back (MenuElem (_("Lock Position"), bind (mem_fun (*this, &Editor::set_region_position_lock), true))); - items.push_back (MenuElem (_("Unlock Position"), bind (mem_fun (*this, &Editor::set_region_position_lock), false))); - items.push_back (MenuElem (_("Mute"), bind (mem_fun (*this, &Editor::set_region_mute), true))); - items.push_back (MenuElem (_("Unmute"), bind (mem_fun (*this, &Editor::set_region_mute), false))); - items.push_back (MenuElem (_("Opaque"), bind (mem_fun (*this, &Editor::set_region_opaque), true))); - items.push_back (MenuElem (_("Transparent"), bind (mem_fun (*this, &Editor::set_region_opaque), false))); + items.push_back (CheckMenuElem (_("Lock"))); + region_lock_item = static_cast<CheckMenuItem*>(&items.back()); + fooc = region_lock_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_lock)); + +#if FIXUP_REGION_MENU + if (region->locked()) { + fooc.block (true); + region_lock_item->set_active(); + fooc.block (false); + } +#endif + + items.push_back (CheckMenuElem (_("Lock Position"))); + region_lock_position_item = static_cast<CheckMenuItem*>(&items.back()); + fooc = region_lock_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_position_lock)); +#if FIXUP_REGION_MENU + if (region->locked()) { + fooc.block (true); + region_lock_position_item->set_active(); + fooc.block (false); + } +#endif + + items.push_back (CheckMenuElem (_("Mute"))); + region_mute_item = static_cast<CheckMenuItem*>(&items.back()); + fooc = region_mute_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_mute)); +#if FIXUP_REGION_MENU + if (region->muted()) { + fooc.block (true); + region_mute_item->set_active(); + fooc.block (false); + } +#endif + + if (!Profile->get_sae()) { + items.push_back (CheckMenuElem (_("Opaque"))); + region_opaque_item = static_cast<CheckMenuItem*>(&items.back()); + fooc = region_opaque_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_opaque)); +#if FIXUP_REGION_MENU + if (region->opaque()) { + fooc.block (true); + region_opaque_item->set_active(); + fooc.block (false); + } +#endif + } /* We allow "Original position" if at least one region is not at its natural position @@ -1549,16 +1669,48 @@ Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items) MenuList& envelopes_items = envelopes_menu->items(); envelopes_menu->set_name ("ArdourContextMenu"); - envelopes_items.push_back (MenuElem (_("Reset"), mem_fun(*this, &Editor::reset_region_gain_envelopes))); - envelopes_items.push_back (MenuElem (_("Visible"), bind (mem_fun (*this, &Editor::set_gain_envelope_visibility), true))); - envelopes_items.push_back (MenuElem (_("Invisible"), bind (mem_fun (*this, &Editor::set_gain_envelope_visibility), false))); - envelopes_items.push_back (MenuElem (_("Active"), bind (mem_fun (*this, &Editor::set_gain_envelope_active), true))); - envelopes_items.push_back (MenuElem (_("Inactive"), bind (mem_fun (*this, &Editor::set_gain_envelope_active), false))); +#if FIXUP_REGION_MENU + + XXX NEED TO RESOLVE ONE v. MANY REGION ISSUE + + RegionView* rv = sv->find_view (ar); + AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv); + + if (!Profile->get_sae()) { + envelopes_items.push_back (MenuElem (_("Reset Envelope"), mem_fun(*this, &Editor::reset_region_gain_envelopes))); + + envelopes_items.push_back (CheckMenuElem (_("Envelope Visible"))); + region_envelope_visible_item = static_cast<CheckMenuItem*> (&items.back()); + fooc = region_envelope_visible_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_gain_envelope_visibility)); + if (arv->envelope_visible()) { + fooc.block (true); + region_envelope_visible_item->set_active (true); + fooc.block (false); + } + + envelopes_items.push_back (CheckMenuElem (_("Envelope Active"))); + region_envelope_active_item = static_cast<CheckMenuItem*> (&items.back()); + fooc = region_envelope_active_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_gain_envelope_active)); + + if (ar->envelope_active()) { + fooc.block (true); + region_envelope_active_item->set_active (true); + fooc.block (false); + } + + items.push_back (SeparatorElem()); + } +#endif items.push_back (MenuElem (_("Envelopes"), *envelopes_menu)); - items.push_back (MenuElem (_("Denormalize"), mem_fun (*this, &Editor::denormalize_regions))); - items.push_back (MenuElem (_("Normalize"), mem_fun (*this, &Editor::normalize_regions))); +#if FIXUP_REGION_MENU + if (ar->scale_amplitude() != 1.0f) { + items.push_back (MenuElem (_("DeNormalize"), mem_fun(*this, &Editor::denormalize_regions))); + } else { + items.push_back (MenuElem (_("Normalize"), mem_fun(*this, &Editor::normalize_regions))); + } +#endif } /* Find out if we have a selected MIDI region */ @@ -1631,7 +1783,7 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) selection_menu->set_name ("ArdourContextMenu"); items.push_back (MenuElem (_("Play range"), mem_fun(*this, &Editor::play_selection))); - items.push_back (MenuElem (_("Loop range"), mem_fun(*this, &Editor::set_route_loop_selection))); + items.push_back (MenuElem (_("Loop range"), bind (mem_fun(*this, &Editor::set_loop_from_selection), true))); #ifdef FFT_ANALYSIS items.push_back (SeparatorElem()); @@ -1639,15 +1791,22 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) #endif items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Separate range to track"), mem_fun(*this, &Editor::separate_region_from_selection))); - items.push_back (MenuElem (_("Separate range to region list"), mem_fun(*this, &Editor::new_region_from_selection))); + items.push_back (MenuElem (_("Extend Range to End of Region"), bind (mem_fun(*this, &Editor::extend_selection_to_end_of_region), false))); + items.push_back (MenuElem (_("Extend Range to Start of Region"), bind (mem_fun(*this, &Editor::extend_selection_to_start_of_region), false))); + + items.push_back (SeparatorElem()); + items.push_back (MenuElem (_("Convert to region in-place"), mem_fun(*this, &Editor::separate_region_from_selection))); + items.push_back (MenuElem (_("Convert to region in region list"), mem_fun(*this, &Editor::new_region_from_selection))); items.push_back (SeparatorElem()); items.push_back (MenuElem (_("Select all in range"), mem_fun(*this, &Editor::select_all_selectables_using_time_selection))); + + items.push_back (SeparatorElem()); + items.push_back (MenuElem (_("Set loop from selection"), bind (mem_fun(*this, &Editor::set_loop_from_selection), false))); + items.push_back (MenuElem (_("Set punch from selection"), mem_fun(*this, &Editor::set_punch_from_selection))); + items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Add range markers"), mem_fun (*this, &Editor::add_location_from_selection))); - items.push_back (MenuElem (_("Set range to loop range"), mem_fun(*this, &Editor::set_selection_from_loop))); - items.push_back (MenuElem (_("Set range to punch range"), mem_fun(*this, &Editor::set_selection_from_punch))); + items.push_back (MenuElem (_("Add Range Markers"), mem_fun (*this, &Editor::add_location_from_selection))); items.push_back (SeparatorElem()); items.push_back (MenuElem (_("Crop region to range"), mem_fun(*this, &Editor::crop_region_to_selection))); items.push_back (MenuElem (_("Fill range with region"), mem_fun(*this, &Editor::region_fill_selection))); @@ -1656,9 +1815,6 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) items.push_back (SeparatorElem()); items.push_back (MenuElem (_("Bounce range"), mem_fun(*this, &Editor::bounce_range_selection))); items.push_back (MenuElem (_("Export range"), mem_fun(*this, &Editor::export_selection))); - - edit_items.push_back (MenuElem (_("Range"), *selection_menu)); - edit_items.push_back (SeparatorElem()); } /** Add context menu items relevant to busses or audio tracks. @@ -2685,8 +2841,10 @@ Editor::convert_drop_to_paths (vector<ustring>& paths, vector<ustring> uris = data.get_uris(); + cerr << "there were " << uris.size() << " in that drag data\n"; + if (uris.empty()) { - + /* This is seriously fucked up. Nautilus doesn't say that its URI lists are actually URI lists. So do it by hand. */ @@ -2736,10 +2894,34 @@ Editor::convert_drop_to_paths (vector<ustring>& paths, } for (vector<ustring>::iterator i = uris.begin(); i != uris.end(); ++i) { + if ((*i).substr (0,7) == "file://") { - string p = *i; + + ustring p = *i; PBD::url_decode (p); - paths.push_back (p.substr (7)); + + // scan forward past three slashes + + ustring::size_type slashcnt = 0; + ustring::size_type n = 0; + ustring::iterator x = p.begin(); + + while (slashcnt < 3 && x != p.end()) { + if ((*x) == '/') { + slashcnt++; + } else if (slashcnt == 3) { + break; + } + ++n; + ++x; + } + + if (slashcnt != 3 || x == p.end()) { + error << _("malformed URL passed to drag-n-drop code") << endmsg; + continue; + } + + paths.push_back (p.substr (n - 1)); } } @@ -3838,3 +4020,60 @@ Editor::edit_cursor_position(bool sync) return edit_cursor->current_frame; } + +void +Editor::set_loop_range (nframes_t start, nframes_t end, string cmd) +{ + if (!session) return; + + begin_reversible_command (cmd); + + Location* tll; + + if ((tll = transport_loop_location()) == 0) { + Location* loc = new Location (start, end, _("Loop"), Location::IsAutoLoop); + XMLNode &before = session->locations()->get_state(); + session->locations()->add (loc, true); + session->set_auto_loop_location (loc); + XMLNode &after = session->locations()->get_state(); + session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after)); + } + else { + XMLNode &before = tll->get_state(); + tll->set_hidden (false, this); + tll->set (start, end); + XMLNode &after = tll->get_state(); + session->add_command (new MementoCommand<Location>(*tll, &before, &after)); + } + + commit_reversible_command (); +} + +void +Editor::set_punch_range (nframes_t start, nframes_t end, string cmd) +{ + if (!session) return; + + begin_reversible_command (cmd); + + Location* tpl; + + if ((tpl = transport_punch_location()) == 0) { + Location* loc = new Location (start, end, _("Loop"), Location::IsAutoPunch); + XMLNode &before = session->locations()->get_state(); + session->locations()->add (loc, true); + session->set_auto_loop_location (loc); + XMLNode &after = session->locations()->get_state(); + session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after)); + } + else { + XMLNode &before = tpl->get_state(); + tpl->set_hidden (false, this); + tpl->set (start, end); + XMLNode &after = tpl->get_state(); + session->add_command (new MementoCommand<Location>(*tpl, &before, &after)); + } + + commit_reversible_command (); +} + diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 976ad68188..63cbc042ac 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -26,9 +26,14 @@ #include <string> #include <sys/time.h> +#include <glibmm/ustring.h> + +#include <boost/optional.hpp> + #include <libgnomecanvasmm/canvas.h> #include <libgnomecanvasmm/group.h> #include <libgnomecanvasmm/line.h> +#include <libgnomecanvasmm/pixbuf.h> #include <cmath> @@ -100,6 +105,7 @@ class MixerStrip; class StreamView; class AudioStreamView; class ControlPoint; +class SoundFileOmega; #ifdef FFT_ANALYSIS class AnalysisWindow; #endif @@ -249,6 +255,7 @@ class Editor : public PublicEditor void add_toplevel_controls (Gtk::Container&); + Gtk::HBox& get_status_bar_packer() { return status_bar_hpacker; } void set_zoom_focus (Editing::ZoomFocus); Editing::ZoomFocus get_zoom_focus () const { return zoom_focus; } @@ -298,6 +305,7 @@ class Editor : public PublicEditor void toggle_waveform_visibility (); void toggle_waveforms_while_recording (); void toggle_measure_visibility (); + void toggle_logo_visibility (); /* SMPTE timecode & video sync */ @@ -346,6 +354,8 @@ class Editor : public PublicEditor void reposition_and_zoom (nframes_t, double); nframes_t edit_cursor_position(bool); + bool update_mouse_speed (); + bool decelerate_mouse_speed (); protected: void map_transport_state (); @@ -360,6 +370,9 @@ class Editor : public PublicEditor ARDOUR::Session *session; bool constructed; + // to keep track of the playhead position for control_scroll + boost::optional<nframes_t> _control_scroll_target; + PlaylistSelector* _playlist_selector; void set_frames_per_unit (double); @@ -464,6 +477,9 @@ class Editor : public PublicEditor void set_selected_regionview_from_region_list (boost::shared_ptr<ARDOUR::Region> region, Selection::Operation op = Selection::Set); void collect_new_region_view (RegionView *); + Gtk::MenuItem* region_edit_menu_split_item; + Gtk::MenuItem* region_edit_menu_split_multichannel_item; + void popup_track_context_menu (int, int, nframes_t); Gtk::Menu* build_track_context_menu (nframes_t); void add_bus_or_audio_track_context_items (Gtk::Menu_Helpers::MenuList&); @@ -507,6 +523,7 @@ class Editor : public PublicEditor Gtk::Frame time_button_frame; ArdourCanvas::Group *minsec_group; + ArdourCanvas::Pixbuf *logo_item; ArdourCanvas::Group *bbt_group; ArdourCanvas::Group *smpte_group; ArdourCanvas::Group *frame_group; @@ -671,6 +688,7 @@ class Editor : public PublicEditor Gtk::VBox track_canvas_vbox; Gtk::VBox time_canvas_vbox; Gtk::VBox edit_controls_vbox; + Gtk::HBox edit_controls_hbox; void control_scroll (float); bool deferred_control_scroll (nframes_t); @@ -728,6 +746,7 @@ class Editor : public PublicEditor Gtk::Menu *region_list_menu; Gtk::ScrolledWindow region_list_scroller; + Gtk::Frame region_list_frame; bool region_list_display_key_press (GdkEventKey *); bool region_list_display_key_release (GdkEventKey *); @@ -868,10 +887,10 @@ class Editor : public PublicEditor /* EDITING OPERATIONS */ void reset_point_selection (); - void set_region_mute (bool); - void set_region_lock (bool); - void set_region_position_lock (bool); - void set_region_opaque (bool); + void toggle_region_mute (); + void toggle_region_lock (); + void toggle_region_opaque (); + void toggle_region_position_lock (); void raise_region_to_top (); void lower_region_to_bottom (); void split_region (); @@ -951,15 +970,32 @@ class Editor : public PublicEditor void insert_region_list_drag (boost::shared_ptr<ARDOUR::Region>, int x, int y); void insert_region_list_selection (float times); + /* import & embed */ + void add_external_audio_action (Editing::ImportMode); + void external_audio_dialog (); + bool check_multichannel_status (const std::vector<Glib::ustring>& paths); + + SoundFileOmega* sfbrowser; + + void bring_in_external_audio (Editing::ImportMode mode, nframes64_t& pos); + void do_import (vector<Glib::ustring> paths, Editing::ImportDisposition, Editing::ImportMode mode, ARDOUR::SrcQuality, nframes64_t&); + + void _do_embed (vector<Glib::ustring> paths, Editing::ImportDisposition, Editing::ImportMode mode, nframes64_t&); + void do_embed (vector<Glib::ustring> paths, Editing::ImportDisposition, Editing::ImportMode mode, nframes64_t&); + bool idle_do_embed (vector<Glib::ustring> paths, Editing::ImportDisposition, Editing::ImportMode mode, nframes64_t&); - void bring_in_external_audio (Editing::ImportMode mode, ARDOUR::AudioTrack*, nframes_t& pos, bool prompt); - void do_import (vector<Glib::ustring> paths, bool split, Editing::ImportMode mode, ARDOUR::AudioTrack*, nframes_t&, bool); - void do_embed (vector<Glib::ustring> paths, bool split, Editing::ImportMode mode, ARDOUR::AudioTrack*, nframes_t&, bool); - int import_sndfile (vector<Glib::ustring> paths, Editing::ImportMode mode, ARDOUR::AudioTrack* track, nframes_t& pos); - int embed_sndfile (vector<Glib::ustring> paths, bool split, bool multiple_files, bool& check_sample_rate, Editing::ImportMode mode, - ARDOUR::AudioTrack* track, nframes_t& pos, bool prompt); - int finish_bringing_in_audio (boost::shared_ptr<ARDOUR::AudioRegion> region, uint32_t, uint32_t, ARDOUR::AudioTrack* track, nframes_t& pos, Editing::ImportMode mode); + int import_sndfiles (vector<Glib::ustring> paths, Editing::ImportMode mode, ARDOUR::SrcQuality, nframes64_t& pos, + int target_regions, int target_tracks, boost::shared_ptr<ARDOUR::AudioTrack>&); + int embed_sndfiles (vector<Glib::ustring> paths, bool multiple_files, bool& check_sample_rate, Editing::ImportMode mode, + nframes64_t& pos, int target_regions, int target_tracks, boost::shared_ptr<ARDOUR::AudioTrack>&); + + int add_sources (vector<Glib::ustring> paths, ARDOUR::SourceList& sources, nframes64_t& pos, Editing::ImportMode, + int target_regions, int target_tracks, boost::shared_ptr<ARDOUR::AudioTrack>&, bool add_channel_suffix); + int finish_bringing_in_audio (boost::shared_ptr<ARDOUR::AudioRegion> region, uint32_t, uint32_t, nframes64_t& pos, Editing::ImportMode mode, + boost::shared_ptr<ARDOUR::AudioTrack>& existing_track); + + boost::shared_ptr<ARDOUR::AudioTrack> get_nth_selected_audio_track (int nth) const; /* generic interthread progress window */ @@ -1030,7 +1066,11 @@ class Editor : public PublicEditor void add_location_from_audio_region (); void add_location_from_selection (); - void set_route_loop_selection (); + void set_loop_from_selection (bool play); + void set_punch_from_selection (); + + void set_loop_range (nframes_t start, nframes_t end, std::string cmd); + void set_punch_range (nframes_t start, nframes_t end, std::string cmd); void add_location_from_playhead_cursor (); @@ -1040,6 +1080,18 @@ class Editor : public PublicEditor void start_scrolling (); void stop_scrolling (); + bool _scrubbing; + bool have_full_mouse_speed; + nframes64_t last_scrub_frame; + double last_scrub_time; + int mouse_speed_update; + double mouse_direction; + double compute_mouse_speed (); + void add_mouse_speed (double, double); + double* mouse_speed; + size_t mouse_speed_entries; + size_t mouse_speed_size; + void keyboard_selection_begin (); void keyboard_selection_finish (bool add); bool have_pending_keyboard_selection; @@ -1488,10 +1540,12 @@ class Editor : public PublicEditor add (text); add (visible); add (tv); + add (route); } Gtk::TreeModelColumn<Glib::ustring> text; Gtk::TreeModelColumn<bool> visible; Gtk::TreeModelColumn<TimeAxisView*> tv; + Gtk::TreeModelColumn<boost::shared_ptr<ARDOUR::Route> > route; }; RouteDisplayModelColumns route_display_columns; @@ -1502,11 +1556,16 @@ class Editor : public PublicEditor Gtk::ScrolledWindow route_list_scroller; Gtk::Menu* route_list_menu; + void sync_order_keys (); + bool ignore_route_order_sync; + bool route_list_display_button_press (GdkEventButton*); bool route_list_selection_filter (const Glib::RefPtr<Gtk::TreeModel>& model, const Gtk::TreeModel::Path& path, bool yn); void route_list_change (const Gtk::TreeModel::Path&,const Gtk::TreeModel::iterator&); void route_list_delete (const Gtk::TreeModel::Path&); + void track_list_reorder (const Gtk::TreeModel::Path& path, const Gtk::TreeModel::iterator& iter, int* new_order); + void initial_route_list_display (); void redisplay_route_list(); void route_list_reordered (const Gtk::TreeModel::Path& path, const Gtk::TreeModel::iterator& iter, int* what); @@ -1858,14 +1917,15 @@ class Editor : public PublicEditor bool _new_regionviews_show_envelope; - void set_gain_envelope_visibility (bool); - void set_gain_envelope_active (bool); + void toggle_gain_envelope_visibility (); + void toggle_gain_envelope_active (); void reset_region_gain_envelopes (); Gtk::CheckMenuItem* region_envelope_visible_item; Gtk::CheckMenuItem* region_envelope_active_item; Gtk::CheckMenuItem* region_mute_item; Gtk::CheckMenuItem* region_lock_item; + Gtk::CheckMenuItem* region_lock_position_item; Gtk::CheckMenuItem* region_opaque_item; bool on_key_press_event (GdkEventKey*); @@ -1876,6 +1936,8 @@ class Editor : public PublicEditor Glib::RefPtr<Gtk::Action> redo_action; void history_changed (); + + Gtk::HBox status_bar_hpacker; }; #endif /* __ardour_editor_h__ */ diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index 7fe0de9aae..051af2c898 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -66,7 +66,6 @@ Editor::register_actions () /* add named actions for the editor */ - act = ActionManager::register_toggle_action (editor_actions, "show-editor-mixer", _("Show Editor Mixer"), mem_fun (*this, &Editor::editor_mixer_button_toggled)); ActionManager::session_sensitive_actions.push_back (act); act = ActionManager::register_toggle_action (editor_actions, "show-editor-list", _("Show Editor List"), mem_fun (*this, &Editor::editor_list_button_toggled)); @@ -385,19 +384,17 @@ Editor::register_actions () act = ActionManager::register_action (editor_actions, X_("addExternalAudioToRegionList"), _("Add External Audio"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsRegion)); ActionManager::session_sensitive_actions.push_back (act); - act = ActionManager::register_action (editor_actions, X_("addExternalAudioAsRegion"), _("as Region(s)"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsRegion)); - ActionManager::session_sensitive_actions.push_back (act); - act = ActionManager::register_action (editor_actions, X_("addExternalAudioAsTrack"), _("as Tracks"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsTrack)); - ActionManager::session_sensitive_actions.push_back (act); - act = ActionManager::register_action (editor_actions, X_("addExternalAudioAsTapeTrack"), _("as Tape Tracks"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsTapeTrack)); - ActionManager::session_sensitive_actions.push_back (act); - act = ActionManager::register_action (editor_actions, X_("addExternalAudioToTrack"), _("to Tracks"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)); - ActionManager::session_sensitive_actions.push_back (act); ActionManager::register_toggle_action (editor_actions, X_("ToggleWaveformVisibility"), _("Show Waveforms"), mem_fun (*this, &Editor::toggle_waveform_visibility)); ActionManager::register_toggle_action (editor_actions, X_("ToggleWaveformsWhileRecording"), _("Show Waveforms While Recording"), mem_fun (*this, &Editor::toggle_waveforms_while_recording)); act = ActionManager::register_toggle_action (editor_actions, X_("ToggleMeasureVisibility"), _("Show Measures"), mem_fun (*this, &Editor::toggle_measure_visibility)); + /* if there is a logo in the editor canvas, its always visible at startup */ + + act = ActionManager::register_toggle_action (editor_actions, X_("ToggleLogoVisibility"), _("Show Logo"), mem_fun (*this, &Editor::toggle_logo_visibility)); + Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act); + tact->set_active (true); + RadioAction::Group layer_model_group; ActionManager::register_radio_action (editor_actions, layer_model_group, X_("LayerLaterHigher"), _("Later is Higher"), bind (mem_fun (*this, &Editor::set_layer_model), LaterHigher)); @@ -474,6 +471,23 @@ Editor::toggle_measure_visibility () } void +Editor::toggle_logo_visibility () +{ + Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleLogoVisibility")); + + if (act) { + Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act); + if (logo_item) { + if (tact->get_active()) { + logo_item->show (); + } else { + logo_item->hide (); + } + } + } +} + +void Editor::set_crossfade_model (CrossfadeModel model) { RefPtr<Action> act; diff --git a/gtk2_ardour/editor_audio_import.cc b/gtk2_ardour/editor_audio_import.cc index 263ac0c31f..0dc0d707c0 100644 --- a/gtk2_ardour/editor_audio_import.cc +++ b/gtk2_ardour/editor_audio_import.cc @@ -19,9 +19,12 @@ #include <sys/types.h> #include <sys/stat.h> +#include <sys/time.h> #include <errno.h> #include <unistd.h> +#include <sndfile.h> + #include <pbd/pthread_utils.h> #include <pbd/basename.h> #include <pbd/shortpath.h> @@ -63,166 +66,307 @@ using Glib::ustring; /* Functions supporting the incorporation of external (non-captured) audio material into ardour */ void -Editor::add_external_audio_action (ImportMode mode) +Editor::add_external_audio_action (ImportMode mode_hint) { - nframes_t& pos = edit_cursor->current_frame; - boost::shared_ptr<AudioTrack> track; - - if (!selection->tracks.empty()) { - AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(selection->tracks.front()); - if (atv) { - track = atv->audio_track(); - } + if (session == 0) { + MessageDialog msg (0, _("You can't import or embed an audiofile until you have a session loaded.")); + msg.run (); + return; + } + + if (sfbrowser == 0) { + sfbrowser = new SoundFileOmega (*this, _("Add existing audio"), session, 0, true, mode_hint); + } else { + sfbrowser->set_mode (mode_hint); } - bring_in_external_audio (mode, track.get(), pos, false); + external_audio_dialog (); } void -Editor::bring_in_external_audio (ImportMode mode, AudioTrack* track, nframes_t& pos, bool prompt) +Editor::external_audio_dialog () { + vector<Glib::ustring> paths; + uint32_t track_cnt; + if (session == 0) { MessageDialog msg (0, _("You can't import or embed an audiofile until you have a session loaded.")); msg.run (); return; } + + track_cnt = 0; - SoundFileOmega sfdb (_("Add existing audio to session"), session); - sfdb.set_mode (mode); + for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) { + AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*x); + + if (!atv) { + continue; + } else if (atv->is_audio_track()) { + track_cnt++; + } + } + + if (sfbrowser == 0) { + sfbrowser = new SoundFileOmega (*this, _("Add existing audio"), session, track_cnt, true); + } else { + sfbrowser->reset (track_cnt); + } + + sfbrowser->show_all (); + + again: + int response = sfbrowser->run (); - switch (sfdb.run()) { - case SoundFileOmega::ResponseImport: - do_import (sfdb.get_paths(), sfdb.get_split(), sfdb.get_mode(), track, pos, prompt); + switch (response) { + case RESPONSE_APPLY: + // leave the dialog open break; - - case SoundFileOmega::ResponseEmbed: - do_embed (sfdb.get_paths(), sfdb.get_split(), sfdb.get_mode(), track, pos, prompt); + + case RESPONSE_OK: + sfbrowser->hide (); break; default: + // cancel from the browser - we are done + sfbrowser->hide (); + return; + } + + /* lets do it */ + + paths = sfbrowser->get_paths (); + + ImportPosition pos = sfbrowser->get_position (); + ImportMode mode = sfbrowser->get_mode (); + ImportDisposition chns = sfbrowser->get_channel_disposition (); + nframes64_t where; + + switch (pos) { + case ImportAtEditCursor: + where = edit_cursor->current_frame; + break; + case ImportAtTimestamp: + where = -1; + break; + case ImportAtPlayhead: + where = playhead_cursor->current_frame; break; + case ImportAtStart: + where = session->current_start_frame(); + break; + } + + SrcQuality quality = sfbrowser->get_src_quality(); + + if (sfbrowser->copy_files_btn.get_active()) { + do_import (paths, chns, mode, quality, where); + } else { + do_embed (paths, chns, mode, where); + } + + if (response == RESPONSE_APPLY) { + sfbrowser->clear_selection (); + goto again; } } -void -Editor::do_import (vector<ustring> paths, bool split, ImportMode mode, AudioTrack* track, nframes_t& pos, bool prompt) +boost::shared_ptr<AudioTrack> +Editor::get_nth_selected_audio_track (int nth) const { - /* SFDB sets "multichan" to true to indicate "split channels" - so reverse the setting to match the way libardour - interprets it. - */ + AudioTimeAxisView* atv; + TrackSelection::iterator x; - import_status.multichan = !split; + for (x = selection->tracks.begin(); nth > 0 && x != selection->tracks.end(); ++x) { - if (interthread_progress_window == 0) { - build_interthread_progress_window (); + atv = dynamic_cast<AudioTimeAxisView*>(*x); + + if (!atv) { + continue; + } else if (atv->is_audio_track()) { + --nth; + } } + + if (x == selection->tracks.end()) { + atv = dynamic_cast<AudioTimeAxisView*>(selection->tracks.back()); + } else { + atv = dynamic_cast<AudioTimeAxisView*>(*x); + } + + if (!atv || !atv->is_audio_track()) { + return boost::shared_ptr<AudioTrack>(); + } + + return atv->audio_track(); +} +void +Editor::do_import (vector<ustring> paths, ImportDisposition chns, ImportMode mode, SrcQuality quality, nframes64_t& pos) +{ + boost::shared_ptr<AudioTrack> track; vector<ustring> to_import; + bool ok = false; + int nth = 0; - for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) { - - to_import.clear (); - to_import.push_back (*a); - - import_sndfile (to_import, mode, track, pos); + if (interthread_progress_window == 0) { + build_interthread_progress_window (); } - interthread_progress_window->hide_all (); -} + switch (chns) { + case Editing::ImportDistinctFiles: + for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) { -void -Editor::do_embed (vector<ustring> paths, bool split, ImportMode mode, AudioTrack* track, nframes_t& pos, bool prompt) -{ - bool multiple_files = paths.size() > 1; - bool check_sample_rate = true; - vector<ustring>::iterator a; + to_import.clear (); + to_import.push_back (*a); - for (a = paths.begin(); a != paths.end(); ) { + if (mode == Editing::ImportToTrack) { + track = get_nth_selected_audio_track (nth++); + } + + if (import_sndfiles (to_import, mode, quality, pos, 1, -1, track)) { + goto out; + } - cerr << "Considering embed of " << (*a) << endl; - - Glib::ustring path = *a; - Glib::ustring pair_base; - vector<ustring> to_embed; + } + break; - to_embed.push_back (path); - a = paths.erase (a); + case Editing::ImportDistinctChannels: + for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) { - if (path_is_paired (path, pair_base)) { + to_import.clear (); + to_import.push_back (*a); - ustring::size_type len = pair_base.length(); + if (import_sndfiles (to_import, mode, quality, pos, -1, -1, track)) { + goto out; + } - for (vector<Glib::ustring>::iterator b = paths.begin(); b != paths.end(); ) { + } + break; - if (((*b).substr (0, len) == pair_base) && ((*b).length() == path.length())) { + case Editing::ImportMergeFiles: + /* create 1 region from all paths, add to 1 track, + ignore "track" + */ + if (import_sndfiles (paths, mode, quality, pos, 1, 1, track)) { + goto out; + } + break; - to_embed.push_back (*b); - - /* don't process this one again */ + case Editing::ImportSerializeFiles: + for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) { - b = paths.erase (b); - break; + to_import.clear (); + to_import.push_back (*a); + + /* create 1 region from this path, add to 1 track, + reuse "track" across paths + */ - } else { - ++b; - } + if (import_sndfiles (to_import, mode, quality, pos, 1, 1, track)) { + goto out; } + } + break; + } - if (to_embed.size() > 1) { + ok = true; + + out: + if (ok) { + session->save_state (""); + } - vector<string> choices; + interthread_progress_window->hide_all (); +} - choices.push_back (string_compose (_("Import as a %1 region"), - to_embed.size() > 2 ? _("multichannel") : _("stereo"))); - choices.push_back (_("Import as multiple regions")); - - Choice chooser (string_compose (_("Paired files detected (%1, %2 ...).\nDo you want to:"), - to_embed[0], - to_embed[1]), - choices); - - if (chooser.run () == 0) { - - /* keep them paired */ +bool +Editor::idle_do_embed (vector<ustring> paths, ImportDisposition chns, ImportMode mode, nframes64_t& pos) +{ + _do_embed (paths, chns, mode, pos); + return false; +} - if (embed_sndfile (to_embed, split, multiple_files, check_sample_rate, mode, track, pos, prompt) < -1) { - break; - } +void +Editor::do_embed (vector<ustring> paths, ImportDisposition chns, ImportMode mode, nframes64_t& pos) +{ +#ifdef GTKOSX + Glib::signal_idle().connect (bind (mem_fun (*this, &Editor::idle_do_embed), paths, chns, mode, pos)); +#else + _do_embed (paths, chns, mode, pos); +#endif +} - } else { +void +Editor::_do_embed (vector<ustring> paths, ImportDisposition chns, ImportMode mode, nframes64_t& pos) +{ + boost::shared_ptr<AudioTrack> track; + bool check_sample_rate = true; + bool ok = false; + vector<ustring> to_embed; + bool multi = paths.size() > 1; + int nth = 0; - /* one thing per file */ + switch (chns) { + case Editing::ImportDistinctFiles: + for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) { - vector<ustring> foo; + to_embed.clear (); + to_embed.push_back (*a); - for (vector<ustring>::iterator x = to_embed.begin(); x != to_embed.end(); ++x) { + if (mode == Editing::ImportToTrack) { + track = get_nth_selected_audio_track (nth++); + } - foo.clear (); - foo.push_back (*x); + if (embed_sndfiles (to_embed, multi, check_sample_rate, mode, pos, 1, -1, track) < -1) { + goto out; + } + } + break; + + case Editing::ImportDistinctChannels: + for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) { - if (embed_sndfile (foo, split, multiple_files, check_sample_rate, mode, track, pos, prompt) < -1) { - break; - } - } + to_embed.clear (); + to_embed.push_back (*a); + + if (embed_sndfiles (to_embed, multi, check_sample_rate, mode, pos, -1, -1, track) < -1) { + goto out; } + } + break; - } else { - - if (embed_sndfile (to_embed, split, multiple_files, check_sample_rate, mode, track, pos, prompt) < -1) { - break; + case Editing::ImportMergeFiles: + if (embed_sndfiles (paths, multi, check_sample_rate, mode, pos, 1, 1, track) < -1) { + goto out; + } + break; + + case Editing::ImportSerializeFiles: + for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) { + + to_embed.clear (); + to_embed.push_back (*a); + + if (embed_sndfiles (to_embed, multi, check_sample_rate, mode, pos, 1, 1, track) < -1) { + goto out; } } + break; } + + ok = true; - if (a == paths.end()) { + out: + if (ok) { session->save_state (""); } } int -Editor::import_sndfile (vector<ustring> paths, ImportMode mode, AudioTrack* track, nframes_t& pos) +Editor::import_sndfiles (vector<ustring> paths, ImportMode mode, SrcQuality quality, nframes64_t& pos, + int target_regions, int target_tracks, boost::shared_ptr<AudioTrack>& track) { WindowTitle title = string_compose (_("importing %1"), paths.front()); @@ -238,7 +382,8 @@ Editor::import_sndfile (vector<ustring> paths, ImportMode mode, AudioTrack* trac import_status.cancel = false; import_status.freeze = false; import_status.done = 0.0; - + import_status.quality = quality; + interthread_progress_connection = Glib::signal_timeout().connect (bind (mem_fun(*this, &Editor::import_progress_timeout), (gpointer) 0), 100); @@ -262,28 +407,31 @@ Editor::import_sndfile (vector<ustring> paths, ImportMode mode, AudioTrack* trac interthread_progress_connection.disconnect (); /* import thread finished - see if we should build a new track */ + + boost::shared_ptr<AudioRegion> r; - if (!import_status.new_regions.empty()) { - boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion>(import_status.new_regions.front()); - finish_bringing_in_audio (region, region->n_channels(), region->n_channels(), track, pos, mode); + if (import_status.cancel || import_status.sources.empty()) { + goto out; + } + + if (add_sources (paths, import_status.sources, pos, mode, target_regions, target_tracks, track, false) == 0) { + session->save_state (""); } + out: track_canvas.get_window()->set_cursor (*current_canvas_cursor); return 0; } int -Editor::embed_sndfile (vector<Glib::ustring> paths, bool split, bool multiple_files, bool& check_sample_rate, ImportMode mode, - AudioTrack* track, nframes_t& pos, bool prompt) +Editor::embed_sndfiles (vector<Glib::ustring> paths, bool multifile, + bool& check_sample_rate, ImportMode mode, nframes64_t& pos, int target_regions, int target_tracks, + boost::shared_ptr<AudioTrack>& track) { boost::shared_ptr<AudioFileSource> source; SourceList sources; - boost::shared_ptr<AudioRegion> region; string linked_path; SoundFileInfo finfo; - ustring region_name; - uint32_t input_chan = 0; - uint32_t output_chan = 0; int ret = 0; track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH)); @@ -294,10 +442,8 @@ Editor::embed_sndfile (vector<Glib::ustring> paths, bool split, bool multiple_fi ustring path = *p; /* lets see if we can link it into the session */ - - sys::path tmp = session->session_directory().sound_path(); - - tmp /= Glib::path_get_basename(path); + + sys::path tmp = session->session_directory().sound_path() / Glib::path_get_basename(path); linked_path = tmp.to_string(); if (link (path.c_str(), linked_path.c_str()) == 0) { @@ -323,22 +469,21 @@ Editor::embed_sndfile (vector<Glib::ustring> paths, bool split, bool multiple_fi } } } - } /* note that we temporarily truncated _id at the colon */ string error_msg; - + if (!AudioFileSource::get_soundfile_info (path, finfo, error_msg)) { - error << string_compose(_("Editor: cannot open file \"%1\", (%2)"), selection, error_msg ) << endmsg; + error << string_compose(_("Editor: cannot open file \"%1\", (%2)"), path, error_msg ) << endmsg; goto out; } if (check_sample_rate && (finfo.samplerate != (int) session->frame_rate())) { vector<string> choices; - if (multiple_files) { + if (multifile) { choices.push_back (_("Cancel entire import")); choices.push_back (_("Don't embed it")); choices.push_back (_("Embed all without questions")); @@ -390,14 +535,8 @@ Editor::embed_sndfile (vector<Glib::ustring> paths, bool split, bool multiple_fi } track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH)); - ARDOUR_UI::instance()->flush_pending (); - - /* make the proper number of channels in the region */ - - input_chan += finfo.channels; - for (int n = 0; n < finfo.channels; ++n) - { + for (int n = 0; n < finfo.channels; ++n) { try { /* check if we have this thing embedded already */ @@ -409,7 +548,8 @@ Editor::embed_sndfile (vector<Glib::ustring> paths, bool split, bool multiple_fi (DataType::AUDIO, *session, path, n, (mode == ImportAsTapeTrack ? AudioFileSource::Destructive : - AudioFileSource::Flag (0)))); + AudioFileSource::Flag (0)), + true, true)); } else { source = boost::dynamic_pointer_cast<AudioFileSource> (s); } @@ -430,14 +570,74 @@ Editor::embed_sndfile (vector<Glib::ustring> paths, bool split, bool multiple_fi goto out; } - if (sources[0]->natural_position() != 0) { - pos = sources[0]->natural_position(); - } + ret = add_sources (paths, sources, pos, mode, target_regions, target_tracks, track, true); - region_name = region_name_from_path (paths.front(), (sources.size() > 1)); - - region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (sources, 0, sources[0]->length(), region_name, 0, - Region::Flag (Region::DefaultFlags|Region::WholeFile|Region::External))); + out: + track_canvas.get_window()->set_cursor (*current_canvas_cursor); + return ret; +} + +int +Editor::add_sources (vector<Glib::ustring> paths, SourceList& sources, nframes64_t& pos, ImportMode mode, + int target_regions, int target_tracks, boost::shared_ptr<AudioTrack>& track, bool add_channel_suffix) +{ + vector<boost::shared_ptr<AudioRegion> > regions; + ustring region_name; + uint32_t input_chan = 0; + uint32_t output_chan = 0; + + if (pos == -1) { // "use timestamp" + if (sources[0]->natural_position() != 0) { + pos = sources[0]->natural_position(); + } else { + // XXX is this the best alternative ? + pos = edit_cursor->current_frame; + } + } + + if (target_regions == 1) { + + /* take all the sources we have and package them up as a region */ + + region_name = region_name_from_path (paths.front(), (sources.size() > 1), false); + + regions.push_back (boost::dynamic_pointer_cast<AudioRegion> + (RegionFactory::create (sources, 0, sources[0]->length(), region_name, 0, + Region::Flag (Region::DefaultFlags|Region::WholeFile|Region::External)))); + + } else if (target_regions == -1) { + + /* take each source and create a region for each one */ + + SourceList just_one; + SourceList::iterator x; + uint32_t n; + + for (n = 0, x = sources.begin(); x != sources.end(); ++x, ++n) { + + just_one.clear (); + just_one.push_back (*x); + + boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (*x); + + region_name = region_name_from_path (afs->path(), false, true, sources.size(), n); + + regions.push_back (boost::dynamic_pointer_cast<AudioRegion> + (RegionFactory::create (just_one, 0, (*x)->length(), region_name, 0, + Region::Flag (Region::DefaultFlags|Region::WholeFile|Region::External)))); + + } + } + + if (target_regions == 1) { + input_chan = regions.front()->n_channels(); + } else { + if (target_tracks == 1) { + input_chan = regions.size(); + } else { + input_chan = 1; + } + } if (Config->get_output_auto_connect() & AutoConnectMaster) { output_chan = (session->master_out() ? session->master_out()->n_inputs().n_audio() : input_chan); @@ -445,15 +645,31 @@ Editor::embed_sndfile (vector<Glib::ustring> paths, bool split, bool multiple_fi output_chan = input_chan; } - finish_bringing_in_audio (region, input_chan, output_chan, track, pos, mode); - - out: - track_canvas.get_window()->set_cursor (*current_canvas_cursor); - return ret; -} + int n = 0; + + for (vector<boost::shared_ptr<AudioRegion> >::iterator r = regions.begin(); r != regions.end(); ++r, ++n) { + + finish_bringing_in_audio (*r, input_chan, output_chan, pos, mode, track); + if (target_tracks != 1) { + track.reset (); + } else { + pos += (*r)->length(); + } + } + + /* setup peak file building in another thread */ + + for (SourceList::iterator x = sources.begin(); x != sources.end(); ++x) { + SourceFactory::setup_peakfile (*x, true); + } + + return 0; +} + int -Editor::finish_bringing_in_audio (boost::shared_ptr<AudioRegion> region, uint32_t in_chans, uint32_t out_chans, AudioTrack* track, nframes_t& pos, ImportMode mode) +Editor::finish_bringing_in_audio (boost::shared_ptr<AudioRegion> region, uint32_t in_chans, uint32_t out_chans, nframes64_t& pos, + ImportMode mode, boost::shared_ptr<AudioTrack>& existing_track) { switch (mode) { case ImportAsRegion: @@ -461,38 +677,52 @@ Editor::finish_bringing_in_audio (boost::shared_ptr<AudioRegion> region, uint32_ break; case ImportToTrack: - if (track) { - boost::shared_ptr<Playlist> playlist = track->diskstream()->playlist(); - - boost::shared_ptr<AudioRegion> copy (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region))); - begin_reversible_command (_("insert sndfile")); - XMLNode &before = playlist->get_state(); - playlist->add_region (copy, pos); - session->add_command (new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state())); - commit_reversible_command (); + { + if (!existing_track) { + + existing_track = get_nth_selected_audio_track (0); - pos += region->length(); + if (!existing_track) { + return -1; + } } + + boost::shared_ptr<Playlist> playlist = existing_track->diskstream()->playlist(); + boost::shared_ptr<AudioRegion> copy (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region))); + begin_reversible_command (_("insert sndfile")); + XMLNode &before = playlist->get_state(); + playlist->add_region (copy, pos); + session->add_command (new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state())); + commit_reversible_command (); break; - + } + case ImportAsTrack: { - list<boost::shared_ptr<AudioTrack> > at (session->new_audio_track (in_chans, out_chans, Normal, 1)); - if (!at.empty()) { - boost::shared_ptr<AudioRegion> copy (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region))); - at.front()->diskstream()->playlist()->add_region (copy, pos); - at.front()->set_name(short_path(copy->name(), 32)); + if (!existing_track) { + list<boost::shared_ptr<AudioTrack> > at (session->new_audio_track (in_chans, out_chans, Normal, 1)); + + if (at.empty()) { + return -1; + } + + existing_track = at.front(); + existing_track->set_name (region->name()); } + + boost::shared_ptr<AudioRegion> copy (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region))); + existing_track->diskstream()->playlist()->add_region (copy, pos); break; } + case ImportAsTapeTrack: { list<boost::shared_ptr<AudioTrack> > at (session->new_audio_track (in_chans, out_chans, Destructive)); if (!at.empty()) { boost::shared_ptr<AudioRegion> copy (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region))); + at.front()->set_name (basename_nosuffix (copy->name())); at.front()->diskstream()->playlist()->add_region (copy, pos); - at.front()->set_name(short_path(copy->name(), 32)); } break; } diff --git a/gtk2_ardour/editor_audiotrack.cc b/gtk2_ardour/editor_audiotrack.cc index cdcbb43f76..87fa8a06b8 100644 --- a/gtk2_ardour/editor_audiotrack.cc +++ b/gtk2_ardour/editor_audiotrack.cc @@ -26,11 +26,13 @@ #include "audio_region_view.h" #include "selection.h" +#include "i18n.h" + using namespace ARDOUR; using namespace PBD; void -Editor::set_route_loop_selection () +Editor::set_loop_from_selection (bool play) { if (session == 0 || selection->time.empty()) { return; @@ -38,18 +40,26 @@ Editor::set_route_loop_selection () nframes_t start = selection->time[clicked_selection].start; nframes_t end = selection->time[clicked_selection].end; + + set_loop_range (start, end, _("set loop range from selection")); - Location* loc = transport_loop_location(); - - if (loc) { - - loc->set (start, end); - - // enable looping, reposition and start rolling + if (play) { session->request_play_loop (true); - session->request_locate (loc->start(), true); + session->request_locate (start, true); + } +} + +void +Editor::set_punch_from_selection () +{ + if (session == 0 || selection->time.empty()) { + return; } + nframes_t start = selection->time[clicked_selection].start; + nframes_t end = selection->time[clicked_selection].end; + + set_punch_range (start, end, _("set punch range from selection")); } void diff --git a/gtk2_ardour/editor_canvas.cc b/gtk2_ardour/editor_canvas.cc index c5a8695158..86beec387e 100644 --- a/gtk2_ardour/editor_canvas.cc +++ b/gtk2_ardour/editor_canvas.cc @@ -22,6 +22,7 @@ #include <gtkmm2ext/utils.h> #include <ardour/audioregion.h> +#include <ardour/profile.h> #include "ardour_ui.h" #include "editor.h" @@ -143,11 +144,21 @@ Editor::initialize_canvas () verbose_cursor_visible = false; - cursor_group = new ArdourCanvas::Group (*track_canvas.root(), 0.0, 0.0); + if (Profile->get_sae()) { + Image img (::get_icon (X_("saelogo"))); + logo_item = new ArdourCanvas::Pixbuf (*track_canvas.root(), 0.0, 0.0, img.get_pixbuf()); + // logo_item->property_height_in_pixels() = true; + // logo_item->property_width_in_pixels() = true; + // logo_item->property_height_set() = true; + // logo_item->property_width_set() = true; + logo_item->show (); + } + + /* a group to hold time (measure) lines */ time_line_group = new ArdourCanvas::Group (*track_canvas.root(), 0.0, 0.0); tempo_lines = new TempoLines(track_canvas, time_line_group); - + cursor_group = new ArdourCanvas::Group (*track_canvas.root(), 0.0, 0.0); time_canvas.set_name ("EditorTimeCanvas"); time_canvas.add_events (Gdk::POINTER_MOTION_HINT_MASK); time_canvas.set_flags (CAN_FOCUS); @@ -257,6 +268,10 @@ Editor::initialize_canvas () initial_ruler_update_required = true; track_canvas.signal_size_allocate().connect (mem_fun(*this, &Editor::track_canvas_allocate)); + if (logo_item) { + logo_item->lower_to_bottom (); + } + ColorsChanged.connect (mem_fun (*this, &Editor::color_handler)); color_handler(); @@ -348,6 +363,11 @@ Editor::track_canvas_size_allocated () update_fixed_rulers(); redisplay_tempo (true); + + if (logo_item) { + // logo_item->property_height() = canvas_height; + // logo_item->property_width() = canvas_width; + } Resized (); /* EMIT_SIGNAL */ @@ -455,7 +475,7 @@ Editor::drop_paths (const RefPtr<Gdk::DragContext>& context, vector<ustring> paths; string spath; GdkEvent ev; - nframes_t frame; + nframes64_t frame; if (convert_drop_to_paths (paths, context, x, y, data, info, time)) { goto out; @@ -483,15 +503,18 @@ Editor::drop_paths (const RefPtr<Gdk::DragContext>& context, /* drop onto canvas background: create new tracks */ - nframes_t pos = 0; - do_embed (paths, false, ImportAsTrack, 0, pos, false); + do_embed (paths, Editing::ImportDistinctFiles, ImportAsTrack, frame); } else if ((tv = dynamic_cast<RouteTimeAxisView*>(tvp)) != 0) { /* check that its an audio track, not a bus */ + /* check that its an audio track, not a bus */ + if (tv->get_diskstream()) { - do_embed (paths, false, ImportToTrack, tv->audio_track().get(), frame, true); + /* select the track, then embed */ + selection->set (tv); + do_embed (paths, Editing::ImportDistinctFiles, ImportToTrack, frame); } } @@ -697,6 +720,10 @@ Editor::canvas_horizontally_scrolled () reset_scrolling_region (); } + if (logo_item) { + logo_item->property_x() = horizontal_adjustment.get_value (); + } + update_fixed_rulers (); redisplay_tempo (!_dragging_hscrollbar); diff --git a/gtk2_ardour/editor_keyboard.cc b/gtk2_ardour/editor_keyboard.cc index 4b0088f075..28b86f53fc 100644 --- a/gtk2_ardour/editor_keyboard.cc +++ b/gtk2_ardour/editor_keyboard.cc @@ -43,6 +43,14 @@ Editor::kbd_driver (sigc::slot<void,GdkEvent*> theslot, bool use_track_canvas, b doit = true; } + /* any use of "keyboard mouse buttons" invalidates an existing grab + */ + + if (drag_info.item) { + drag_info.item->ungrab (GDK_CURRENT_TIME); + drag_info.item = 0; + } + if (doit) { if (entered_regionview && can_select) { diff --git a/gtk2_ardour/editor_markers.cc b/gtk2_ardour/editor_markers.cc index 29a212f2f5..724ff2c31a 100644 --- a/gtk2_ardour/editor_markers.cc +++ b/gtk2_ardour/editor_markers.cc @@ -909,56 +909,13 @@ Editor::new_transport_marker_menu_popdown (GdkEventAny *ev) void Editor::new_transport_marker_menu_set_loop () { - if (!session) return; - - begin_reversible_command (_("set loop range")); - - Location* tll; - - if ((tll = transport_loop_location()) == 0) { - Location* loc = new Location (temp_location->start(), temp_location->end(), _("Loop"), Location::IsAutoLoop); - XMLNode &before = session->locations()->get_state(); - session->locations()->add (loc, true); - session->set_auto_loop_location (loc); - XMLNode &after = session->locations()->get_state(); - session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after)); - } - else { - XMLNode &before = tll->get_state(); - tll->set_hidden (false, this); - tll->set (temp_location->start(), temp_location->end()); - XMLNode &after = tll->get_state(); - session->add_command (new MementoCommand<Location>(*tll, &before, &after)); - } - - commit_reversible_command (); + set_loop_range (temp_location->start(), temp_location->end(), _("set loop range")); } void Editor::new_transport_marker_menu_set_punch () { - if (!session) return; - - begin_reversible_command (_("set punch range")); - - Location* tpl; - - if ((tpl = transport_punch_location()) == 0) { - tpl = new Location (temp_location->start(), temp_location->end(), _("Punch"), Location::IsAutoPunch); - XMLNode &before = session->locations()->get_state(); - session->locations()->add (tpl, true); - session->set_auto_punch_location (tpl); - XMLNode &after = session->locations()->get_state(); - session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after)); - } else { - XMLNode &before = tpl->get_state(); - tpl->set_hidden(false, this); - tpl->set(temp_location->start(), temp_location->end()); - XMLNode &after = tpl->get_state(); - session->add_command (new MementoCommand<Location>(*tpl, &before, &after)); - } - - commit_reversible_command (); + set_punch_range (temp_location->start(), temp_location->end(), _("set punch range")); } void diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index e8bc98a73c..da95d50d95 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -442,8 +442,23 @@ Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType it switch (item_type) { case RegionItem: - case RegionViewNameHighlight: - case RegionViewName: + if (mouse_mode != MouseRange) { + commit = set_selected_regionview_from_click (press, op, true); + } else if (event->type == GDK_BUTTON_PRESS) { + commit = set_selected_track_from_click (press, op, false); + } + break; + + case RegionViewNameHighlight: + case RegionViewName: + if (mouse_mode != MouseRange) { + commit = set_selected_regionview_from_click (press, op, true); + } else if (event->type == GDK_BUTTON_PRESS) { + commit = set_selected_track_from_click (press, op, false); + } + break; + + case FadeInHandleItem: case FadeInItem: case FadeOutHandleItem: @@ -754,7 +769,12 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp break; case MouseAudition: - /* handled in release */ + _scrubbing = true; + last_scrub_frame = 0; + last_scrub_time = 0; + have_full_mouse_speed = false; + memset (mouse_speed, 0, sizeof (double) * mouse_speed_size); + /* rest handled in motion & release */ break; default: @@ -906,10 +926,19 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT break; case StreamItem: + popup_track_context_menu (1, event->button.time, where); + break; + case RegionItem: case RegionViewNameHighlight: case RegionViewName: + popup_track_context_menu (1, event->button.time, where); + break; + case SelectionItem: + popup_track_context_menu (1, event->button.time, where); + break; + case AutomationTrackItem: case CrossfadeViewItem: popup_track_context_menu (1, event->button.time, where); @@ -1073,13 +1102,20 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT break; case MouseAudition: - switch (item_type) { - case RegionItem: - audition_selected_region (); - break; - default: - break; - } + _scrubbing = false; + if (last_scrub_frame == 0) { + /* no drag, just a click */ + switch (item_type) { + case RegionItem: + audition_selected_region (); + break; + default: + break; + } + } else { + /* make sure we stop */ + session->request_transport_speed (0.0); + } break; default: @@ -1424,6 +1460,12 @@ Editor::left_automation_track () return false; } +static gboolean +_update_mouse_speed (void *arg) +{ + return static_cast<Editor*>(arg)->update_mouse_speed (); +} + bool Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll) { @@ -1455,6 +1497,66 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x, &drag_info.current_pointer_y); + switch (mouse_mode) { + case MouseAudition: + if (_scrubbing) { + struct timeval tmnow; + + if (last_scrub_frame == 0) { + + /* first motion, just set up the variables */ + + last_scrub_frame = (nframes64_t) drag_info.current_pointer_frame; + gettimeofday (&tmnow, 0); + last_scrub_time = tmnow.tv_sec * 1000000.0 + tmnow.tv_usec; + session->request_locate (last_scrub_frame, true); + + } else { + /* how fast is the mouse moving ? */ + + double speed; + nframes_t distance; + double time; + double dir; + +#if 1 + if (last_scrub_frame < (nframes64_t) drag_info.current_pointer_frame) { + distance = (nframes64_t) drag_info.current_pointer_frame - last_scrub_frame; + dir = 1.0; + } else { + distance = last_scrub_frame - (nframes64_t) drag_info.current_pointer_frame; + dir = -1.0; + } +#else + if (drag_info.grab_x < drag_info.current_pointer_x) { + distance = drag_info.current_pointer_x - drag_info.grab_x; + dir = -1.0; + } else { + distance = drag_info.grab_x - drag_info.current_pointer_x; + dir = 1.0; + } +#endif + + gettimeofday (&tmnow, 0); + time = (tmnow.tv_sec * 1000000.0 + tmnow.tv_usec) - last_scrub_time; + last_scrub_frame = drag_info.current_pointer_frame; + last_scrub_time = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec; + speed = ((double)distance/session->frame_rate()) / (time/1000000.0); // frames/sec + + add_mouse_speed (speed, dir); + + if (mouse_speed_update < 0) { + mouse_speed_update = g_timeout_add (10, _update_mouse_speed, this); + update_mouse_speed (); + } + } + } + + default: + break; + } + + if (!from_autoscroll && drag_info.item) { /* item != 0 is the best test i can think of for dragging. */ @@ -3469,6 +3571,10 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event rtv->reveal_dependent_views (*latest_regionview); selection->add (latest_regionview); } + + /* if the original region was locked, we don't care for the new one */ + + newregion->set_locked (false); } else { @@ -4832,6 +4938,11 @@ Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event) if (drag_info.first_move) { return; } + + if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) { + /* backwards drag of the left edge - not usable */ + return; + } nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position(); float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f; @@ -4909,3 +5020,91 @@ Editor::track_height_step_timeout () } return true; } + + +void +Editor::add_mouse_speed (double speed, double dir) +{ + size_t index; + + mouse_direction = dir; + + index = mouse_speed_entries; + + if (++index >= mouse_speed_size) { + index = 0; + have_full_mouse_speed = true; + } + + mouse_speed[index] = speed; + mouse_speed_entries = index; +} + +double +Editor::compute_mouse_speed () +{ + double total = 0; + + if (!have_full_mouse_speed) { + + /* partial speed buffer, just use whatever we have so far */ + + if (mouse_speed_entries == 0 ) { + return 0.0; + } + + for (size_t n = 0; n < mouse_speed_entries; ++n) { + total += mouse_speed[n]; + } + + return mouse_direction * total/mouse_speed_entries; + } + + /* compute the average (effectively low-pass filtering) mouse speed + across the entire buffer. + */ + + for (size_t n = 0; n < mouse_speed_size; ++n) { + total += mouse_speed[n]; + } + + + return mouse_direction * total/mouse_speed_size; +} + +bool +Editor::update_mouse_speed () +{ + double speed; + + if (!_scrubbing) { + session->request_transport_speed (0.0); + mouse_speed_update = -1; + return false; + } + + speed = compute_mouse_speed (); + + struct timeval tmnow; + + gettimeofday (&tmnow, 0); + double now = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec; + + if (now - last_scrub_time > 250000) { + + // 0.25 seconds since last mouse motion, start to brake + + if (fabs (speed) < 0.1) { + /* don't asymptotically approach zero */ + memset (mouse_speed, 0, sizeof (double) * mouse_speed_size); + speed = 0.0; + } else if (fabs (speed) < 0.25) { + add_mouse_speed (fabs (speed * 0.2), mouse_direction); + } else { + add_mouse_speed (fabs (speed * 0.6), mouse_direction); + } + } + + session->request_transport_speed (speed); + return _scrubbing; +} diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index 8906837031..ca2d7039ad 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -119,7 +119,22 @@ Editor::split_regions_at (nframes_t where, RegionSelection& regions) { begin_reversible_command (_("split")); - snap_to (where); + // if splitting a single region, and snap-to is using + // region boundaries, don't pay attention to them + + if (regions.size() == 1) { + switch (snap_type) { + case SnapToRegionStart: + case SnapToRegionSync: + case SnapToRegionEnd: + break; + default: + snap_to (where); + } + } else { + snap_to (where); + } + for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) { RegionSelection::iterator tmp; @@ -178,16 +193,17 @@ Editor::remove_selected_regions () /* XXX: should be called remove regions if we're removing more than one */ begin_reversible_command (_("remove region")); + while (!selection->regions.empty()) { boost::shared_ptr<Region> region = selection->regions.front()->region (); boost::shared_ptr<Playlist> playlist = region->playlist (); - + XMLNode &before = playlist->get_state(); playlist->remove_region (region); XMLNode &after = playlist->get_state(); session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after)); } - + commit_reversible_command (); } @@ -497,12 +513,13 @@ Editor::build_region_boundary_cache () for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) { if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) { - at_end = true; + if (*p == interesting_points.back()) { + at_end = true; + } /* move to next point type */ continue; } - switch (*p) { case Start: rpos = r->first_frame(); @@ -3423,73 +3440,94 @@ Editor::reset_region_gain_envelopes () session->commit_reversible_command (); } -/** Set whether or not gain envelopes are visible for the selected regions. - * @param yn true to make visible, false to make invisible. - */ void -Editor::set_gain_envelope_visibility (bool yn) +Editor::toggle_gain_envelope_visibility () { for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i); if (arv) { - if (arv->envelope_visible() != yn) { - arv->set_envelope_visible (yn); + bool x = region_envelope_visible_item->get_active(); + if (x != arv->envelope_visible()) { + arv->set_envelope_visible (x); } } } } -/** Set whether or not gain envelopes are active for the selected regions. - * @param yn true to make active, false to make inactive. - */ void -Editor::set_gain_envelope_active (bool yn) +Editor::toggle_gain_envelope_active () { for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i); if (arv) { - if (arv->audio_region()->envelope_active() != yn) { - arv->audio_region()->set_envelope_active (yn); + bool x = region_envelope_active_item->get_active(); + if (x != arv->audio_region()->envelope_active()) { + arv->audio_region()->set_envelope_active (x); } } } } -/** Set the locked state of all selected regions to a particular value. +/** Set the position-locked state of all selected regions to a particular value. * @param yn true to make locked, false to make unlocked. */ void -Editor::set_region_lock (bool yn) +Editor::toggle_region_position_lock () { + bool x = region_lock_position_item->get_active(); + for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - (*i)->region()->set_locked (yn); + AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i); + if (arv) { + if (x != arv->audio_region()->locked()) { + arv->audio_region()->set_position_locked (x); + } + } } } -/** Set the position-locked state of all selected regions to a particular value. - * @param yn true to make locked, false to make unlocked. - */ void -Editor::set_region_position_lock (bool yn) +Editor::toggle_region_lock () { + bool x = region_lock_item->get_active(); + for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - (*i)->region()->set_position_locked (yn); + AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i); + if (arv) { + if (x != arv->audio_region()->locked()) { + arv->audio_region()->set_locked (x); + } + } } } void -Editor::set_region_mute (bool yn) +Editor::toggle_region_mute () { + bool x = region_mute_item->get_active(); + for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - (*i)->region()->set_muted (yn); + AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i); + if (arv) { + if (x != arv->audio_region()->muted()) { + arv->audio_region()->set_muted (x); + } + } } } void -Editor::set_region_opaque (bool yn) +Editor::toggle_region_opaque () { + bool x = region_opaque_item->get_active(); + for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - (*i)->region()->set_opaque (yn); + AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i); + if (arv) { + if (x != arv->audio_region()->opaque()) { + arv->audio_region()->set_opaque (x); + } + } } } diff --git a/gtk2_ardour/editor_region_list.cc b/gtk2_ardour/editor_region_list.cc index a9df928222..f1001cb8e4 100644 --- a/gtk2_ardour/editor_region_list.cc +++ b/gtk2_ardour/editor_region_list.cc @@ -626,8 +626,8 @@ Editor::region_list_display_drag_data_received (const RefPtr<Gdk::DragContext>& vector<ustring> paths; if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) { - nframes_t pos = 0; - do_embed (paths, false, ImportAsRegion, 0, pos, true); + nframes64_t pos = 0; + do_embed (paths, Editing::ImportDistinctFiles, ImportAsRegion, pos); context->drag_finish (true, false, time); } } diff --git a/gtk2_ardour/editor_route_list.cc b/gtk2_ardour/editor_route_list.cc index e11738f65b..5e10a578f5 100644 --- a/gtk2_ardour/editor_route_list.cc +++ b/gtk2_ardour/editor_route_list.cc @@ -53,6 +53,10 @@ Editor::handle_new_route (Session::RouteList& routes) TreeModel::Row parent; TreeModel::Row row; + ignore_route_list_reorder = true; + ignore_route_order_sync = true; + no_route_list_redisplay = true; + for (Session::RouteList::iterator x = routes.begin(); x != routes.end(); ++x) { boost::shared_ptr<Route> route = (*x); @@ -100,6 +104,7 @@ Editor::handle_new_route (Session::RouteList& routes) row[route_display_columns.text] = route->name(); row[route_display_columns.visible] = tv->marked_for_display(); row[route_display_columns.tv] = tv; + row[route_display_columns.route] = route; track_views.push_back (tv); @@ -114,11 +119,18 @@ Editor::handle_new_route (Session::RouteList& routes) ignore_route_list_reorder = false; + tv->set_old_order_key (route_display_model->children().size() - 1); route->gui_changed.connect (mem_fun(*this, &Editor::handle_gui_changes)); tv->GoingAway.connect (bind (mem_fun(*this, &Editor::remove_route), tv)); } + ignore_route_list_reorder = false; + ignore_route_order_sync = false; + no_route_list_redisplay = false; + + redisplay_route_list (); + if (show_editor_mixer_when_tracks_arrive) { show_editor_mixer (true); } @@ -234,6 +246,33 @@ Editor::route_list_reordered (const TreeModel::Path& path,const TreeModel::itera redisplay_route_list (); } + +void +Editor::sync_order_keys () +{ + vector<int> neworder; + TreeModel::Children rows = route_display_model->children(); + TreeModel::Children::iterator ri; + + if (ignore_route_order_sync || !session || (session->state_of_the_state() & Session::Loading) || rows.empty()) { + return; + } + + for (ri = rows.begin(); ri != rows.end(); ++ri) { + neworder.push_back (0); + } + + for (ri = rows.begin(); ri != rows.end(); ++ri) { + TimeAxisView* tv = (*ri)[route_display_columns.tv]; + boost::shared_ptr<Route> route = (*ri)[route_display_columns.route]; + neworder[route->order_key (X_("editor"))] = tv->old_order_key (); + } + + ignore_route_list_reorder = true; + route_display_model->reorder (neworder); + ignore_route_list_reorder = false; +} + void Editor::redisplay_route_list () { @@ -288,6 +327,13 @@ Editor::redisplay_route_list () cursor_group->raise_to_top (); reset_scrolling_region (); + + if (Config->get_sync_all_route_ordering() && !ignore_route_list_reorder) { + ignore_route_order_sync = true; + Route::SyncOrderKeys (); // EMIT SIGNAL + ignore_route_order_sync = false; + } + } void @@ -524,6 +570,13 @@ Editor::initial_route_list_display () } void +Editor::track_list_reorder (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter, int* new_order) +{ + session->set_remote_control_ids(); + redisplay_route_list (); +} + +void Editor::route_list_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter) { session->set_remote_control_ids(); diff --git a/gtk2_ardour/editor_rulers.cc b/gtk2_ardour/editor_rulers.cc index cfc7157510..88ff3a0c1e 100644 --- a/gtk2_ardour/editor_rulers.cc +++ b/gtk2_ardour/editor_rulers.cc @@ -152,14 +152,14 @@ Editor::ruler_button_press (GdkEventButton* ev) switch (ev->button) { case 1: - // Since we are about to move the playhead, cancel any running + // Since we will locate the playhead on button release, cancel any running // auditions. if (session->is_auditioning()) { session->cancel_audition (); } - /* transport playhead */ + /* playhead cursor */ snap_to (where); - session->request_locate (where); + playhead_cursor->set_position (where); _dragging_playhead = true; break; diff --git a/gtk2_ardour/engine_dialog.cc b/gtk2_ardour/engine_dialog.cc new file mode 100644 index 0000000000..5be28e9226 --- /dev/null +++ b/gtk2_ardour/engine_dialog.cc @@ -0,0 +1,1126 @@ +#include <vector> +#include <cmath> +#include <fstream> + +#include <glibmm.h> +#include <pbd/xml++.h> + +#ifdef __APPLE__ +#include <CoreAudio/CoreAudio.h> +#include <CoreFoundation/CFString.h> +#include <sys/param.h> +#include <mach-o/dyld.h> +#else +#include <alsa/asoundlib.h> +#endif + +#include <ardour/profile.h> +#include <jack/jack.h> + +#include <gtkmm/stock.h> +#include <gtkmm2ext/utils.h> + +#include <pbd/convert.h> +#include <pbd/error.h> + +#ifdef __APPLE +#include <CFBundle.h> +#endif + +#include "engine_dialog.h" +#include "i18n.h" + +using namespace std; +using namespace Gtk; +using namespace Gtkmm2ext; +using namespace PBD; +using namespace Glib; + +EngineControl::EngineControl () + : periods_adjustment (2, 2, 16, 1, 2), + periods_spinner (periods_adjustment), + priority_adjustment (60, 10, 90, 1, 10), + priority_spinner (priority_adjustment), + ports_adjustment (128, 8, 1024, 1, 16), + ports_spinner (ports_adjustment), + realtime_button (_("Realtime")), + no_memory_lock_button (_("Do not lock memory")), + unlock_memory_button (_("Unlock memory")), + soft_mode_button (_("No zombies")), + monitor_button (_("Provide monitor ports")), + force16bit_button (_("Force 16 bit")), + hw_monitor_button (_("H/W monitoring")), + hw_meter_button (_("H/W metering")), + verbose_output_button (_("Verbose output")), + start_button (_("Start")), + stop_button (_("Stop")), +#ifdef __APPLE__ + basic_packer (5, 2), + options_packer (4, 2), + device_packer (4, 2) +#else + basic_packer (8, 2), + options_packer (14, 2), + device_packer (6, 2) +#endif +{ + using namespace Notebook_Helpers; + Label* label; + vector<string> strings; + int row = 0; + + _used = false; + + strings.push_back (_("8000Hz")); + strings.push_back (_("22050Hz")); + strings.push_back (_("44100Hz")); + strings.push_back (_("48000Hz")); + strings.push_back (_("88200Hz")); + strings.push_back (_("96000Hz")); + strings.push_back (_("192000Hz")); + set_popdown_strings (sample_rate_combo, strings); + sample_rate_combo.set_active_text ("48000Hz"); + + strings.clear (); + strings.push_back ("32"); + strings.push_back ("64"); + strings.push_back ("128"); + strings.push_back ("256"); + strings.push_back ("512"); + strings.push_back ("1024"); + strings.push_back ("2048"); + strings.push_back ("4096"); + strings.push_back ("8192"); + set_popdown_strings (period_size_combo, strings); + period_size_combo.set_active_text ("1024"); + + strings.clear (); + strings.push_back (_("None")); + strings.push_back (_("Triangular")); + strings.push_back (_("Rectangular")); + strings.push_back (_("Shaped")); + set_popdown_strings (dither_mode_combo, strings); + dither_mode_combo.set_active_text (_("None")); + + /* basic parameters */ + + basic_packer.set_spacings (6); + + strings.clear (); +#ifdef __APPLE__ + strings.push_back (X_("CoreAudio")); +#else + strings.push_back (X_("ALSA")); + strings.push_back (X_("OSS")); + strings.push_back (X_("FFADO")); +#endif + strings.push_back (X_("NetJACK")); + strings.push_back (X_("Dummy")); + set_popdown_strings (driver_combo, strings); + driver_combo.set_active_text (strings.front()); + + /* figure out available devices and set up interface_combo */ + + enumerate_devices (); + driver_combo.signal_changed().connect (mem_fun (*this, &EngineControl::driver_changed)); + driver_changed (); + + strings.clear (); + strings.push_back (_("Playback/Recording on 1 Device")); + strings.push_back (_("Playback/Recording on 2 Devices")); + strings.push_back (_("Playback only")); + strings.push_back (_("Recording only")); + set_popdown_strings (audio_mode_combo, strings); + audio_mode_combo.set_active_text (strings.front()); + + audio_mode_combo.signal_changed().connect (mem_fun (*this, &EngineControl::audio_mode_changed)); + audio_mode_changed (); + + row = 0; + + label = manage (new Label (_("Driver"))); + basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + row++; + + label = manage (new Label (_("Interface"))); + basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + row++; + + label = manage (new Label (_("Sample Rate"))); + basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + row++; + + label = manage (new Label (_("Buffer size"))); + basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + row++; + +#ifndef __APPLE__ + label = manage (new Label (_("Number of buffers"))); + basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + basic_packer.attach (periods_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + periods_spinner.set_value (2); + row++; +#endif + + label = manage (new Label (_("Approximate latency"))); + label->set_alignment (0.0, 0.5); + basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + row++; + + sample_rate_combo.signal_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency)); + periods_adjustment.signal_value_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency)); + period_size_combo.signal_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency)); + redisplay_latency(); + row++; + /* no audio mode with CoreAudio, its duplex or nuthin' */ + +#ifndef __APPLE__ + label = manage (new Label (_("Audio Mode"))); + basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + row++; +#endif + + interface_combo.set_size_request (125, -1); + input_device_combo.set_size_request (125, -1); + output_device_combo.set_size_request (125, -1); + + /* + + if (engine_running()) { + start_button.set_sensitive (false); + } else { + stop_button.set_sensitive (false); + } + + start_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine)); + stop_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine)); + */ + + button_box.pack_start (start_button, false, false); + button_box.pack_start (stop_button, false, false); + + // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0); + + /* options */ + + options_packer.set_spacings (6); + row = 0; + + options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + + realtime_button.signal_toggled().connect (mem_fun (*this, &EngineControl::realtime_changed)); + realtime_changed (); + +#ifndef __APPLE__ + label = manage (new Label (_("Realtime Priority"))); + label->set_alignment (1.0, 0.5); + options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + options_packer.attach (priority_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + priority_spinner.set_value (60); + + options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; +#else + options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; +#endif + + strings.clear (); + strings.push_back (_("Ignore")); + strings.push_back ("500 msec"); + strings.push_back ("1 sec"); + strings.push_back ("2 sec"); + strings.push_back ("10 sec"); + set_popdown_strings (timeout_combo, strings); + timeout_combo.set_active_text (strings.front ()); + + label = manage (new Label (_("Client timeout"))); + label->set_alignment (1.0, 0.5); + options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0)); + options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + + label = manage (new Label (_("Number of ports"))); + label->set_alignment (1.0, 0.5); + options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0)); + options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + +#ifndef __APPLE__ + label = manage (new Label (_("Dither"))); + label->set_alignment (1.0, 0.5); + options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0)); + options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; +#endif + + find_jack_servers (server_strings); + + if (server_strings.empty()) { + fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg; + /*NOTREACHED*/ + } + + set_popdown_strings (serverpath_combo, server_strings); + serverpath_combo.set_active_text (server_strings.front()); + + if (server_strings.size() > 1) { + label = manage (new Label (_("Server:"))); + options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + label->set_alignment (0.0, 0.5); + options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + } + + /* device settings */ + + device_packer.set_spacings (6); + row = 0; + +#ifndef __APPLE__ + label = manage (new Label (_("Input device"))); + label->set_alignment (1.0, 0.5); + device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0); + device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0); + ++row; + label = manage (new Label (_("Output device"))); + label->set_alignment (1.0, 0.5); + device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0); + device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0); + ++row; +#endif + label = manage (new Label (_("Input channels"))); + label->set_alignment (1.0, 0.5); + device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0); + device_packer.attach (input_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0); + ++row; + label = manage (new Label (_("Output channels"))); + label->set_alignment (1.0, 0.5); + device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0); + device_packer.attach (output_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0); + ++row; + label = manage (new Label (_("Hardware input latency (samples)"))); + label->set_alignment (1.0, 0.5); + device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0); + device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0); + ++row; + label = manage (new Label (_("Hardware output latency (samples)"))); + label->set_alignment (1.0, 0.5); + device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0); + device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0); + ++row; + + basic_hbox.pack_start (basic_packer, false, false); + options_hbox.pack_start (options_packer, false, false); + + device_packer.set_border_width (12); + options_packer.set_border_width (12); + basic_packer.set_border_width (12); + + notebook.pages().push_back (TabElem (basic_hbox, _("Device"))); + notebook.pages().push_back (TabElem (options_hbox, _("Options"))); + notebook.pages().push_back (TabElem (device_packer, _("Advanced"))); + notebook.set_border_width (12); + + set_border_width (12); + pack_start (notebook); +} + +EngineControl::~EngineControl () +{ + +} + +void +EngineControl::build_command_line (vector<string>& cmd) +{ + string str; + string driver; + bool using_oss = false; + bool using_alsa = false; + bool using_coreaudio = false; + bool using_netjack = false; + bool using_ffado = false; + + /* first, path to jackd */ + + cmd.push_back (serverpath_combo.get_active_text ()); + + /* now jackd arguments */ + + str = timeout_combo.get_active_text (); + if (str != _("Ignore")) { + double secs; + uint32_t msecs; + atof (str); + msecs = (uint32_t) floor (secs * 1000.0); + cmd.push_back ("-t"); + cmd.push_back (to_string (msecs, std::dec)); + } + + if (no_memory_lock_button.get_active()) { + cmd.push_back ("-m"); /* no munlock */ + } + + cmd.push_back ("-p"); /* port max */ + cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec)); + + if (realtime_button.get_active()) { + cmd.push_back ("-R"); + cmd.push_back ("-P"); + cmd.push_back (to_string ((uint32_t) floor (priority_spinner.get_value()), std::dec)); + } + + if (unlock_memory_button.get_active()) { + cmd.push_back ("-u"); + } + + if (verbose_output_button.get_active()) { + cmd.push_back ("-v"); + } + + /* now add fixed arguments (not user-selectable) */ + + cmd.push_back ("-T"); // temporary */ + + /* next the driver */ + + cmd.push_back ("-d"); + + driver = driver_combo.get_active_text (); + if (driver == X_("ALSA")) { + using_alsa = true; + cmd.push_back ("alsa"); + } else if (driver == X_("OSS")) { + using_oss = true; + cmd.push_back ("oss"); + } else if (driver == X_("CoreAudio")) { + using_coreaudio = true; + cmd.push_back ("coreaudio"); + } else if (driver == X_("NetJACK")) { + using_netjack = true; + cmd.push_back ("netjack"); + } else if (driver == X_("FFADO")) { + using_ffado = true; + cmd.push_back ("ffado"); + } + + /* driver arguments */ + + if (!using_coreaudio) { + str = audio_mode_combo.get_active_text(); + + if (str == _("Playback/Recording on 1 Device")) { + + /* relax */ + + } else if (str == _("Playback/Recording on 2 Devices")) { + + cmd.push_back ("-C"); + cmd.push_back (get_device_name (driver, input_device_combo.get_active_text())); + cmd.push_back ("-P"); + cmd.push_back (get_device_name (driver, output_device_combo.get_active_text())); + + } else if (str == _("Playback only")) { + cmd.push_back ("-P"); + } else if (str == _("Recording only")) { + cmd.push_back ("-C"); + } + + cmd.push_back ("-n"); + cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec)); + } + + cmd.push_back ("-r"); + cmd.push_back (to_string (get_rate(), std::dec)); + + cmd.push_back ("-p"); + cmd.push_back (period_size_combo.get_active_text()); + + if (using_alsa) { + + if (audio_mode_combo.get_active_text() != _("Playback/Recording on 2 Devices")) { + cmd.push_back ("-d"); + cmd.push_back (get_device_name (driver, interface_combo.get_active_text())); + } + + if (hw_meter_button.get_active()) { + cmd.push_back ("-M"); + } + + if (hw_monitor_button.get_active()) { + cmd.push_back ("-H"); + } + + str = dither_mode_combo.get_active_text(); + + if (str == _("None")) { + } else if (str == _("Triangular")) { + cmd.push_back ("-z triangular"); + } else if (str == _("Rectangular")) { + cmd.push_back ("-z rectangular"); + } else if (str == _("Shaped")) { + cmd.push_back ("-z shaped"); + } + + if (force16bit_button.get_active()) { + cmd.push_back ("-S"); + } + + if (soft_mode_button.get_active()) { + cmd.push_back ("-s"); + } + + } else if (using_coreaudio) { + +#ifdef __APPLE__ + // note: older versions of the CoreAudio JACK backend use -n instead of -d here + cmd.push_back ("-d"); + cmd.push_back (get_device_name (driver, interface_combo.get_active_text())); +#endif + + } else if (using_oss) { + + } else if (using_netjack) { + + } +} + +bool +EngineControl::engine_running () +{ + jack_status_t status; + jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status); + + if (status == 0) { + jack_client_close (c); + return true; + } + return false; +} + +int +EngineControl::setup_engine () +{ + vector<string> args; + std::string cwd = "/tmp"; + + build_command_line (args); + + Glib::ustring jackdrc_path = Glib::get_home_dir(); + jackdrc_path += "/.jackdrc"; + + ofstream jackdrc (jackdrc_path.c_str()); + if (!jackdrc) { + error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg; + return -1; + } + + for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) { + jackdrc << (*i) << ' '; + } + jackdrc << endl; + jackdrc.close (); + + _used = true; + + return 0; +} + +void +EngineControl::realtime_changed () +{ +#ifndef __APPLE__ + priority_spinner.set_sensitive (realtime_button.get_active()); +#endif +} + +void +EngineControl::enumerate_devices () +{ + /* note: case matters for the map keys */ + +#ifdef __APPLE__ + devices["CoreAudio"] = enumerate_coreaudio_devices (); +#else + devices["ALSA"] = enumerate_alsa_devices (); + devices["FFADO"] = enumerate_ffado_devices (); + devices["OSS"] = enumerate_oss_devices (); + devices["Dummy"] = enumerate_dummy_devices (); + devices["NetJACK"] = enumerate_netjack_devices (); +#endif +} + +#ifdef __APPLE__ +static OSStatus +getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize) +{ + UInt32 size = sizeof(CFStringRef); + CFStringRef UI; + OSStatus res = AudioDeviceGetProperty(id, 0, false, + kAudioDevicePropertyDeviceUID, &size, &UI); + if (res == noErr) + CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding()); + CFRelease(UI); + return res; +} + +vector<string> +EngineControl::enumerate_coreaudio_devices () +{ + vector<string> devs; + + // Find out how many Core Audio devices are there, if any... + // (code snippet gently "borrowed" from St?hane Letz jackdmp;) + OSStatus err; + Boolean isWritable; + size_t outSize = sizeof(isWritable); + + backend_devs.clear (); + + err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, + &outSize, &isWritable); + if (err == noErr) { + // Calculate the number of device available... + int numCoreDevices = outSize / sizeof(AudioDeviceID); + // Make space for the devices we are about to get... + AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices]; + err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, + &outSize, (void *) coreDeviceIDs); + if (err == noErr) { + // Look for the CoreAudio device name... + char coreDeviceName[256]; + size_t nameSize; + for (int i = 0; i < numCoreDevices; i++) { + + nameSize = sizeof (coreDeviceName); + + err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i], + 0, true, kAudioDevicePropertyDeviceName, + &outSize, &isWritable); + if (err == noErr) { + err = AudioDeviceGetProperty(coreDeviceIDs[i], + 0, true, kAudioDevicePropertyDeviceName, + &nameSize, (void *) coreDeviceName); + if (err == noErr) { + char drivername[128]; + + // this returns the unique id for the device + // that must be used on the commandline for jack + + if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) { + devs.push_back (coreDeviceName); + backend_devs.push_back (drivername); + } + } + } + } + } + delete [] coreDeviceIDs; + } + + return devs; +} +#else +vector<string> +EngineControl::enumerate_alsa_devices () +{ + vector<string> devs; + + 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; + + backend_devs.clear (); + + while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) { + + devname = "hw:"; + devname += to_string (cardnum, std::dec); + + if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) { + + while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) { + + bool have_playback = false; + bool have_capture = false; + + /* find duplex devices only */ + + 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) { + have_capture = true; + } + + 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) { + have_playback = true; + } + + if (have_capture && have_playback) { + devs.push_back (snd_pcm_info_get_name (pcminfo)); + devname += ','; + devname += to_string (device, std::dec); + backend_devs.push_back (devname); + } + } + + snd_ctl_close(handle); + } + } + + return devs; +} + +vector<string> +EngineControl::enumerate_ffado_devices () +{ + vector<string> devs; + return devs; +} +vector<string> +EngineControl::enumerate_oss_devices () +{ + vector<string> devs; + return devs; +} +vector<string> +EngineControl::enumerate_dummy_devices () +{ + vector<string> devs; + return devs; +} +vector<string> +EngineControl::enumerate_netjack_devices () +{ + vector<string> devs; + return devs; +} +#endif + +void +EngineControl::driver_changed () +{ + string driver = driver_combo.get_active_text(); + vector<string>& strings = devices[driver]; + string::size_type maxlen = 0; + int maxindex = -1; + int n = 0; + + for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) { + if ((*i).length() > maxlen) { + maxlen = (*i).length(); + maxindex = n; + } + } + + set_popdown_strings (interface_combo, strings); + set_popdown_strings (input_device_combo, strings); + set_popdown_strings (output_device_combo, strings); + + if (!strings.empty()) { + interface_combo.set_active_text (strings.front()); + input_device_combo.set_active_text (strings.front()); + output_device_combo.set_active_text (strings.front()); + } + + if (driver == "ALSA") { + soft_mode_button.set_sensitive (true); + force16bit_button.set_sensitive (true); + hw_monitor_button.set_sensitive (true); + hw_meter_button.set_sensitive (true); + monitor_button.set_sensitive (true); + } else { + soft_mode_button.set_sensitive (false); + force16bit_button.set_sensitive (false); + hw_monitor_button.set_sensitive (false); + hw_meter_button.set_sensitive (false); + monitor_button.set_sensitive (false); + } +} + +uint32_t +EngineControl::get_rate () +{ + return atoi (sample_rate_combo.get_active_text ()); +} + +void +EngineControl::redisplay_latency () +{ + uint32_t rate = get_rate(); +#ifdef __APPLE_ + float periods = 2; +#else + float periods = periods_adjustment.get_value(); +#endif + float period_size = atof (period_size_combo.get_active_text()); + + char buf[32]; + snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0)); + + latency_label.set_text (buf); +} + +void +EngineControl::audio_mode_changed () +{ + Glib::ustring str = audio_mode_combo.get_active_text(); + + if (str == _("Playback/Recording on 1 Device")) { + input_device_combo.set_sensitive (false); + output_device_combo.set_sensitive (false); + } else if (str == _("Playback/Recording on 2 Devices")) { + input_device_combo.set_sensitive (true); + output_device_combo.set_sensitive (true); + } else if (str == _("Playback only")) { + output_device_combo.set_sensitive (true); + } else if (str == _("Recording only")) { + input_device_combo.set_sensitive (true); + } +} + +void +EngineControl::find_jack_servers (vector<string>& strings) +{ +#ifdef __APPLE__ + /* this magic lets us finds the path to the OSX bundle, and then + we infer JACK's location from there + */ + + char execpath[MAXPATHLEN+1]; + uint32_t pathsz = sizeof (execpath); + + _NSGetExecutablePath (execpath, &pathsz); + + cerr << " execpath = " << execpath << endl; + + Glib::ustring path (Glib::path_get_dirname (execpath)); + path += "/jackd"; + + if (Glib::file_test (path, FILE_TEST_EXISTS)) { + strings.push_back (path); + cerr << "Found jack in " << path << endl; + } + + if (getenv ("ARDOUR_WITH_JACK")) { + /* no other options - only use the JACK we supply */ + if (strings.empty()) { + fatal << _("JACK appears to be missing from the Ardour bundle") << endmsg; + /*NOTREACHED*/ + } + return; + } +#endif + + if (Glib::file_test ("/usr/bin/jackd", FILE_TEST_EXISTS)) { + strings.push_back ("/usr/bin/jackd"); + } + if (Glib::file_test ("/usr/local/bin/jackd", FILE_TEST_EXISTS)) { + strings.push_back ("/usr/local/bin/jackd"); + } + if (Glib::file_test ("/opt/bin/jackd", FILE_TEST_EXISTS)) { + strings.push_back ("/opt/bin/jackd"); + } + if (Glib::file_test ("/usr/bin/jackdmp", FILE_TEST_EXISTS)) { + strings.push_back ("/usr/bin/jackd"); + } + if (Glib::file_test ("/usr/local/bin/jackdmp", FILE_TEST_EXISTS)) { + strings.push_back ("/usr/local/bin/jackd"); + } + if (Glib::file_test ("/opt/bin/jackdmp", FILE_TEST_EXISTS)) { + strings.push_back ("/opt/bin/jackd"); + } + +} + +string +EngineControl::get_device_name (const string& driver, const string& human_readable) +{ + vector<string>::iterator n; + vector<string>::iterator i; + + if (backend_devs.empty()) { + return human_readable; + } + + for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) { + if (human_readable == (*i)) { + return (*n); + } + } + + if (i == devices[driver].end()) { + fatal << string_compose (_("programming error: %1"), "true hardware name for ID missing") << endmsg; + /*NOTREACHED*/ + } + + /* keep gcc happy */ + + return string(); +} + +XMLNode& +EngineControl::get_state () +{ + XMLNode* root = new XMLNode ("AudioSetup"); + XMLNode* child; + Glib::ustring path; + + child = new XMLNode ("periods"); + child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("priority"); + child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("ports"); + child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("inchannels"); + child->add_property ("val", to_string (input_channels.get_value(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("outchannels"); + child->add_property ("val", to_string (output_channels.get_value(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("inlatency"); + child->add_property ("val", to_string (input_latency.get_value(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("outlatency"); + child->add_property ("val", to_string (output_latency.get_value(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("realtime"); + child->add_property ("val", to_string (realtime_button.get_active(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("nomemorylock"); + child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("unlockmemory"); + child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("softmode"); + child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("force16bit"); + child->add_property ("val", to_string (force16bit_button.get_active(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("hwmonitor"); + child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("hwmeter"); + child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("verbose"); + child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("samplerate"); + child->add_property ("val", sample_rate_combo.get_active_text()); + root->add_child_nocopy (*child); + + child = new XMLNode ("periodsize"); + child->add_property ("val", period_size_combo.get_active_text()); + root->add_child_nocopy (*child); + + child = new XMLNode ("serverpath"); + child->add_property ("val", serverpath_combo.get_active_text()); + root->add_child_nocopy (*child); + + child = new XMLNode ("driver"); + child->add_property ("val", driver_combo.get_active_text()); + root->add_child_nocopy (*child); + + child = new XMLNode ("interface"); + child->add_property ("val", interface_combo.get_active_text()); + root->add_child_nocopy (*child); + + child = new XMLNode ("timeout"); + child->add_property ("val", timeout_combo.get_active_text()); + root->add_child_nocopy (*child); + + child = new XMLNode ("dither"); + child->add_property ("val", dither_mode_combo.get_active_text()); + root->add_child_nocopy (*child); + + child = new XMLNode ("audiomode"); + child->add_property ("val", audio_mode_combo.get_active_text()); + root->add_child_nocopy (*child); + + child = new XMLNode ("inputdevice"); + child->add_property ("val", input_device_combo.get_active_text()); + root->add_child_nocopy (*child); + + child = new XMLNode ("outputdevice"); + child->add_property ("val", output_device_combo.get_active_text()); + root->add_child_nocopy (*child); + + return *root; +} + +void +EngineControl::set_state (const XMLNode& root) +{ + XMLNodeList clist; + XMLNodeConstIterator citer; + XMLNode* child; + XMLProperty* prop; + + int val; + string strval; + + clist = root.children(); + + for (citer = clist.begin(); citer != clist.end(); ++citer) { + + child = *citer; + + prop = child->property ("val"); + + if (!prop || prop->value().empty()) { + error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg; + continue; + } + + strval = prop->value(); + + /* adjustments/spinners */ + + if (child->name() == "periods") { + val = atoi (strval); + periods_adjustment.set_value(val); + } else if (child->name() == "priority") { + val = atoi (strval); + priority_adjustment.set_value(val); + } else if (child->name() == "ports") { + val = atoi (strval); + ports_adjustment.set_value(val); + } else if (child->name() == "inchannels") { + val = atoi (strval); + input_channels.set_value(val); + } else if (child->name() == "outchannels") { + val = atoi (strval); + output_channels.set_value(val); + } else if (child->name() == "inlatency") { + val = atoi (strval); + input_latency.set_value(val); + } else if (child->name() == "outlatency") { + val = atoi (strval); + output_latency.set_value(val); + } + + /* buttons */ + + else if (child->name() == "realtime") { + val = atoi (strval); + realtime_button.set_active(val); + } else if (child->name() == "nomemorylock") { + val = atoi (strval); + no_memory_lock_button.set_active(val); + } else if (child->name() == "unlockmemory") { + val = atoi (strval); + unlock_memory_button.set_active(val); + } else if (child->name() == "softmode") { + val = atoi (strval); + soft_mode_button.set_active(val); + } else if (child->name() == "force16bit") { + val = atoi (strval); + force16bit_button.set_active(val); + } else if (child->name() == "hwmonitor") { + val = atoi (strval); + hw_monitor_button.set_active(val); + } else if (child->name() == "hwmeter") { + val = atoi (strval); + hw_meter_button.set_active(val); + } else if (child->name() == "verbose") { + val = atoi (strval); + verbose_output_button.set_active(val); + } + + /* combos */ + + else if (child->name() == "samplerate") { + sample_rate_combo.set_active_text(strval); + } else if (child->name() == "periodsize") { + period_size_combo.set_active_text(strval); + } else if (child->name() == "serverpath") { + /* do not allow us to use a server path that doesn't + exist on this system. this handles cases where + the user has an RC file listing a serverpath + from some other machine. + */ + vector<string>::iterator x; + for (x = server_strings.begin(); x != server_strings.end(); ++x) { + if (*x == strval) { + break; + } + } + if (x != server_strings.end()) { + serverpath_combo.set_active_text (strval); + } else { + warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"), + strval) + << endmsg; + } + } else if (child->name() == "driver") { + driver_combo.set_active_text(strval); + } else if (child->name() == "interface") { + interface_combo.set_active_text(strval); + } else if (child->name() == "timeout") { + timeout_combo.set_active_text(strval); + } else if (child->name() == "dither") { + dither_mode_combo.set_active_text(strval); + } else if (child->name() == "audiomode") { + audio_mode_combo.set_active_text(strval); + } else if (child->name() == "inputdevice") { + input_device_combo.set_active_text(strval); + } else if (child->name() == "outputdevice") { + output_device_combo.set_active_text(strval); + } + } +} diff --git a/gtk2_ardour/engine_dialog.h b/gtk2_ardour/engine_dialog.h new file mode 100644 index 0000000000..925d12acbb --- /dev/null +++ b/gtk2_ardour/engine_dialog.h @@ -0,0 +1,106 @@ +#ifndef __gtk2_ardour_engine_dialog_h__ +#define __gtk2_ardour_engine_dialog_h__ + +#include <map> +#include <vector> +#include <string> + +#include <gtkmm/checkbutton.h> +#include <gtkmm/spinbutton.h> +#include <gtkmm/notebook.h> +#include <gtkmm/comboboxtext.h> +#include <gtkmm/table.h> +#include <gtkmm/expander.h> +#include <gtkmm/box.h> +#include <gtkmm/buttonbox.h> +#include <gtkmm/button.h> + +class EngineControl : public Gtk::VBox { + public: + EngineControl (); + ~EngineControl (); + + static bool engine_running (); + int setup_engine (); + + bool was_used() const { return _used; } + XMLNode& get_state (); + void set_state (const XMLNode&); + + private: + Gtk::Adjustment periods_adjustment; + Gtk::SpinButton periods_spinner; + Gtk::Adjustment priority_adjustment; + Gtk::SpinButton priority_spinner; + Gtk::Adjustment ports_adjustment; + Gtk::SpinButton ports_spinner; + Gtk::SpinButton input_channels; + Gtk::SpinButton output_channels; + Gtk::SpinButton input_latency; + Gtk::SpinButton output_latency; + Gtk::Label latency_label; + + Gtk::CheckButton realtime_button; + Gtk::CheckButton no_memory_lock_button; + Gtk::CheckButton unlock_memory_button; + Gtk::CheckButton soft_mode_button; + Gtk::CheckButton monitor_button; + Gtk::CheckButton force16bit_button; + Gtk::CheckButton hw_monitor_button; + Gtk::CheckButton hw_meter_button; + Gtk::CheckButton verbose_output_button; + + Gtk::Button start_button; + Gtk::Button stop_button; + Gtk::HButtonBox button_box; + + Gtk::ComboBoxText sample_rate_combo; + Gtk::ComboBoxText period_size_combo; + + Gtk::ComboBoxText preset_combo; + Gtk::ComboBoxText serverpath_combo; + Gtk::ComboBoxText driver_combo; + Gtk::ComboBoxText interface_combo; + Gtk::ComboBoxText timeout_combo; + Gtk::ComboBoxText dither_mode_combo; + Gtk::ComboBoxText audio_mode_combo; + Gtk::ComboBoxText input_device_combo; + Gtk::ComboBoxText output_device_combo; + + Gtk::Table basic_packer; + Gtk::Table options_packer; + Gtk::Table device_packer; + Gtk::HBox basic_hbox; + Gtk::HBox options_hbox; + Gtk::HBox device_hbox; + Gtk::Notebook notebook; + + bool _used; + + void realtime_changed (); + void driver_changed (); + void build_command_line (std::vector<std::string>&); + + std::map<std::string,std::vector<std::string> > devices; + std::vector<std::string> backend_devs; + void enumerate_devices (); + +#ifdef __APPLE__ + std::vector<std::string> enumerate_coreaudio_devices (); +#else + std::vector<std::string> enumerate_alsa_devices (); + std::vector<std::string> enumerate_oss_devices (); + std::vector<std::string> enumerate_netjack_devices (); + std::vector<std::string> enumerate_ffado_devices (); + std::vector<std::string> enumerate_dummy_devices (); +#endif + + void redisplay_latency (); + uint32_t get_rate(); + void audio_mode_changed (); + std::vector<std::string> server_strings; + void find_jack_servers (std::vector<std::string>&); + std::string get_device_name (const std::string& driver, const std::string& human_readable_name); +}; + +#endif /* __gtk2_ardour_engine_dialog_h__ */ diff --git a/gtk2_ardour/enums.cc b/gtk2_ardour/enums.cc index e6c81598fb..c33b2965c3 100644 --- a/gtk2_ardour/enums.cc +++ b/gtk2_ardour/enums.cc @@ -20,11 +20,13 @@ #include <pbd/enumwriter.h> #include "audio_clock.h" +#include "editing.h" #include "enums.h" using namespace std; using namespace PBD; using namespace ARDOUR; +using namespace Editing; void setup_gtk_ardour_enums () @@ -35,6 +37,7 @@ setup_gtk_ardour_enums () AudioClock::Mode clock_mode; Width width; + ImportMode import_mode; #define REGISTER(e) enum_writer.register_distinct (typeid(e).name(), i, s); i.clear(); s.clear() #define REGISTER_BITS(e) enum_writer.register_bits (typeid(e).name(), i, s); i.clear(); s.clear() @@ -51,4 +54,10 @@ setup_gtk_ardour_enums () REGISTER_ENUM (Wide); REGISTER_ENUM (Narrow); REGISTER (width); + + REGISTER_ENUM (ImportAsTrack); + REGISTER_ENUM (ImportToTrack); + REGISTER_ENUM (ImportAsRegion); + REGISTER_ENUM (ImportAsTapeTrack); + REGISTER (import_mode); } diff --git a/gtk2_ardour/keyboard.cc b/gtk2_ardour/keyboard.cc index 90fea321de..e6eaffefcb 100644 --- a/gtk2_ardour/keyboard.cc +++ b/gtk2_ardour/keyboard.cc @@ -48,14 +48,14 @@ uint32_t Keyboard::Shift = GDK_SHIFT_MASK; uint32_t Keyboard::Alt = GDK_MOD1_MASK; uint32_t Keyboard::Meta; -Keyboard* Keyboard::_the_keyboard = 0; +Keyboard* Keyboard::_the_keyboard = 0; +Gtk::Window* Keyboard::current_window = 0; +bool Keyboard::_some_magic_widget_has_focus = false; /* set this to initially contain the modifiers we care about, then track changes in ::set_edit_modifier() etc. */ GdkModifierType Keyboard::RelevantModifierKeyMask; -bool Keyboard::_some_magic_widget_has_focus = false; - void Keyboard::magic_widget_grab_focus () { @@ -199,6 +199,13 @@ Keyboard::snooper (GtkWidget *widget, GdkEventKey *event) } + if (event->type == GDK_KEY_RELEASE && event->keyval == GDK_w && modifier_state_equals (event->state, Control)) { + if (current_window) { + current_window->hide (); + current_window = 0; + } + } + return false; } @@ -211,6 +218,7 @@ Keyboard::key_is_down (uint32_t keyval) bool Keyboard::enter_window (GdkEventCrossing *ev, Gtk::Window* win) { + current_window = win; return false; } @@ -236,6 +244,7 @@ Keyboard::leave_window (GdkEventCrossing *ev, Gtk::Window* win) cerr << "clearing current target\n"; } state.clear (); + current_window = 0; } return false; diff --git a/gtk2_ardour/keyboard.h b/gtk2_ardour/keyboard.h index 3d9e1c9f41..fb22f2eca9 100644 --- a/gtk2_ardour/keyboard.h +++ b/gtk2_ardour/keyboard.h @@ -105,6 +105,7 @@ class Keyboard : public sigc::trackable, PBD::Stateful static guint delete_but; static guint delete_mod; static guint snap_mod; + static Gtk::Window* current_window; static gint _snooper (GtkWidget*, GdkEventKey*, gpointer); gint snooper (GtkWidget*, GdkEventKey*); diff --git a/gtk2_ardour/main.cc b/gtk2_ardour/main.cc index 67e59d4b80..1e9b27d8a7 100644 --- a/gtk2_ardour/main.cc +++ b/gtk2_ardour/main.cc @@ -47,7 +47,7 @@ #include "i18n.h" using namespace Gtk; -using namespace GTK_ARDOUR; +using namespace ARDOUR_COMMAND_LINE; using namespace ARDOUR; using namespace PBD; using namespace sigc; @@ -57,6 +57,7 @@ TextReceiver text_receiver ("ardour"); extern int curvetest (string); static ARDOUR_UI *ui = 0; +static char* localedir = LOCALEDIR; gint show_ui_callback (void *arg) @@ -98,65 +99,129 @@ Please consider the possibilities, and perhaps (re)start JACK.")); win.run (); } -static bool -maybe_load_session () -{ - /* If no session name is given: we're not loading a session yet, nor creating a new one */ - if (!session_name.length()) { - ui->hide_splash (); - if (!Config->get_no_new_session_dialog()) { - if (!ui->new_session ()) { - return false; - } - } - return true; +#ifdef __APPLE__ + +#include <mach-o/dyld.h> +#include <sys/param.h> +#include <fstream> + +void +fixup_bundle_environment () +{ + if (!getenv ("ARDOUR_BUNDLED")) { + return; } - /* Load session or start the new session dialog */ - string name, path; + char execpath[MAXPATHLEN+1]; + uint32_t pathsz = sizeof (execpath); - bool isnew; + _NSGetExecutablePath (execpath, &pathsz); - if (find_session (session_name, path, name, isnew)) { - error << string_compose(_("could not load command line session \"%1\""), session_name) << endmsg; - return false; + Glib::ustring exec_path (execpath); + Glib::ustring dir_path = Glib::path_get_dirname (exec_path); + Glib::ustring path; + const char *cstr = getenv ("PATH"); + + /* ensure that we find any bundled executables (e.g. JACK) */ + + path = dir_path; + if (cstr) { + path += ':'; + path += cstr; } + setenv ("PATH", path.c_str(), 1); + + path = dir_path; + path += "/../Resources"; + path += dir_path; + path += "/../Resources/Surfaces"; + path += dir_path; + path += "/../Resources/Panners"; + + setenv ("ARDOUR_MODULE_PATH", path.c_str(), 1); + + path = dir_path; + path += "/../Resources/icons:"; + path += dir_path; + path += "/../Resources/pixmaps:"; + path += dir_path; + path += "/../Resources/share:"; + path += dir_path; + path += "/../Resources"; + + setenv ("ARDOUR_PATH", path.c_str(), 1); + setenv ("ARDOUR_CONFIG_PATH", path.c_str(), 1); + setenv ("ARDOUR_DATA_PATH", path.c_str(), 1); + + cstr = getenv ("LADSPA_PATH"); + if (cstr) { + path = cstr; + path += ':'; + } + path = dir_path; + path += "/../Plugins"; + + setenv ("LADSPA_PATH", path.c_str(), 1); - if (!new_session) { - - /* Loading a session, but the session doesn't exist */ - if (isnew) { - error << string_compose (_("\n\nNo session named \"%1\" exists.\n" - "To create it from the command line, start ardour as \"ardour --new %1"), path) - << endmsg; - return false; - } + path = dir_path; + path += "/../Frameworks/clearlooks"; - if (ui->load_session (path, name)) { - /* it failed */ - return false; - } + setenv ("GTK_PATH", path.c_str(), 1); - } else { + path = dir_path; + path += "/../Resources/locale"; + + localedir = strdup (path.c_str()); + + /* write a pango.rc file and tell pango to use it */ - /* TODO: This bit of code doesn't work properly yet - Glib::signal_idle().connect (bind (mem_fun (*ui, &ARDOUR_UI::cmdline_new_session), path)); - ui->set_will_create_new_session_automatically (true); - */ + path = dir_path; + path += "/../Resources/pango.rc"; + + std::ofstream pangorc (path.c_str()); + if (!pangorc) { + error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg; + } else { + pangorc << "[Pango]\nModuleFiles="; + Glib::ustring mpath = dir_path; + mpath += "/../Resources/pango.modules"; + pangorc << mpath << endl; - /* Show the NSD */ - ui->hide_splash (); - if (!Config->get_no_new_session_dialog()) { - if (!ui->new_session ()) { - return false; - } - } + pangorc.close (); + setenv ("PANGO_RC_FILE", path.c_str(), 1); } - return true; + // gettext charset aliases + + setenv ("CHARSETALIASDIR", path.c_str(), 1); + + // font config + + path = dir_path; + path += "/../Resources/fonts.conf"; + + setenv ("FONTCONFIG_FILE", path.c_str(), 1); + + // GDK Pixbuf loader module file + + path = dir_path; + path += "/../Resources/gdk-pixbuf.loaders"; + + setenv ("GDK_PIXBUF_MODULE_FILE", path.c_str(), 1); + + if (getenv ("ARDOUR_WITH_JACK")) { + // JACK driver dir + + path = dir_path; + path += "/../Frameworks"; + + setenv ("JACK_DRIVER_DIR", path.c_str(), 1); + } } +#endif + #ifdef VST_SUPPORT /* this is called from the entry point of a wine-compiled executable that is linked against gtk2_ardour built @@ -167,15 +232,17 @@ int ardour_main (int argc, char *argv[]) #else int main (int argc, char *argv[]) #endif - { - ARDOUR::AudioEngine *engine = NULL; vector<Glib::ustring> null_file_list; - Glib::thread_init(); +#ifdef __APPLE__ + fixup_bundle_environment (); +#endif + + Glib::thread_init(); gtk_set_locale (); - (void) bindtextdomain (PACKAGE, LOCALEDIR); + (void) bindtextdomain (PACKAGE, localedir); /* our i18n translations are all in UTF-8, so make sure that even if the user locale doesn't specify UTF-8, we use that when handling them. @@ -229,17 +296,13 @@ int main (int argc, char *argv[]) PBD::ID::init (); - try { + try { ui = new ARDOUR_UI (&argc, &argv); } catch (failed_constructor& err) { error << _("could not create ARDOUR GUI") << endmsg; exit (1); } - if (!keybindings_path.empty()) { - ui->set_keybindings_path (keybindings_path); - } - if (!no_splash) { ui->show_splash (); if (session_name.length()) { @@ -247,50 +310,17 @@ int main (int argc, char *argv[]) } } - try { - ARDOUR::init (use_vst, try_hw_optimization); - setup_gtk_ardour_enums (); - Config->set_current_owner (ConfigVariableBase::Interface); - ui->setup_profile (); - - try { - engine = new ARDOUR::AudioEngine (jack_client_name); - } catch (AudioEngine::NoBackendAvailable& err) { - gui_jack_error (); - error << string_compose (_("Could not connect to JACK server as \"%1\""), jack_client_name) << endmsg; - return -1; - } - - ARDOUR::setup_midi(*engine); - - ui->set_engine (*engine); - - } catch (failed_constructor& err) { - error << _("could not initialize Ardour.") << endmsg; - return -1; - } - - ui->start_engine (); - - if (maybe_load_session ()) { - ui->run (text_receiver); - ui = 0; + if (!keybindings_path.empty()) { + ui->set_keybindings_path (keybindings_path); } - delete engine; - ARDOUR::cleanup (); + ui->run (text_receiver); + ui = 0; - if (ui) { - ui->kill(); - } - + ARDOUR::cleanup (); pthread_cancel_all (); - - exit (0); - return 0; } #ifdef VST_SUPPORT } // end of extern C block #endif - diff --git a/gtk2_ardour/mixer_strip.cc b/gtk2_ardour/mixer_strip.cc index dfe5a268fa..f791314d75 100644 --- a/gtk2_ardour/mixer_strip.cc +++ b/gtk2_ardour/mixer_strip.cc @@ -1227,19 +1227,19 @@ MixerStrip::route_active_changed () } else if (is_audio_track()) { if (_route->active()) { set_name ("AudioTrackStripBase"); - gpm.set_meter_strip_name ("AudioTrackStripBase"); + gpm.set_meter_strip_name ("AudioTrackMetrics"); } else { set_name ("AudioTrackStripBaseInactive"); - gpm.set_meter_strip_name ("AudioTrackStripBaseInactive"); + gpm.set_meter_strip_name ("AudioTrackMetricsInactive"); } gpm.set_fader_name ("AudioTrackFader"); } else { if (_route->active()) { set_name ("AudioBusStripBase"); - gpm.set_meter_strip_name ("AudioBusStripBase"); + gpm.set_meter_strip_name ("AudioBusMetrics"); } else { set_name ("AudioBusStripBaseInactive"); - gpm.set_meter_strip_name ("AudioBusStripBaseInactive"); + gpm.set_meter_strip_name ("AudioBusMetricsInactive"); } gpm.set_fader_name ("AudioBusFader"); diff --git a/gtk2_ardour/mixer_ui.cc b/gtk2_ardour/mixer_ui.cc index 89d7ef5cc4..e3f6cae6ac 100644 --- a/gtk2_ardour/mixer_ui.cc +++ b/gtk2_ardour/mixer_ui.cc @@ -61,12 +61,17 @@ using PBD::atoi; Mixer_UI::Mixer_UI () : Window (Gtk::WINDOW_TOPLEVEL) { + session = 0; _strip_width = Wide; track_menu = 0; mix_group_context_menu = 0; no_track_list_redisplay = false; in_group_row_change = false; _visible = false; + ignore_route_reorder = false; + ignore_sync = false; + + Route::SyncOrderKeys.connect (mem_fun (*this, &Mixer_UI::sync_order_keys)); scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK); scroller_base.set_name ("MixerWindow"); @@ -92,6 +97,7 @@ Mixer_UI::Mixer_UI () track_model->signal_row_deleted().connect (mem_fun (*this, &Mixer_UI::track_list_delete)); track_model->signal_row_changed().connect (mem_fun (*this, &Mixer_UI::track_list_change)); + track_model->signal_rows_reordered().connect (mem_fun (*this, &Mixer_UI::track_list_reorder)); CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1)); track_list_visible_cell->property_activatable() = true; @@ -318,6 +324,8 @@ Mixer_UI::add_strip (Session::RouteList& routes) row[track_columns.route] = route; row[track_columns.strip] = strip; + strip->set_old_order_key (track_model->children().size() - 1); + no_track_list_redisplay = false; redisplay_track_list (); @@ -349,6 +357,42 @@ Mixer_UI::remove_strip (MixerStrip* strip) } } +const char* +Mixer_UI::get_order_key() +{ + if (Config->get_sync_all_route_ordering()) { + return X_("editor"); + } else { + return X_("signal"); + } +} + +void +Mixer_UI::sync_order_keys () +{ + vector<int> neworder; + TreeModel::Children rows = track_model->children(); + TreeModel::Children::iterator ri; + + if (ignore_sync || !session || (session->state_of_the_state() & Session::Loading) || rows.empty()) { + return; + } + + for (ri = rows.begin(); ri != rows.end(); ++ri) { + neworder.push_back (0); + } + + for (ri = rows.begin(); ri != rows.end(); ++ri) { + boost::shared_ptr<Route> route = (*ri)[track_columns.route]; + MixerStrip* strip = (*ri)[track_columns.strip]; + neworder[route->order_key (get_order_key())] = strip->old_order_key (); + } + + ignore_route_reorder = true; + track_model->reorder (neworder); + ignore_route_reorder = false; +} + void Mixer_UI::follow_strip_selection () { @@ -594,6 +638,13 @@ Mixer_UI::hide_all_audiotracks () } void +Mixer_UI::track_list_reorder (const TreeModel::Path& path, const TreeModel::iterator& iter, int* new_order) +{ + session->set_remote_control_ids(); + redisplay_track_list (); +} + +void Mixer_UI::track_list_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter) { session->set_remote_control_ids(); @@ -632,6 +683,10 @@ Mixer_UI::redisplay_track_list () strip->set_marked_for_display (true); strip->route()->set_order_key (N_("signal"), order); + if (!ignore_route_reorder) { + strip->route()->set_order_key (get_order_key(), order); + } + if (strip->packed()) { if (strip->route()->is_master() || strip->route()->is_control()) { @@ -663,12 +718,18 @@ Mixer_UI::redisplay_track_list () } } } + + if (Config->get_sync_all_route_ordering() && !ignore_route_reorder) { + ignore_sync = true; + Route::SyncOrderKeys (); // EMIT SIGNAL + ignore_sync = false; + } } struct SignalOrderRouteSorter { bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) { /* use of ">" forces the correct sort order */ - return a->order_key ("signal") < b->order_key ("signal"); + return a->order_key (Mixer_UI::get_order_key()) < b->order_key (Mixer_UI::get_order_key()); } }; diff --git a/gtk2_ardour/mixer_ui.h b/gtk2_ardour/mixer_ui.h index 3c9355efb7..5d00acff5b 100644 --- a/gtk2_ardour/mixer_ui.h +++ b/gtk2_ardour/mixer_ui.h @@ -77,6 +77,8 @@ class Mixer_UI : public Gtk::Window void ensure_float (Gtk::Window&); RouteRedirectSelection& selection() { return _selection; } + + static const char* get_order_key(); private: ARDOUR::Session *session; @@ -148,6 +150,7 @@ class Mixer_UI : public Gtk::Window void track_list_change (const Gtk::TreeModel::Path&,const Gtk::TreeModel::iterator&); void track_list_delete (const Gtk::TreeModel::Path&); + void track_list_reorder (const Gtk::TreeModel::Path& path, const Gtk::TreeModel::iterator& iter, int* new_order); void initial_track_display (); void show_track_list_menu (); @@ -234,6 +237,10 @@ class Mixer_UI : public Gtk::Window Width _strip_width; + void sync_order_keys (); + bool ignore_route_reorder; + bool ignore_sync; + static const int32_t default_width = 478; static const int32_t default_height = 765; }; diff --git a/gtk2_ardour/new_session_dialog.cc b/gtk2_ardour/new_session_dialog.cc index 132af4a86c..ebf520fba7 100644 --- a/gtk2_ardour/new_session_dialog.cc +++ b/gtk2_ardour/new_session_dialog.cc @@ -23,6 +23,8 @@ #include <ardour/recent_sessions.h> #include <ardour/session_state_utils.h> #include <ardour/template_utils.h> +#include <ardour/session.h> +#include <ardour/profile.h> #include <gtkmm/entry.h> #include <gtkmm/filechooserbutton.h> @@ -44,9 +46,10 @@ using namespace PBD; NewSessionDialog::NewSessionDialog() : ArdourDialog ("session control") { + in_destructor = false; session_name_label = Gtk::manage(new class Gtk::Label(_("Name :"))); m_name = Gtk::manage(new class Gtk::Entry()); - m_name->set_text(GTK_ARDOUR::session_name); + m_name->set_text(ARDOUR_COMMAND_LINE::session_name); chan_count_label_1 = Gtk::manage(new class Gtk::Label(_("channels"))); chan_count_label_2 = Gtk::manage(new class Gtk::Label(_("channels"))); @@ -308,8 +311,11 @@ NewSessionDialog::NewSessionDialog() new_session_table->attach(*m_folder, 1, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::FILL, 0, 0); new_session_table->attach(*session_template_label, 0, 1, 2, 3, Gtk::FILL, Gtk::FILL, 0, 0); new_session_table->attach(*m_template, 1, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::FILL, 0, 0); - new_session_table->attach(*advanced_expander, 0, 2, 3, 4, Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 6); + if (!ARDOUR::Profile->get_sae()) { + new_session_table->attach(*advanced_expander, 0, 2, 3, 4, Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 6); + } + open_session_hbox->pack_start(*open_session_file_label, false, false, 12); open_session_hbox->pack_start(*m_open_filechooser, true, true, 12); m_treeview->set_flags(Gtk::CAN_FOCUS); @@ -358,7 +364,7 @@ NewSessionDialog::NewSessionDialog() // add_button(Gtk::Stock::HELP, Gtk::RESPONSE_HELP); add_button(Gtk::Stock::QUIT, Gtk::RESPONSE_CANCEL); add_button(Gtk::Stock::CLEAR, Gtk::RESPONSE_NONE); - m_okbutton = add_button(Gtk::Stock::NEW, Gtk::RESPONSE_OK); + m_okbutton = add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK); recent_model = Gtk::TreeStore::create (recent_columns); m_treeview->set_model (recent_model); @@ -401,7 +407,7 @@ NewSessionDialog::NewSessionDialog() set_default_response (Gtk::RESPONSE_OK); - if (!GTK_ARDOUR::session_name.length()) { + if (!ARDOUR_COMMAND_LINE::session_name.length()) { set_response_sensitive (Gtk::RESPONSE_OK, false); set_response_sensitive (Gtk::RESPONSE_NONE, false); } else { @@ -426,12 +432,39 @@ NewSessionDialog::NewSessionDialog() m_name->grab_focus(); } +NewSessionDialog::~NewSessionDialog() +{ + in_destructor = true; +} + +void +NewSessionDialog::set_have_engine (bool yn) +{ + if (yn) { + m_notebook->remove_page (engine_control); + } else { + // XXX this is a bit of crude hack. if we ever add or remove + // pages from the notebook, this is going to break. + if (m_notebook->get_n_pages () != 3) { + m_notebook->append_page (engine_control, _("Audio Setup")); + m_notebook->show_all_children(); + } + } +} + + void NewSessionDialog::set_session_name(const Glib::ustring& name) { m_name->set_text(name); } +void +NewSessionDialog::set_session_folder(const Glib::ustring& dir) +{ + // XXX DO SOMETHING +} + std::string NewSessionDialog::session_name() const { @@ -450,7 +483,7 @@ NewSessionDialog::session_name() const } */ - if (on_new_session_page ()) { + if (on_newable_page()) { return Glib::filename_from_utf8(m_name->get_text()); } else { if (m_treeview->get_selection()->count_selected_rows() == 0) { @@ -464,10 +497,9 @@ NewSessionDialog::session_name() const std::string NewSessionDialog::session_folder() const { - if (on_new_session_page ()) { + if (on_newable_page()) { return Glib::filename_from_utf8(m_folder->get_filename()); } else { - if (m_treeview->get_selection()->count_selected_rows() == 0) { std::string str = Glib::filename_from_utf8(m_open_filechooser->get_filename()); return Glib::path_get_dirname(str); @@ -563,9 +595,16 @@ NewSessionDialog::connect_outs_to_physical() const } bool -NewSessionDialog::on_new_session_page() const +NewSessionDialog::on_newable_page() const { - return (m_notebook->get_current_page() == 0); + return (m_notebook->get_current_page() == 0 || + m_notebook->get_current_page() == 2); +} + +int +NewSessionDialog::get_current_page() const +{ + return m_notebook->get_current_page(); } void @@ -595,7 +634,11 @@ NewSessionDialog::on_new_session_name_entry_changed () void NewSessionDialog::notebook_page_changed (GtkNotebookPage* np, uint pagenum) { - if (!on_new_session_page ()) { + if (in_destructor) { + return; + } + + if (!on_newable_page ()) { m_okbutton->set_label(_("Open")); set_response_sensitive (Gtk::RESPONSE_NONE, false); m_okbutton->set_image (*(new Gtk::Image (Gtk::Stock::OPEN, Gtk::ICON_SIZE_BUTTON))); @@ -635,7 +678,7 @@ NewSessionDialog::treeview_selection_changed () void NewSessionDialog::file_chosen () { - if (on_new_session_page ()) return; + if (on_newable_page ()) return; m_treeview->get_selection()->unselect_all(); diff --git a/gtk2_ardour/new_session_dialog.h b/gtk2_ardour/new_session_dialog.h index 2a2081e56a..53ec3eabf2 100644 --- a/gtk2_ardour/new_session_dialog.h +++ b/gtk2_ardour/new_session_dialog.h @@ -38,6 +38,7 @@ #include <glibmm/refptr.h> #include "ardour_dialog.h" +#include "engine_dialog.h" namespace Gtk { class Entry; @@ -54,8 +55,10 @@ class NewSessionDialog : public ArdourDialog public: NewSessionDialog(); + ~NewSessionDialog (); void set_session_name(const Glib::ustring& name); + void set_session_folder(const Glib::ustring& folder); std::string session_name() const; std::string session_folder() const; @@ -82,14 +85,17 @@ public: bool connect_outs_to_master() const; bool connect_outs_to_physical() const ; - bool on_new_session_page () const; - + bool on_newable_page() const; + int get_current_page () const; void set_current_page (int); void reset_recent(); // reset everything to default values. void reset(); + EngineControl engine_control; + void set_have_engine (bool yn); + protected: void reset_name(); @@ -173,6 +179,8 @@ protected: RecentSessionModelColumns recent_columns; Glib::RefPtr<Gtk::TreeStore> recent_model; + bool in_destructor; + void recent_session_selection_changed (); void nsd_redisplay_recent_sessions(); void nsd_recent_session_row_activated (const Gtk::TreePath& path, Gtk::TreeViewColumn* col); @@ -194,6 +202,7 @@ protected: void master_bus_button_clicked (); void monitor_bus_button_clicked (); + bool have_engine; }; #endif // NEW_SESSION_DIALOG_H diff --git a/gtk2_ardour/option_editor.cc b/gtk2_ardour/option_editor.cc index 513e46448e..e49b410668 100644 --- a/gtk2_ardour/option_editor.cc +++ b/gtk2_ardour/option_editor.cc @@ -27,7 +27,6 @@ #include <ardour/crossfade.h> #include <midi++/manager.h> #include <midi++/factory.h> -#include <midi++/port_request.h> #include <gtkmm2ext/stop_signal.h> #include <gtkmm2ext/utils.h> #include <gtkmm2ext/window_title.h> @@ -43,6 +42,7 @@ #include "editing.h" #include "option_editor.h" #include "midi_port_dialog.h" +#include "gui_thread.h" #include "i18n.h" @@ -56,7 +56,7 @@ using namespace std; static vector<string> positional_sync_strings; OptionEditor::OptionEditor (ARDOUR_UI& uip, PublicEditor& ed, Mixer_UI& mixui) - : Dialog ("options editor"), + : ArdourDialog ("options editor", false), ui (uip), editor (ed), mixer (mixui), @@ -64,24 +64,33 @@ OptionEditor::OptionEditor (ARDOUR_UI& uip, PublicEditor& ed, Mixer_UI& mixui) /* Paths */ path_table (11, 2), - /* Fades */ + /* misc */ short_xfade_adjustment (0, 1.0, 500.0, 5.0, 100.0), short_xfade_slider (short_xfade_adjustment), destructo_xfade_adjustment (1.0, 1.0, 500.0, 1.0, 100.0), destructo_xfade_slider (destructo_xfade_adjustment), + history_depth (20, -1, 100, 1.0, 10.0), + saved_history_depth (20, 0, 100, 1.0, 10.0), + history_depth_spinner (history_depth), + saved_history_depth_spinner (saved_history_depth), + limit_history_button (_("Limit undo history")), + save_history_button (_("Save undo history")), /* Sync */ smpte_offset_clock (X_("smpteoffset"), false, X_("SMPTEOffsetClock"), true, true), smpte_offset_negative_button (_("SMPTE offset is negative")), + synced_timecode_button (_("Timecode source is sample-clock synced")), /* MIDI */ + midi_port_table (4, 11), mmc_receive_device_id_adjustment (0.0, 0.0, (double) 0x7f, 1.0, 16.0), mmc_receive_device_id_spinner (mmc_receive_device_id_adjustment), mmc_send_device_id_adjustment (0.0, 0.0, (double) 0x7f, 1.0, 16.0), mmc_send_device_id_spinner (mmc_send_device_id_adjustment), + add_midi_port_button (_("Add new MIDI port")), /* Click */ @@ -105,13 +114,13 @@ OptionEditor::OptionEditor (ARDOUR_UI& uip, PublicEditor& ed, Mixer_UI& mixui) session = 0; WindowTitle title(Glib::get_application_name()); - title += _("Options Editor"); + title += _("Preferences"); set_title(title.get_string()); set_default_size (300, 300); - set_wmclass (X_("ardour_option_editor"), "Ardour"); + set_wmclass (X_("ardour_preferences"), "Ardour"); - set_name ("OptionsWindow"); + set_name ("Preferences"); add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK); VBox *vbox = get_vbox(); @@ -128,7 +137,7 @@ OptionEditor::OptionEditor (ARDOUR_UI& uip, PublicEditor& ed, Mixer_UI& mixui) setup_sync_options(); setup_path_options(); - setup_fade_options (); + setup_misc_options (); setup_keyboard_options (); setup_auditioner_editor (); @@ -137,15 +146,16 @@ OptionEditor::OptionEditor (ARDOUR_UI& uip, PublicEditor& ed, Mixer_UI& mixui) notebook.pages().push_back (TabElem (keyboard_mouse_table, _("Kbd/Mouse"))); notebook.pages().push_back (TabElem (click_packer, _("Click"))); notebook.pages().push_back (TabElem (audition_packer, _("Audition"))); - notebook.pages().push_back (TabElem (fade_packer, _("Layers & Fades"))); + notebook.pages().push_back (TabElem (misc_packer, _("Misc"))); - if (!MIDI::Manager::instance()->get_midi_ports().empty()) { - setup_midi_options (); - notebook.pages().push_back (TabElem (midi_packer, _("MIDI"))); - } + setup_midi_options (); + notebook.pages().push_back (TabElem (midi_packer, _("MIDI"))); set_session (0); show_all_children(); + + Config->map_parameters (mem_fun (*this, &OptionEditor::parameter_changed)); + Config->ParameterChanged.connect (mem_fun (*this, &OptionEditor::parameter_changed)); } void @@ -182,27 +192,7 @@ OptionEditor::set_session (Session *s) smpte_offset_negative_button.set_active (session->smpte_offset_negative()); - /* set up port assignments */ - - std::map<MIDI::Port*,vector<RadioButton*> >::iterator res; - - if (session->mtc_port()) { - if ((res = port_toggle_buttons.find (session->mtc_port())) != port_toggle_buttons.end()) { - (*res).second[MtcIndex]->set_active (true); - } - } - - if (session->mmc_port ()) { - if ((res = port_toggle_buttons.find (session->mmc_port())) != port_toggle_buttons.end()) { - (*res).second[MmcIndex]->set_active (true); - } - } - - if (session->midi_port()) { - if ((res = port_toggle_buttons.find (session->midi_port())) != port_toggle_buttons.end()) { - (*res).second[MidiIndex]->set_active (true); - } - } + redisplay_midi_ports (); setup_click_editor (); connect_audition_editor (); @@ -260,7 +250,7 @@ OptionEditor::add_session_paths () } void -OptionEditor::setup_fade_options () +OptionEditor::setup_misc_options () { Gtk::HBox* hbox; @@ -272,7 +262,7 @@ OptionEditor::setup_fade_options () hbox->set_spacing (10); hbox->pack_start (*label, false, false); hbox->pack_start (short_xfade_slider, true, true); - fade_packer.pack_start (*hbox, false, false); + misc_packer.pack_start (*hbox, false, false); short_xfade_adjustment.signal_value_changed().connect (mem_fun(*this, &OptionEditor::short_xfade_adjustment_changed)); @@ -284,16 +274,94 @@ OptionEditor::setup_fade_options () hbox->set_spacing (10); hbox->pack_start (*label, false, false); hbox->pack_start (destructo_xfade_slider, true, true); - fade_packer.pack_start (*hbox, false, false); + misc_packer.pack_start (*hbox, false, false); + destructo_xfade_adjustment.signal_value_changed().connect (mem_fun(*this, &OptionEditor::destructo_xfade_adjustment_changed)); + hbox = manage (new HBox); + hbox->set_border_width (5); + hbox->set_spacing (10); + hbox->pack_start (limit_history_button, false, false); + misc_packer.pack_start (*hbox, false, false); + + label = manage (new Label (_("History depth (commands)"))); + label->set_name ("OptionsLabel"); + + hbox = manage (new HBox); + hbox->set_border_width (5); + hbox->set_spacing (10); + hbox->pack_start (*label, false, false); + hbox->pack_start (history_depth_spinner, false, false); + misc_packer.pack_start (*hbox, false, false); + + history_depth.signal_value_changed().connect (mem_fun (*this, &OptionEditor::history_depth_changed)); + saved_history_depth.signal_value_changed().connect (mem_fun (*this, &OptionEditor::saved_history_depth_changed)); + save_history_button.signal_toggled().connect (mem_fun (*this, &OptionEditor::save_history_toggled)); + limit_history_button.signal_toggled().connect (mem_fun (*this, &OptionEditor::limit_history_toggled)); + + hbox = manage (new HBox); + hbox->set_border_width (5); + hbox->set_spacing (10); + hbox->pack_start (save_history_button, false, false); + misc_packer.pack_start (*hbox, false, false); + + label = manage (new Label (_("Saved history depth (commands)"))); + label->set_name ("OptionsLabel"); + + hbox = manage (new HBox); + hbox->set_border_width (5); + hbox->set_spacing (10); + hbox->pack_start (*label, false, false); + hbox->pack_start (saved_history_depth_spinner, false, false); + misc_packer.pack_start (*hbox, false, false); + short_xfade_slider.set_update_policy (UPDATE_DISCONTINUOUS); destructo_xfade_slider.set_update_policy (UPDATE_DISCONTINUOUS); destructo_xfade_adjustment.set_value (Config->get_destructive_xfade_msecs()); - fade_packer.show_all (); + misc_packer.show_all (); +} + +void +OptionEditor::limit_history_toggled () +{ + bool x = limit_history_button.get_active(); + + if (!x) { + Config->set_history_depth (0); + history_depth_spinner.set_sensitive (false); + } else { + if (Config->get_history_depth() == 0) { + /* get back to a sane default */ + Config->set_history_depth (20); + } + history_depth_spinner.set_sensitive (true); + } +} + +void +OptionEditor::save_history_toggled () +{ + bool x = save_history_button.get_active(); + + if (x != Config->get_save_history()) { + Config->set_save_history (x); + saved_history_depth_spinner.set_sensitive (x); + } +} + +void +OptionEditor::history_depth_changed() +{ + Config->set_history_depth ((int32_t) floor (history_depth.get_value())); +} + +void +OptionEditor::saved_history_depth_changed() +{ + Config->set_saved_history_depth ((int32_t) floor (saved_history_depth.get_value())); } void @@ -347,8 +415,10 @@ OptionEditor::setup_sync_options () hbox->pack_start (smpte_offset_negative_button, false, false); sync_packer.pack_start (*hbox, false, false); + sync_packer.pack_start (synced_timecode_button, false, false); smpte_offset_negative_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::smpte_offset_negative_clicked)); + synced_timecode_button.signal_toggled().connect (mem_fun(*this, &OptionEditor::synced_timecode_toggled)); } void @@ -360,6 +430,17 @@ OptionEditor::smpte_offset_negative_clicked () } void +OptionEditor::synced_timecode_toggled () +{ + bool x; + + if ((x = synced_timecode_button.get_active()) != Config->get_timecode_source_is_synced()) { + Config->set_timecode_source_is_synced (x); + Config->save_state(); + } +} + +void OptionEditor::smpte_offset_chosen() { if (session) { @@ -368,6 +449,7 @@ OptionEditor::smpte_offset_chosen() } } + void OptionEditor::setup_midi_options () { @@ -379,6 +461,9 @@ OptionEditor::setup_midi_options () redisplay_midi_ports (); + mmc_receive_device_id_adjustment.set_value (Config->get_mmc_receive_device_id()); + mmc_send_device_id_adjustment.set_value (Config->get_mmc_send_device_id()); + mmc_receive_device_id_adjustment.signal_value_changed().connect (mem_fun (*this, &OptionEditor::mmc_receive_device_id_adjusted)); mmc_send_device_id_adjustment.signal_value_changed().connect (mem_fun (*this, &OptionEditor::mmc_send_device_id_adjusted)); @@ -396,7 +481,9 @@ OptionEditor::setup_midi_options () label = (manage (new Label (_("Inbound MMC Device ID")))); hbox->pack_start (mmc_receive_device_id_spinner, false, false); hbox->pack_start (*label, false, false); - midi_packer.pack_start (*hbox, false, false); + midi_packer.pack_start (*hbox, false, false); + + mmc_receive_device_id_spinner.set_value(Config->get_mmc_receive_device_id ()); hbox = manage (new HBox); hbox->set_border_width (6); @@ -406,6 +493,8 @@ OptionEditor::setup_midi_options () hbox->pack_start (*label, false, false); midi_packer.pack_start (*hbox, false, false); + mmc_send_device_id_spinner.set_value(Config->get_mmc_send_device_id ()); + add_midi_port_button.signal_clicked().connect (mem_fun (*this, &OptionEditor::add_midi_port)); } @@ -624,12 +713,15 @@ OptionEditor::add_midi_port () smod = "duplex"; } - MIDI::PortRequest req (X_("ardour"), - dialog.port_name.get_text(), - smod, - MIDI::PortFactory::default_port_type()); - if (MIDI::Manager::instance()->add_port (req) != 0) { + XMLNode node (X_("MIDI-port")); + + node.add_property ("tag", dialog.port_name.get_text()); + node.add_property ("device", X_("ardour")); // XXX this can't be right for all types + node.add_property ("type", MIDI::PortFactory::default_port_type()); + node.add_property ("mode", smod); + + if (MIDI::Manager::instance()->add_port (node) != 0) { redisplay_midi_ports (); } } @@ -696,23 +788,27 @@ OptionEditor::port_online_toggled (MIDI::Port* port, ToggleButton* tb) { bool wanted = tb->get_active(); - if (wanted != port->input()->offline()) { - port->input()->set_offline (wanted); - } + if (port->input()) { + if (wanted != port->input()->offline()) { + port->input()->set_offline (wanted); + } + } } void OptionEditor::map_port_online (MIDI::Port* port, ToggleButton* tb) { bool bstate = tb->get_active (); - - if (bstate != port->input()->offline()) { - if (port->input()->offline()) { - tb->set_label (_("offline")); - tb->set_active (false); - } else { - tb->set_label (_("online")); - tb->set_active (true); + + if (port->input()) { + if (bstate != port->input()->offline()) { + if (port->input()->offline()) { + tb->set_label (_("offline")); + tb->set_active (false); + } else { + tb->set_label (_("online")); + tb->set_active (true); + } } } } @@ -721,20 +817,14 @@ void OptionEditor::mmc_receive_device_id_adjusted () { uint8_t id = (uint8_t) mmc_receive_device_id_spinner.get_value(); - - if (id != Config->get_mmc_receive_device_id()) { - Config->set_mmc_receive_device_id (id); - } + Config->set_mmc_receive_device_id (id); } void OptionEditor::mmc_send_device_id_adjusted () { uint8_t id = (uint8_t) mmc_send_device_id_spinner.get_value(); - - if (id != Config->get_mmc_send_device_id()) { - Config->set_mmc_send_device_id (id); - } + Config->set_mmc_send_device_id (id); } void @@ -742,8 +832,10 @@ OptionEditor::port_trace_in_toggled (MIDI::Port* port, ToggleButton* tb) { bool trace = tb->get_active(); - if (port->input()->tracing() != trace) { - port->input()->trace (trace, &cerr, string (port->name()) + string (" input: ")); + if (port->input()) { + if (port->input()->tracing() != trace) { + port->input()->trace (trace, &cerr, string (port->name()) + string (" input: ")); + } } } @@ -752,8 +844,10 @@ OptionEditor::port_trace_out_toggled (MIDI::Port* port, ToggleButton* tb) { bool trace = tb->get_active(); - if (port->output()->tracing() != trace) { - port->output()->trace (trace, &cerr, string (port->name()) + string (" output: ")); + if (port->output()) { + if (port->output()->tracing() != trace) { + port->output()->trace (trace, &cerr, string (port->name()) + string (" output: ")); + } } } @@ -784,10 +878,13 @@ OptionEditor::raid_path_changed () void OptionEditor::click_browse_clicked () { - SoundFileChooser sfdb (_("Choose Click"), session); + SoundFileChooser sfdb (*this, _("Choose Click"), session); - int result = sfdb.run (); + sfdb.show_all (); + sfdb.present (); + int result = sfdb.run (); + if (result == Gtk::RESPONSE_OK) { click_chosen(sfdb.get_filename()); } @@ -803,7 +900,10 @@ OptionEditor::click_chosen (const string & path) void OptionEditor::click_emphasis_browse_clicked () { - SoundFileChooser sfdb (_("Choose Click Emphasis"), session); + SoundFileChooser sfdb (*this, _("Choose Click Emphasis"), session); + + sfdb.show_all (); + sfdb.present (); int result = sfdb.run (); @@ -1167,3 +1267,31 @@ OptionEditor::fixup_combo_size (Gtk::ComboBoxText& combo, vector<string>& string set_size_request_to_display_given_text (combo, maxstring.c_str(), 10 + FUDGE, 10); } +void +OptionEditor::parameter_changed (const char* parameter_name) +{ + ENSURE_GUI_THREAD (bind (mem_fun (*this, &OptionEditor::parameter_changed), parameter_name)); + +#define PARAM_IS(x) (!strcmp (parameter_name, (x))) + + if (PARAM_IS ("timecode-source-is-synced")) { + synced_timecode_button.set_active (Config->get_timecode_source_is_synced()); + } else if (PARAM_IS ("history-depth")) { + int32_t depth = Config->get_history_depth(); + + history_depth.set_value (depth); + history_depth_spinner.set_sensitive (depth != 0); + limit_history_button.set_active (depth != 0); + + } else if (PARAM_IS ("saved-history-depth")) { + + saved_history_depth.set_value (Config->get_saved_history_depth()); + + } else if (PARAM_IS ("save-history")) { + + bool x = Config->get_save_history(); + + save_history_button.set_active (x); + saved_history_depth_spinner.set_sensitive (x); + } +} diff --git a/gtk2_ardour/option_editor.h b/gtk2_ardour/option_editor.h index a234f1d752..82bb4db79b 100644 --- a/gtk2_ardour/option_editor.h +++ b/gtk2_ardour/option_editor.h @@ -45,7 +45,7 @@ class IOSelector; class GainMeter; class PannerUI; -class OptionEditor : public Gtk::Dialog +class OptionEditor : public ArdourDialog { public: OptionEditor (ARDOUR_UI&, PublicEditor&, Mixer_UI&); @@ -66,6 +66,7 @@ class OptionEditor : public Gtk::Dialog gint wm_close (GdkEventAny *); bool focus_out_event_handler (GdkEventFocus*, void (OptionEditor::*pmf)()); + void parameter_changed (const char* name); /* paths */ @@ -77,18 +78,32 @@ class OptionEditor : public Gtk::Dialog void remove_session_paths (); void raid_path_changed (); - /* fades */ + /* misc */ + + Gtk::VBox misc_packer; - Gtk::VBox fade_packer; Gtk::Adjustment short_xfade_adjustment; Gtk::HScale short_xfade_slider; Gtk::Adjustment destructo_xfade_adjustment; Gtk::HScale destructo_xfade_slider; - void setup_fade_options(); + void setup_misc_options(); + void short_xfade_adjustment_changed (); void destructo_xfade_adjustment_changed (); + Gtk::Adjustment history_depth; + Gtk::Adjustment saved_history_depth; + Gtk::SpinButton history_depth_spinner; + Gtk::SpinButton saved_history_depth_spinner; + Gtk::CheckButton limit_history_button; + Gtk::CheckButton save_history_button; + + void history_depth_changed(); + void saved_history_depth_changed(); + void save_history_toggled (); + void limit_history_toggled (); + /* Sync */ Gtk::VBox sync_packer; @@ -96,11 +111,13 @@ class OptionEditor : public Gtk::Dialog Gtk::ComboBoxText slave_type_combo; AudioClock smpte_offset_clock; Gtk::CheckButton smpte_offset_negative_button; + Gtk::CheckButton synced_timecode_button; void setup_sync_options (); void smpte_offset_chosen (); void smpte_offset_negative_clicked (); + void synced_timecode_toggled (); /* MIDI */ diff --git a/gtk2_ardour/opts.cc b/gtk2_ardour/opts.cc index 608f684fc9..8da0fb9ca1 100644 --- a/gtk2_ardour/opts.cc +++ b/gtk2_ardour/opts.cc @@ -27,18 +27,19 @@ using namespace std; -string GTK_ARDOUR::session_name = ""; -string GTK_ARDOUR::jack_client_name = "ardour"; -bool GTK_ARDOUR::show_key_actions = false; -bool GTK_ARDOUR::no_splash = true; -bool GTK_ARDOUR::just_version = false; -bool GTK_ARDOUR::use_vst = true; -bool GTK_ARDOUR::new_session = false; -char* GTK_ARDOUR::curvetest_file = 0; -bool GTK_ARDOUR::try_hw_optimization = true; -string GTK_ARDOUR::keybindings_path = ""; /* empty means use builtin default */ - -using namespace GTK_ARDOUR; +string ARDOUR_COMMAND_LINE::session_name = ""; +string ARDOUR_COMMAND_LINE::jack_client_name = "ardour"; +bool ARDOUR_COMMAND_LINE::show_key_actions = false; +bool ARDOUR_COMMAND_LINE::no_splash = true; +bool ARDOUR_COMMAND_LINE::just_version = false; +bool ARDOUR_COMMAND_LINE::use_vst = true; +bool ARDOUR_COMMAND_LINE::new_session = false; +char* ARDOUR_COMMAND_LINE::curvetest_file = 0; +bool ARDOUR_COMMAND_LINE::try_hw_optimization = true; +string ARDOUR_COMMAND_LINE::keybindings_path = ""; /* empty means use builtin default */ +Glib::ustring ARDOUR_COMMAND_LINE::menus_file = "ardour.menus"; + +using namespace ARDOUR_COMMAND_LINE; int print_help (const char *execname) @@ -49,9 +50,10 @@ print_help (const char *execname) << _(" -b, --bindings Print all possible keyboard binding names\n") << _(" -n, --show-splash Show splash screen\n") << _(" -c, --name name Use a specific jack client name, default is ardour\n") + << _(" -m, --menus file Use \"file\" for Ardour menus\n") << _(" -N, --new session-name Create a new session from the command line\n") << _(" -O, --no-hw-optimizations Disable h/w specific optimizations\n") - << _(" -S, --sync Draw the gui synchronously \n") + << _(" -S, --sync Draw the gui synchronously \n") #ifdef VST_SUPPORT << _(" -V, --novst Do not use VST support\n") #endif @@ -64,12 +66,16 @@ print_help (const char *execname) } int -GTK_ARDOUR::parse_opts (int argc, char *argv[]) +ARDOUR_COMMAND_LINE::parse_opts (int argc, char *argv[]) { - const char *optstring = "U:hSbvVnOc:C:N:k:"; + const char *optstring = "U:hSbvVnOc:C:m:N:k:"; const char *execname = strrchr (argv[0], '/'); + if (getenv ("ARDOUR_SAE")) { + menus_file = "ardour-sae.menus"; + } + if (execname == 0) { execname = argv[0]; } else { @@ -81,11 +87,12 @@ GTK_ARDOUR::parse_opts (int argc, char *argv[]) { "help", 0, 0, 'h' }, { "bindings", 0, 0, 'b' }, { "show-splash", 0, 0, 'n' }, + { "menus", 1, 0, 'm' }, { "name", 1, 0, 'c' }, { "novst", 0, 0, 'V' }, { "new", 1, 0, 'N' }, { "no-hw-optimizations", 0, 0, 'O' }, - { "sync", 0, 0, 'O' }, + { "sync", 0, 0, 'S' }, { "curvetest", 1, 0, 'C' }, { 0, 0, 0, 0 } }; @@ -116,6 +123,11 @@ GTK_ARDOUR::parse_opts (int argc, char *argv[]) show_key_actions = true; break; + + case 'm': + menus_file = optarg; + break; + case 'n': no_splash = false; break; @@ -133,6 +145,11 @@ GTK_ARDOUR::parse_opts (int argc, char *argv[]) try_hw_optimization = false; break; + + case 'p': + //undocumented OS X finder -psn_XXXXX argument + break; + case 'V': #ifdef VST_SUPPORT use_vst = false; diff --git a/gtk2_ardour/opts.h b/gtk2_ardour/opts.h index fb780fc8aa..c1b3f062d4 100644 --- a/gtk2_ardour/opts.h +++ b/gtk2_ardour/opts.h @@ -21,10 +21,11 @@ #define __ardour_opts_h__ #include <string> +#include <glibmm/ustring.h> using std::string; -namespace GTK_ARDOUR { +namespace ARDOUR_COMMAND_LINE { extern string session_name; extern bool show_key_actions; @@ -37,6 +38,7 @@ extern char* curvetest_file; extern bool try_hw_optimization; extern bool use_gtk_theme; extern string keybindings_path; +extern Glib::ustring menus_file; extern int32_t parse_opts (int argc, char *argv[]); diff --git a/gtk2_ardour/processor_box.cc b/gtk2_ardour/processor_box.cc index d093fda99f..12e30e59c0 100644 --- a/gtk2_ardour/processor_box.cc +++ b/gtk2_ardour/processor_box.cc @@ -338,7 +338,7 @@ ProcessorBox::processor_button_release_event (GdkEventButton *ev) show_processor_menu(ev->time); ret = true; - } else if (processor && (ev->button == 2) && (ev->state == Gdk::BUTTON2_MASK)) { + } else if (processor && (ev->button == 2) && (Keyboard::no_modifier_keys_pressed (ev) && ((ev->state & Gdk::BUTTON2_MASK) == Gdk::BUTTON2_MASK))) { processor->set_active (!processor->active()); ret = true; @@ -394,13 +394,13 @@ ProcessorBox::processor_plugin_chosen (boost::shared_ptr<Plugin> plugin) boost::shared_ptr<Processor> processor (new PluginInsert (_session, plugin, _placement)); - processor->ActiveChanged.connect (bind (mem_fun (*this, &ProcessorBox::show_processor_active), boost::weak_ptr<Processor>(processor))); - Route::ProcessorStreams err; if (_route->add_processor (processor, &err)) { weird_plugin_dialog (*plugin, err, _route); // XXX SHAREDPTR delete plugin here .. do we even need to care? + } else { + processor->ActiveChanged.connect (bind (mem_fun (*this, &ProcessorBox::show_processor_active), boost::weak_ptr<Processor>(processor))); } } } @@ -482,36 +482,39 @@ ProcessorBox::choose_send () boost::shared_ptr<Send> send (new Send (_session, _placement)); //send->set_default_type(_route->default_type()); - /* XXX need redirect lock on route */ + ChanCount outs; + + /* make an educated guess at the initial number of outputs for the send */ + + if (_session.master_out()) { + outs = _session.master_out()->n_outputs(); + } else { + outs = _route->n_outputs(); + } + + send->io()->ensure_io (ChanCount::ZERO, outs, false, this); - // This will be set properly in route->add_processor - send->configure_io (_route->max_processor_outs(), _route->max_processor_outs()); + SendUIWindow* gui = new SendUIWindow (send, _session); - IOSelectorWindow *ios = new IOSelectorWindow (_session, send->io(), false, true); + /* let the user adjust the output setup (number and connections) before passing + it along to the Route + */ - ios->show_all (); + gui->show_all (); + gui->present (); - ios->selector().Finished.connect (bind (mem_fun(*this, &ProcessorBox::send_io_finished), send, ios)); + /* pass shared_ptr, it will go out of scope when the GUI is deleted */ + /* also, connect it *before* existing handlers so that its definitely executed */ + + gui->signal_delete_event().connect (bind (mem_fun(*this, &ProcessorBox::send_io_finished), send, gui), false); } -void -ProcessorBox::send_io_finished (IOSelector::Result r, boost::shared_ptr<Send> send, IOSelectorWindow* ios) +bool +ProcessorBox::send_io_finished (GdkEventAny* ev, boost::shared_ptr<Send> send, SendUIWindow* sui) { - if (!send) { - return; - } - - switch (r) { - case IOSelector::Cancelled: - // send will go away when all shared_ptrs to it vanish - break; - - case IOSelector::Accepted: - _route->add_processor (send); - break; - } - - delete_when_idle (ios); + _route->add_processor (send); + delete sui; + return false; } void diff --git a/gtk2_ardour/processor_box.h b/gtk2_ardour/processor_box.h index 1ed0bcb39e..b6bb3ae3d7 100644 --- a/gtk2_ardour/processor_box.h +++ b/gtk2_ardour/processor_box.h @@ -50,6 +50,7 @@ class MotionController; class PluginSelector; class PluginUIWindow; class RouteRedirectSelection; +class SendUIWindow; namespace ARDOUR { class Bundle; @@ -142,7 +143,7 @@ class ProcessorBox : public Gtk::HBox void show_processor_menu (gint arg); void choose_send (); - void send_io_finished (IOSelector::Result, boost::shared_ptr<ARDOUR::Send>, IOSelectorWindow*); + bool send_io_finished (GdkEventAny*,boost::shared_ptr<ARDOUR::Send>, SendUIWindow*); void choose_processor (); void choose_plugin (); void processor_plugin_chosen (boost::shared_ptr<ARDOUR::Plugin>); diff --git a/gtk2_ardour/send_ui.cc b/gtk2_ardour/send_ui.cc index 7dda653894..c91068f97c 100644 --- a/gtk2_ardour/send_ui.cc +++ b/gtk2_ardour/send_ui.cc @@ -54,7 +54,7 @@ SendUI::SendUI (boost::shared_ptr<Send> s, Session& se) _send->set_metering (true); - _send->io()->output_changed.connect (mem_fun (*this, &SendUI::ins_changed)); + _send->io()->input_changed.connect (mem_fun (*this, &SendUI::ins_changed)); _send->io()->output_changed.connect (mem_fun (*this, &SendUI::outs_changed)); panners.set_width (Wide); diff --git a/gtk2_ardour/sfdb_ui.cc b/gtk2_ardour/sfdb_ui.cc index 5415cd70cb..67bedb6182 100644 --- a/gtk2_ardour/sfdb_ui.cc +++ b/gtk2_ardour/sfdb_ui.cc @@ -21,90 +21,181 @@ #include <cerrno> #include <sstream> +#include <unistd.h> #include <sys/stat.h> +#include <sys/param.h> #include <gtkmm/box.h> #include <gtkmm/stock.h> +#include <glibmm/fileutils.h> #include <pbd/convert.h> #include <pbd/tokenizer.h> +#include <pbd/enumwriter.h> #include <gtkmm2ext/utils.h> #include <ardour/audio_library.h> +#include <ardour/auditioner.h> #include <ardour/audioregion.h> #include <ardour/audiofilesource.h> #include <ardour/region_factory.h> #include <ardour/source_factory.h> +#include <ardour/session.h> +#include <ardour/session_directory.h> +#include <ardour/profile.h> #include "ardour_ui.h" #include "editing.h" #include "gui_thread.h" #include "prompter.h" #include "sfdb_ui.h" +#include "editing.h" #include "utils.h" +#include "gain_meter.h" #include "i18n.h" using namespace ARDOUR; using namespace PBD; using namespace std; +using namespace Gtk; +using namespace Gtkmm2ext; +using namespace Editing; + +using Glib::ustring; -Glib::ustring SoundFileBrowser::persistent_folder; +ustring SoundFileBrowser::persistent_folder; -SoundFileBox::SoundFileBox () - : - _session(0), - current_pid(0), - main_box (false, 3), - bottom_box (true, 4), - play_btn(_("Play")), - stop_btn(_("Stop")), - apply_btn(_("Apply")) +static ImportMode +string2importmode (string str) { - set_name (X_("SoundFileBox")); + if (str == "as new tracks") { + return ImportAsTrack; + } else if (str == "to selected tracks") { + return ImportToTrack; + } else if (str == "to region list") { + return ImportAsRegion; + } else if (str == "as new tape tracks") { + return ImportAsTapeTrack; + } + + warning << string_compose (_("programming error: unknown import mode string %1"), str) << endmsg; - set_size_request (250, 500); + return ImportAsTrack; +} + +static string +importmode2string (ImportMode mode) +{ + switch (mode) { + case ImportAsTrack: + return _("as new tracks"); + case ImportToTrack: + return _("to selected tracks"); + case ImportAsRegion: + return _("to region list"); + case ImportAsTapeTrack: + return _("as new tape tracks"); + } + /*NOTREACHED*/ + return _("as new tracks"); +} + +SoundFileBox::SoundFileBox (bool persistent) + : _session(0), + table (6, 2), + length_clock ("sfboxLengthClock", !persistent, "EditCursorClock", false, true, false), + timecode_clock ("sfboxTimecodeClock", !persistent, "EditCursorClock", false, false, false), + main_box (false, 6), + autoplay_btn (_("Auto-play")) - border_frame.set_label (_("Soundfile Info")); +{ + HBox* hbox; + VBox* vbox; + + set_name (X_("SoundFileBox")); + set_size_request (300, -1); + + preview_label.set_markup (_("<b>Soundfile Info</b>")); + + border_frame.set_label_widget (preview_label); border_frame.add (main_box); - Gtk::Label* tag_label = manage(new Gtk::Label(_("comma seperated tags"))); + pack_start (border_frame, true, true); + set_border_width (6); - pack_start (border_frame); - set_border_width (4); + main_box.set_border_width (6); + main_box.set_spacing (12); - main_box.set_border_width (4); + length.set_text (_("Length:")); + timecode.set_text (_("Timestamp:")); + format.set_text (_("Format:")); + channels.set_text (_("Channels:")); + samplerate.set_text (_("Sample rate:")); - main_box.pack_start(length, false, false); - main_box.pack_start(format, false, false); - main_box.pack_start(channels, false, false); - main_box.pack_start(samplerate, false, false); - main_box.pack_start(timecode, false, false); - main_box.pack_start(*tag_label, false, false); - main_box.pack_start(tags_entry, false, false); - main_box.pack_start(apply_btn, false, false); - main_box.pack_start(bottom_box, false, false); + table.set_col_spacings (6); + table.set_homogeneous (false); + table.set_row_spacings (6); + + table.attach (channels, 0, 1, 0, 1, FILL|EXPAND, (AttachOptions) 0); + table.attach (samplerate, 0, 1, 1, 2, FILL|EXPAND, (AttachOptions) 0); + table.attach (format, 0, 1, 2, 4, FILL|EXPAND, (AttachOptions) 0); + table.attach (length, 0, 1, 4, 5, FILL|EXPAND, (AttachOptions) 0); + table.attach (timecode, 0, 1, 5, 6, FILL|EXPAND, (AttachOptions) 0); + + table.attach (channels_value, 1, 2, 0, 1, FILL, (AttachOptions) 0); + table.attach (samplerate_value, 1, 2, 1, 2, FILL, (AttachOptions) 0); + table.attach (format_text, 1, 2, 2, 4, FILL, AttachOptions (0)); + table.attach (length_clock, 1, 2, 4, 5, FILL, (AttachOptions) 0); + table.attach (timecode_clock, 1, 2, 5, 6, FILL, (AttachOptions) 0); + + length_clock.set_mode (ARDOUR_UI::instance()->secondary_clock.mode()); + timecode_clock.set_mode (AudioClock::SMPTE); + + hbox = manage (new HBox); + hbox->pack_start (table, false, false); + main_box.pack_start (*hbox, false, false); - bottom_box.set_homogeneous(true); - bottom_box.pack_start(play_btn); - bottom_box.pack_start(stop_btn); + tags_entry.set_editable (true); + tags_entry.signal_focus_out_event().connect (mem_fun (*this, &SoundFileBox::tags_entry_left)); + hbox = manage (new HBox); + hbox->pack_start (tags_entry, true, true); - play_btn.signal_clicked().connect (mem_fun (*this, &SoundFileBox::play_btn_clicked)); - stop_btn.signal_clicked().connect (mem_fun (*this, &SoundFileBox::stop_btn_clicked)); - apply_btn.signal_clicked().connect (mem_fun (*this, &SoundFileBox::apply_btn_clicked)); - tags_entry.signal_activate().connect (mem_fun (*this, &SoundFileBox::apply_btn_clicked)); + vbox = manage (new VBox); - length.set_alignment (0.0f, 0.0f); - format.set_alignment (0.0f, 0.0f); - channels.set_alignment (0.0f, 0.0f); - samplerate.set_alignment (0.0f, 0.0f); - timecode.set_alignment (0.0f, 0.0f); + Label* label = manage (new Label (_("Tags:"))); + label->set_alignment (0.0f, 0.5f); + vbox->set_spacing (6); + vbox->pack_start(*label, false, false); + vbox->pack_start(*hbox, true, true); - stop_btn.set_no_show_all (true); - stop_btn.hide(); + main_box.pack_start(*vbox, true, true); + main_box.pack_start(bottom_box, false, false); + + play_btn.set_image (*(manage (new Image (Stock::MEDIA_PLAY, ICON_SIZE_BUTTON)))); + play_btn.set_label (_("Play (double click)")); + + stop_btn.set_image (*(manage (new Image (Stock::MEDIA_STOP, ICON_SIZE_BUTTON)))); + stop_btn.set_label (_("Stop")); - show_all(); + bottom_box.set_homogeneous (false); + bottom_box.set_spacing (6); + bottom_box.pack_start(play_btn, true, true); + bottom_box.pack_start(stop_btn, true, true); + bottom_box.pack_start(autoplay_btn, false, false); + + play_btn.signal_clicked().connect (mem_fun (*this, &SoundFileBox::audition)); + stop_btn.signal_clicked().connect (mem_fun (*this, &SoundFileBox::stop_audition)); + + length.set_alignment (0.0f, 0.5f); + format.set_alignment (0.0f, 0.5f); + channels.set_alignment (0.0f, 0.5f); + samplerate.set_alignment (0.0f, 0.5f); + timecode.set_alignment (0.0f, 0.5f); + + channels_value.set_alignment (0.0f, 0.5f); + samplerate_value.set_alignment (0.0f, 0.5f); } void @@ -113,41 +204,66 @@ SoundFileBox::set_session(Session* s) _session = s; if (!_session) { - play_btn.set_sensitive(false); - } else { - _session->AuditionActive.connect(mem_fun (*this, &SoundFileBox::audition_status_changed)); - } + play_btn.set_sensitive (false); + stop_btn.set_sensitive (false); + } + + + length_clock.set_session (s); + timecode_clock.set_session (s); } bool -SoundFileBox::setup_labels (string filename) +SoundFileBox::setup_labels (const ustring& filename) { + if (!path.empty()) { + // save existing tags + tags_changed (); + } + path = filename; string error_msg; if(!AudioFileSource::get_soundfile_info (filename, sf_info, error_msg)) { - length.set_text (_("Length: n/a")); - format.set_text (_("Format: n/a")); - channels.set_text (_("Channels: n/a")); - samplerate.set_text (_("Samplerate: n/a")); - timecode.set_text (_("Timecode: n/a")); - tags_entry.set_text (""); + + preview_label.set_markup (_("<b>Soundfile Info</b>")); + format_text.set_text (_("n/a")); + channels_value.set_text (_("n/a")); + samplerate_value.set_text (_("n/a")); + tags_entry.get_buffer()->set_text (""); + + length_clock.set (0); + timecode_clock.set (0); tags_entry.set_sensitive (false); play_btn.set_sensitive (false); - apply_btn.set_sensitive (false); return false; } - length.set_text (string_compose(_("Length: %1"), length2string(sf_info.length, sf_info.samplerate))); - format.set_text (sf_info.format_name); - channels.set_text (string_compose(_("Channels: %1"), sf_info.channels)); - samplerate.set_text (string_compose(_("Samplerate: %1"), sf_info.samplerate)); - timecode.set_text (string_compose (_("Timecode: %1"), length2string(sf_info.timecode, sf_info.samplerate))); + preview_label.set_markup (string_compose ("<b>%1</b>", Glib::path_get_basename (filename))); + format_text.set_text (sf_info.format_name); + channels_value.set_text (to_string (sf_info.channels, std::dec)); + + if (_session && sf_info.samplerate != _session->frame_rate()) { + samplerate.set_markup (string_compose ("<b>%1</b>", _("Sample rate:"))); + samplerate_value.set_markup (string_compose (X_("<b>%1 Hz</b>"), sf_info.samplerate)); + samplerate_value.set_name ("NewSessionSR1Label"); + samplerate.set_name ("NewSessionSR1Label"); + } else { + samplerate.set_text (_("Sample rate:")); + samplerate_value.set_text (string_compose (X_("%1 Hz"), sf_info.samplerate)); + samplerate_value.set_name ("NewSessionSR2Label"); + samplerate.set_name ("NewSessionSR2Label"); + } + + length_clock.set (sf_info.length, true); + timecode_clock.set (sf_info.timecode, true); + + // this is a hack that is fixed in trunk, i think (august 26th, 2007) - vector<string> tags = Library->get_tags (filename); + vector<string> tags = Library->get_tags (string ("//") + filename); stringstream tag_string; for (vector<string>::iterator i = tags.begin(); i != tags.end(); ++i) { @@ -156,158 +272,164 @@ SoundFileBox::setup_labels (string filename) } tag_string << *i; } - tags_entry.set_text (tag_string.str()); + tags_entry.get_buffer()->set_text (tag_string.str()); tags_entry.set_sensitive (true); if (_session) { play_btn.set_sensitive (true); } - apply_btn.set_sensitive (true); return true; } bool -SoundFileBox::tags_entry_left (GdkEventFocus* event) -{ - apply_btn_clicked (); - - return true; +SoundFileBox::autoplay() const +{ + return autoplay_btn.get_active(); +} + +bool +SoundFileBox::audition_oneshot() +{ + audition (); + return false; } void -SoundFileBox::play_btn_clicked () +SoundFileBox::audition () { if (!_session) { return; } - + _session->cancel_audition(); - if (access(path.c_str(), R_OK)) { + if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) { warning << string_compose(_("Could not read file: %1 (%2)."), path, strerror(errno)) << endmsg; return; } - typedef std::map<string, boost::shared_ptr<AudioRegion> > RegionCache; - static RegionCache region_cache; - RegionCache::iterator the_region; + boost::shared_ptr<Region> r; + SourceList srclist; + boost::shared_ptr<AudioFileSource> afs; + bool old_sbp = AudioSource::get_build_peakfiles (); - if ((the_region = region_cache.find (path)) == region_cache.end()) { - SourceList srclist; - boost::shared_ptr<AudioFileSource> afs; - - for (int n = 0; n < sf_info.channels; ++n) { - try { - afs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createReadable (DataType::AUDIO, *_session, path, n, AudioFileSource::Flag (0))); - srclist.push_back(afs); - - } catch (failed_constructor& err) { - error << _("Could not access soundfile: ") << path << endmsg; - return; - } - } + /* don't even think of building peakfiles for these files */ + + AudioSource::set_build_peakfiles (false); - if (srclist.empty()) { + for (int n = 0; n < sf_info.channels; ++n) { + try { + afs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createReadable (DataType::AUDIO, *_session, path, + n, AudioFileSource::Flag (0), false)); + + srclist.push_back(afs); + + } catch (failed_constructor& err) { + error << _("Could not access soundfile: ") << path << endmsg; + AudioSource::set_build_peakfiles (old_sbp); return; } - - string rname; - - _session->region_name (rname, Glib::path_get_basename(srclist[0]->name()), false); - - pair<string,boost::shared_ptr<AudioRegion> > newpair; - pair<RegionCache::iterator,bool> res; - - newpair.first = path; - newpair.second = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (srclist, 0, srclist[0]->length(), rname, 0, Region::DefaultFlags, false)); - - res = region_cache.insert (newpair); - the_region = res.first; } - play_btn.hide(); - stop_btn.show(); - - boost::shared_ptr<Region> r = boost::static_pointer_cast<Region> (the_region->second); + AudioSource::set_build_peakfiles (old_sbp); + + if (srclist.empty()) { + return; + } + + afs = boost::dynamic_pointer_cast<AudioFileSource> (srclist[0]); + string rname = region_name_from_path (afs->path(), false); + r = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (srclist, 0, srclist[0]->length(), rname, 0, Region::DefaultFlags, false)); _session->audition_region(r); } void -SoundFileBox::stop_btn_clicked () +SoundFileBox::stop_audition () { if (_session) { _session->cancel_audition(); - play_btn.show(); - stop_btn.hide(); } } +bool +SoundFileBox::tags_entry_left (GdkEventFocus *ev) +{ + tags_changed (); + return false; +} + void -SoundFileBox::apply_btn_clicked () +SoundFileBox::tags_changed () { - string tag_string = tags_entry.get_text (); + string tag_string = tags_entry.get_buffer()->get_text (); + + if (tag_string.empty()) { + return; + } vector<string> tags; - if (!PBD::tokenize (tag_string, string(","), std::back_inserter (tags), true)) { + if (!PBD::tokenize (tag_string, string(",\n"), std::back_inserter (tags), true)) { warning << _("SoundFileBox: Could not tokenize string: ") << tag_string << endmsg; return; } - - Library->set_tags (path, tags); - Library->save_changes (); + + save_tags (tags); } void -SoundFileBox::audition_status_changed (bool active) +SoundFileBox::save_tags (const vector<string>& tags) { - ENSURE_GUI_THREAD(bind (mem_fun (*this, &SoundFileBox::audition_status_changed), active)); - - if (!active) { - stop_btn_clicked (); - } + Library->set_tags (string ("//") + path, tags); + Library->save_changes (); } -// this needs to be kept in sync with the ImportMode enum defined in editing.h and editing_syms.h. -static const char *import_mode_strings[] = { - N_("Add to Region list"), - N_("Add to selected Track(s)"), - N_("Add as new Track(s)"), - N_("Add as new Tape Track(s)"), - 0 -}; - -SoundFileBrowser::SoundFileBrowser (string title, ARDOUR::Session* s) - : ArdourDialog (title, false), - chooser (Gtk::FILE_CHOOSER_ACTION_OPEN), - found_list (Gtk::ListStore::create(found_list_columns)), +SoundFileBrowser::SoundFileBrowser (Gtk::Window& parent, string title, ARDOUR::Session* s, bool persistent) + : ArdourDialog (parent, title, false, false), + found_list (ListStore::create(found_list_columns)), + chooser (FILE_CHOOSER_ACTION_OPEN), found_list_view (found_list), + preview (persistent), found_search_btn (_("Search")) + { - set_default_size (700, 500); - Gtk::HBox* hbox = manage(new Gtk::HBox); - hbox->pack_start(notebook); - hbox->pack_start(preview, Gtk::PACK_SHRINK); - get_vbox()->pack_start(*hbox); + VBox* vbox; + HBox* hbox; + + gm = 0; + + set_session (s); + resetting_ourselves = false; + + hpacker.set_spacing (6); + hpacker.pack_start (notebook, true, true); + hpacker.pack_start (preview, false, false); + + get_vbox()->pack_start (hpacker, true, true); - hbox = manage(new Gtk::HBox); + hbox = manage(new HBox); hbox->pack_start (found_entry); hbox->pack_start (found_search_btn); - Gtk::VBox* vbox = manage(new Gtk::VBox); - vbox->pack_start (*hbox, Gtk::PACK_SHRINK); + vbox = manage(new VBox); + vbox->pack_start (*hbox, PACK_SHRINK); vbox->pack_start (found_list_view); found_list_view.append_column(_("Paths"), found_list_columns.pathname); - notebook.append_page (chooser, _("Files")); - notebook.append_page (*vbox, _("Tags")); + chooser.set_border_width (12); + + notebook.append_page (chooser, _("Browse Files")); + notebook.append_page (*vbox, _("Search Tags")); - found_list_view.get_selection()->set_mode (Gtk::SELECTION_MULTIPLE); + notebook.set_size_request (500, -1); - custom_filter.add_custom (Gtk::FILE_FILTER_FILENAME, mem_fun(*this, &SoundFileBrowser::on_custom)); - custom_filter.set_name (_("Probable audio files")); + found_list_view.get_selection()->set_mode (SELECTION_MULTIPLE); + found_list_view.signal_row_activated().connect (mem_fun (*this, &SoundFileBrowser::found_list_view_activated)); + + custom_filter.add_custom (FILE_FILTER_FILENAME, mem_fun(*this, &SoundFileBrowser::on_custom)); + custom_filter.set_name (_("Audio files")); matchall_filter.add_pattern ("*.*"); matchall_filter.set_name (_("All files")); @@ -316,6 +438,7 @@ SoundFileBrowser::SoundFileBrowser (string title, ARDOUR::Session* s) chooser.add_filter (matchall_filter); chooser.set_select_multiple (true); chooser.signal_update_preview().connect(mem_fun(*this, &SoundFileBrowser::update_preview)); + chooser.signal_file_activated().connect (mem_fun (*this, &SoundFileBrowser::chooser_file_activated)); if (!persistent_folder.empty()) { chooser.set_current_folder (persistent_folder); @@ -325,10 +448,11 @@ SoundFileBrowser::SoundFileBrowser (string title, ARDOUR::Session* s) found_search_btn.signal_clicked().connect(mem_fun(*this, &SoundFileBrowser::found_search_clicked)); found_entry.signal_activate().connect(mem_fun(*this, &SoundFileBrowser::found_search_clicked)); + + add_button (Stock::CANCEL, RESPONSE_CANCEL); + add_button (Stock::APPLY, RESPONSE_APPLY); + add_button (Stock::OK, RESPONSE_OK); - show_all (); - - set_session (s); } SoundFileBrowser::~SoundFileBrowser () @@ -336,37 +460,129 @@ SoundFileBrowser::~SoundFileBrowser () persistent_folder = chooser.get_current_folder(); } + +void +SoundFileBrowser::on_show () +{ + ArdourDialog::on_show (); + start_metering (); +} + +void +SoundFileBrowser::clear_selection () +{ + chooser.unselect_all (); + found_list_view.get_selection()->unselect_all (); +} + +void +SoundFileBrowser::chooser_file_activated () +{ + preview.audition (); +} + +void +SoundFileBrowser::found_list_view_activated (const TreeModel::Path& path, TreeViewColumn* col) +{ + preview.audition (); +} + void SoundFileBrowser::set_session (Session* s) { - preview.set_session(s); + ArdourDialog::set_session (s); + preview.set_session (s); + if (s) { + add_gain_meter (); + } else { + remove_gain_meter (); + } +} + +void +SoundFileBrowser::add_gain_meter () +{ + if (gm) { + delete gm; + } + + gm = new GainMeter (session->the_auditioner(), *session); + + meter_packer.set_border_width (12); + meter_packer.pack_start (*gm, false, true); + hpacker.pack_end (meter_packer, false, false); + meter_packer.show_all (); + start_metering (); +} + +void +SoundFileBrowser::remove_gain_meter () +{ + if (gm) { + meter_packer.remove (*gm); + hpacker.remove (meter_packer); + delete gm; + gm = 0; + } +} + +void +SoundFileBrowser::start_metering () +{ + metering_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun(*this, &SoundFileBrowser::meter)); +} + +void +SoundFileBrowser::stop_metering () +{ + metering_connection.disconnect(); +} + +void +SoundFileBrowser::meter () +{ + if (is_mapped () && session && gm) { + gm->update_meters (); + } } bool -SoundFileBrowser::on_custom (const Gtk::FileFilter::Info& filter_info) +SoundFileBrowser::on_custom (const FileFilter::Info& filter_info) { - return AudioFileSource::safe_file_extension(filter_info.filename); + return AudioFileSource::safe_file_extension (filter_info.filename); } void SoundFileBrowser::update_preview () { - preview.setup_labels(chooser.get_filename()); + preview.setup_labels (chooser.get_filename()); + + if (preview.autoplay()) { + Glib::signal_idle().connect (mem_fun (preview, &SoundFileBox::audition_oneshot)); + } } void SoundFileBrowser::found_list_view_selected () { - string file; - - Gtk::TreeView::Selection::ListHandle_Path rows = found_list_view.get_selection()->get_selected_rows (); - - if (!rows.empty()) { - Gtk::TreeIter iter = found_list->get_iter(*rows.begin()); - file = (*iter)[found_list_columns.pathname]; - chooser.set_filename (file); + if (!reset_options ()) { + set_response_sensitive (RESPONSE_OK, false); + } else { + ustring file; + + TreeView::Selection::ListHandle_Path rows = found_list_view.get_selection()->get_selected_rows (); + + if (!rows.empty()) { + TreeIter iter = found_list->get_iter(*rows.begin()); + file = (*iter)[found_list_columns.pathname]; + chooser.set_filename (file); + set_response_sensitive (RESPONSE_OK, true); + } else { + set_response_sensitive (RESPONSE_OK, false); + } + + preview.setup_labels (file); } - preview.setup_labels (file); } void @@ -376,194 +592,608 @@ SoundFileBrowser::found_search_clicked () vector<string> tags; - if (!PBD::tokenize (tag_string, string(","), std::back_inserter (tags), true)) { + if (!PBD::tokenize (tag_string, string(","), std::back_inserter (tags), true)) { warning << _("SoundFileBrowser: Could not tokenize string: ") << tag_string << endmsg; return; } - + vector<string> results; Library->search_members_and (results, tags); found_list->clear(); for (vector<string>::iterator i = results.begin(); i != results.end(); ++i) { - Gtk::TreeModel::iterator new_row = found_list->append(); - Gtk::TreeModel::Row row = *new_row; - row[found_list_columns.pathname] = *i; + TreeModel::iterator new_row = found_list->append(); + TreeModel::Row row = *new_row; + string path = Glib::filename_from_uri (string ("file:") + *i); + row[found_list_columns.pathname] = path; } } -SoundFileChooser::SoundFileChooser (string title, ARDOUR::Session* s) - : - SoundFileBrowser(title, s) +vector<ustring> +SoundFileBrowser::get_paths () { - add_button (Gtk::Stock::OPEN, Gtk::RESPONSE_OK); - add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + vector<ustring> results; - chooser.set_select_multiple (false); - found_list_view.get_selection()->set_mode (Gtk::SELECTION_SINGLE); - show_all (); + int n = notebook.get_current_page (); + + if (n == 0) { + vector<ustring> filenames = chooser.get_filenames(); + vector<ustring>::iterator i; + + for (i = filenames.begin(); i != filenames.end(); ++i) { + struct stat buf; + if ((!stat((*i).c_str(), &buf)) && S_ISREG(buf.st_mode)) { + results.push_back (*i); + } + } + + } else { + + typedef TreeView::Selection::ListHandle_Path ListPath; + + ListPath rows = found_list_view.get_selection()->get_selected_rows (); + for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) { + TreeIter iter = found_list->get_iter(*i); + ustring str = (*iter)[found_list_columns.pathname]; + + results.push_back (str); + } + } + + return results; } -string -SoundFileChooser::get_filename () +void +SoundFileOmega::reset_options_noret () { - Gtk::TreeModel::iterator iter; - Gtk::TreeModel::Row row; - - string filename; - switch (notebook.get_current_page()) { - case 0: - filename = chooser.get_filename(); - case 1: - iter = found_list_view.get_selection()->get_selected(); - row = *iter; - filename = row[found_list_columns.pathname]; - default: - /* NOT REACHED */ - return ""; + if (!resetting_ourselves) { + (void) reset_options (); } - - struct stat buf; - if (stat (filename.c_str(), &buf) || !S_ISREG(buf.st_mode)) { - return ""; - } - - return filename; } -vector<string> SoundFileOmega::mode_strings; - -SoundFileOmega::SoundFileOmega (string title, ARDOUR::Session* s) - : SoundFileBrowser (title, s), - split_check (_("Split Channels")) +bool +SoundFileOmega::reset_options () { - ARDOUR_UI::instance()->tooltips().set_tip(split_check, - _("Create a region for each channel")); + vector<ustring> paths = get_paths (); - Gtk::Button* btn = add_button (_("Embed"), ResponseEmbed); - ARDOUR_UI::instance()->tooltips().set_tip(*btn, - _("Link to an external file")); + if (paths.empty()) { - btn = add_button (_("Import"), ResponseImport); - ARDOUR_UI::instance()->tooltips().set_tip(*btn, - _("Copy a file to the session folder")); + channel_combo.set_sensitive (false); + action_combo.set_sensitive (false); + where_combo.set_sensitive (false); + copy_files_btn.set_sensitive (false); - add_button (Gtk::Stock::CLOSE, Gtk::RESPONSE_CLOSE); + return false; + + } else { + + channel_combo.set_sensitive (true); + action_combo.set_sensitive (true); + where_combo.set_sensitive (true); + + /* if we get through this function successfully, this may be + reset at the end, once we know if we can use hard links + to do embedding + */ + + if (Config->get_only_copy_imported_files()) { + copy_files_btn.set_sensitive (false); + } else { + copy_files_btn.set_sensitive (false); + } + } + + bool same_size; + bool src_needed; + bool selection_includes_multichannel; + bool selection_can_be_embedded_with_links = check_link_status (*session, paths); + ImportMode mode; + + if (check_info (paths, same_size, src_needed, selection_includes_multichannel)) { + Glib::signal_idle().connect (mem_fun (*this, &SoundFileOmega::bad_file_message)); + return false; + } + + ustring existing_choice; + vector<string> action_strings; + + if (selected_track_cnt > 0) { + if (channel_combo.get_active_text().length()) { + ImportDisposition id = get_channel_disposition(); + + switch (id) { + case Editing::ImportDistinctFiles: + if (selected_track_cnt == paths.size()) { + action_strings.push_back (importmode2string (ImportToTrack)); + } + break; + + case Editing::ImportDistinctChannels: + /* XXX it would be nice to allow channel-per-selected track + but its too hard we don't want to deal with all the + different per-file + per-track channel configurations. + */ + break; + + default: + action_strings.push_back (importmode2string (ImportToTrack)); + break; + } + } + } + + action_strings.push_back (importmode2string (ImportAsTrack)); + action_strings.push_back (importmode2string (ImportAsRegion)); + action_strings.push_back (importmode2string (ImportAsTapeTrack)); + + resetting_ourselves = true; + + existing_choice = action_combo.get_active_text(); + + set_popdown_strings (action_combo, action_strings); + + /* preserve any existing choice, if possible */ + + + if (existing_choice.length()) { + vector<string>::iterator x; + for (x = action_strings.begin(); x != action_strings.end(); ++x) { + if (*x == existing_choice) { + action_combo.set_active_text (existing_choice); + break; + } + } + if (x == action_strings.end()) { + action_combo.set_active_text (action_strings.front()); + } + } else { + action_combo.set_active_text (action_strings.front()); + } + + resetting_ourselves = false; + + if ((mode = get_mode()) == ImportAsRegion) { + where_combo.set_sensitive (false); + } else { + where_combo.set_sensitive (true); + } + + vector<string> channel_strings; - if (mode_strings.empty()) { - mode_strings = I18N (import_mode_strings); + if (mode == ImportAsTrack || mode == ImportAsTapeTrack || mode == ImportToTrack) { + channel_strings.push_back (_("one track per file")); + + if (selection_includes_multichannel) { + channel_strings.push_back (_("one track per channel")); + } + + if (paths.size() > 1) { + /* tape tracks are a single region per track, so we cannot + sequence multiple files. + */ + if (mode != ImportAsTapeTrack) { + channel_strings.push_back (_("sequence files")); + } + if (same_size) { + channel_strings.push_back (_("all files in one region")); + } + + } + + } else { + channel_strings.push_back (_("one region per file")); + + if (selection_includes_multichannel) { + channel_strings.push_back (_("one region per channel")); + } + + if (paths.size() > 1) { + if (same_size) { + channel_strings.push_back (_("all files in one region")); + } + } + } + + existing_choice = channel_combo.get_active_text(); + + set_popdown_strings (channel_combo, channel_strings); + + /* preserve any existing choice, if possible */ + + if (existing_choice.length()) { + vector<string>::iterator x; + for (x = channel_strings.begin(); x != channel_strings.end(); ++x) { + if (*x == existing_choice) { + channel_combo.set_active_text (existing_choice); + break; + } + } + if (x == channel_strings.end()) { + channel_combo.set_active_text (channel_strings.front()); + } + } else { + channel_combo.set_active_text (channel_strings.front()); } - Gtkmm2ext::set_popdown_strings (mode_combo, mode_strings); - set_mode (Editing::ImportAsRegion); + if (src_needed) { + src_combo.set_sensitive (true); + } else { + src_combo.set_sensitive (false); + } + + if (Config->get_only_copy_imported_files()) { - get_action_area()->pack_start (split_check); - get_action_area()->pack_start (mode_combo); + if (selection_can_be_embedded_with_links) { + copy_files_btn.set_sensitive (true); + } else { + copy_files_btn.set_sensitive (false); + } - mode_combo.signal_changed().connect (mem_fun (*this, &SoundFileOmega::mode_changed)); + } else { + + copy_files_btn.set_sensitive (true); + } - show_all (); + return true; +} + + +bool +SoundFileOmega::bad_file_message() +{ + MessageDialog msg (*this, + _("One or more of the selected files\ncannot be used by Ardour"), + true, + Gtk::MESSAGE_INFO, + Gtk::BUTTONS_OK); + msg.run (); + resetting_ourselves = true; + chooser.unselect_uri (chooser.get_preview_uri()); + resetting_ourselves = false; + + return false; } bool -SoundFileOmega::get_split () +SoundFileOmega::check_info (const vector<ustring>& paths, bool& same_size, bool& src_needed, bool& multichannel) { - return split_check.get_active(); + SNDFILE* sf; + SF_INFO info; + nframes64_t sz = 0; + bool err = false; + + same_size = true; + src_needed = false; + multichannel = false; + + for (vector<ustring>::const_iterator i = paths.begin(); i != paths.end(); ++i) { + + info.format = 0; // libsndfile says to clear this before sf_open(). + + if ((sf = sf_open ((char*) (*i).c_str(), SFM_READ, &info)) != 0) { + sf_close (sf); + + if (info.channels > 1) { + multichannel = true; + } + + if (sz == 0) { + sz = info.frames; + } else { + if (sz != info.frames) { + same_size = false; + } + } + + if ((nframes_t) info.samplerate != session->frame_rate()) { + src_needed = true; + } + + } else { + err = true; + } + } + + return err; } -vector<Glib::ustring> -SoundFileOmega::get_paths () + +bool +SoundFileOmega::check_link_status (const Session& s, const vector<ustring>& paths) { - vector<Glib::ustring> results; - - int n = notebook.get_current_page (); + sys::path path = s.session_directory().sound_path() / "linktest"; + string tmpdir = path.to_string(); + bool ret = false; + + if (mkdir (tmpdir.c_str(), 0744)) { + if (errno != EEXIST) { + return false; + } + } - if (n == 0) { - vector<Glib::ustring> filenames = chooser.get_filenames(); - vector<Glib::ustring>::iterator i; - for (i = filenames.begin(); i != filenames.end(); ++i) { - struct stat buf; - if ((!stat((*i).c_str(), &buf)) && S_ISREG(buf.st_mode)) { - results.push_back (*i); - } + for (vector<ustring>::const_iterator i = paths.begin(); i != paths.end(); ++i) { + + char tmpc[MAXPATHLEN+1]; + + snprintf (tmpc, sizeof(tmpc), "%s/%s", tmpdir.c_str(), Glib::path_get_basename (*i).c_str()); + + /* can we link ? */ + + if (link ((*i).c_str(), tmpc)) { + goto out; } - return results; - - } else { - typedef Gtk::TreeView::Selection::ListHandle_Path ListPath; - - ListPath rows = found_list_view.get_selection()->get_selected_rows (); - for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) { - Gtk::TreeIter iter = found_list->get_iter(*i); - string str = (*iter)[found_list_columns.pathname]; - - results.push_back (str); - } - return results; + unlink (tmpc); } + + ret = true; + + out: + rmdir (tmpdir.c_str()); + return ret; +} + +SoundFileChooser::SoundFileChooser (Gtk::Window& parent, string title, ARDOUR::Session* s) + : SoundFileBrowser (parent, title, s, false) +{ + set_size_request (780, 300); + chooser.set_select_multiple (false); + found_list_view.get_selection()->set_mode (SELECTION_SINGLE); } void -SoundFileOmega::set_mode (Editing::ImportMode mode) +SoundFileChooser::on_hide () { - mode_combo.set_active_text (mode_strings[(int)mode]); + ArdourDialog::on_hide(); + stop_metering (); - switch (mode) { - case Editing::ImportAsRegion: - split_check.set_sensitive (true); - break; - case Editing::ImportAsTrack: - split_check.set_sensitive (true); - break; - case Editing::ImportToTrack: - split_check.set_sensitive (false); - break; - case Editing::ImportAsTapeTrack: - split_check.set_sensitive (true); - break; - } -} - -Editing::ImportMode -SoundFileOmega::get_mode () -{ - vector<string>::iterator i; - uint32_t n; - string str = mode_combo.get_active_text (); - - for (n = 0, i = mode_strings.begin (); i != mode_strings.end(); ++i, ++n) { - if (str == (*i)) { - break; - } + if (session) { + session->cancel_audition(); + } +} + +ustring +SoundFileChooser::get_filename () +{ + vector<ustring> paths; + + paths = get_paths (); + + if (paths.empty()) { + return ustring (); + } + + if (!Glib::file_test (paths.front(), Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) { + return ustring(); } - if (i == mode_strings.end()) { - fatal << string_compose (_("programming error: %1"), X_("unknown import mode string")) << endmsg; + return paths.front(); +} + +SoundFileOmega::SoundFileOmega (Gtk::Window& parent, string title, ARDOUR::Session* s, int selected_tracks, bool persistent, + Editing::ImportMode mode_hint) + : SoundFileBrowser (parent, title, s, persistent), + copy_files_btn ( _("Copy files to session")), + selected_track_cnt (selected_tracks) +{ + VBox* vbox; + HBox* hbox; + vector<string> str; + + set_size_request (-1, 450); + + block_two.set_border_width (12); + block_three.set_border_width (12); + block_four.set_border_width (12); + + options.set_spacing (12); + + str.clear (); + str.push_back (_("use file timestamp")); + str.push_back (_("at edit cursor")); + str.push_back (_("at playhead")); + str.push_back (_("at session start")); + set_popdown_strings (where_combo, str); + where_combo.set_active_text (str.front()); + + Label* l = manage (new Label); + l->set_text (_("Add files:")); + + hbox = manage (new HBox); + hbox->set_border_width (12); + hbox->set_spacing (6); + hbox->pack_start (*l, false, false); + hbox->pack_start (action_combo, false, false); + vbox = manage (new VBox); + vbox->pack_start (*hbox, false, false); + options.pack_start (*vbox, false, false); + + /* dummy entry for action combo so that it doesn't look odd if we + come up with no tracks selected. + */ + + str.clear (); + str.push_back (importmode2string (mode_hint)); + set_popdown_strings (action_combo, str); + action_combo.set_active_text (str.front()); + action_combo.set_sensitive (false); + + l = manage (new Label); + l->set_text (_("Insert:")); + + hbox = manage (new HBox); + hbox->set_border_width (12); + hbox->set_spacing (6); + hbox->pack_start (*l, false, false); + hbox->pack_start (where_combo, false, false); + vbox = manage (new VBox); + vbox->pack_start (*hbox, false, false); + options.pack_start (*vbox, false, false); + + + l = manage (new Label); + l->set_text (_("Mapping:")); + + hbox = manage (new HBox); + hbox->set_border_width (12); + hbox->set_spacing (6); + hbox->pack_start (*l, false, false); + hbox->pack_start (channel_combo, false, false); + vbox = manage (new VBox); + vbox->pack_start (*hbox, false, false); + options.pack_start (*vbox, false, false); + + str.clear (); + str.push_back (_("one track per file")); + set_popdown_strings (channel_combo, str); + channel_combo.set_active_text (str.front()); + channel_combo.set_sensitive (false); + + l = manage (new Label); + l->set_text (_("Conversion Quality:")); + + hbox = manage (new HBox); + hbox->set_border_width (12); + hbox->set_spacing (6); + hbox->pack_start (*l, false, false); + hbox->pack_start (src_combo, false, false); + vbox = manage (new VBox); + vbox->pack_start (*hbox, false, false); + options.pack_start (*vbox, false, false); + + str.clear (); + str.push_back (_("Best")); + str.push_back (_("Good")); + str.push_back (_("Quick")); + str.push_back (_("Fast")); + str.push_back (_("Fastest")); + + set_popdown_strings (src_combo, str); + src_combo.set_active_text (str.front()); + src_combo.set_sensitive (false); + + reset_options (); + + action_combo.signal_changed().connect (mem_fun (*this, &SoundFileOmega::reset_options_noret)); + + copy_files_btn.set_active (true); + + block_four.pack_start (copy_files_btn, false, false); + + options.pack_start (block_four, false, false); + + get_vbox()->pack_start (options, false, false); + + /* setup disposition map */ + + disposition_map.insert (pair<ustring,ImportDisposition>(_("one track per file"), ImportDistinctFiles)); + disposition_map.insert (pair<ustring,ImportDisposition>(_("one track per channel"), ImportDistinctChannels)); + disposition_map.insert (pair<ustring,ImportDisposition>(_("merge files"), ImportMergeFiles)); + disposition_map.insert (pair<ustring,ImportDisposition>(_("sequence files"), ImportSerializeFiles)); + + disposition_map.insert (pair<ustring,ImportDisposition>(_("one region per file"), ImportDistinctFiles)); + disposition_map.insert (pair<ustring,ImportDisposition>(_("one region per channel"), ImportDistinctChannels)); + disposition_map.insert (pair<ustring,ImportDisposition>(_("all files in one region"), ImportMergeFiles)); + + chooser.signal_selection_changed().connect (mem_fun (*this, &SoundFileOmega::file_selection_changed)); +} + +void +SoundFileOmega::set_mode (ImportMode mode) +{ + action_combo.set_active_text (importmode2string (mode)); +} + +ImportMode +SoundFileOmega::get_mode () const +{ + return string2importmode (action_combo.get_active_text()); +} + +void +SoundFileOmega::on_hide () +{ + ArdourDialog::on_hide(); + if (session) { + session->cancel_audition(); + } +} + +ImportPosition +SoundFileOmega::get_position() const +{ + ustring str = where_combo.get_active_text(); + + if (str == _("use file timestamp")) { + return ImportAtTimestamp; + } else if (str == _("at edit cursor")) { + return ImportAtEditCursor; + } else if (str == _("at playhead")) { + return ImportAtPlayhead; + } else { + return ImportAtStart; + } +} + +SrcQuality +SoundFileOmega::get_src_quality() const +{ + ustring str = where_combo.get_active_text(); + + if (str == _("Best")) { + return SrcBest; + } else if (str == _("Good")) { + return SrcGood; + } else if (str == _("Quick")) { + return SrcQuick; + } else if (str == _("Fast")) { + return SrcFast; + } else { + return SrcFastest; + } +} + +ImportDisposition +SoundFileOmega::get_channel_disposition () const +{ + /* we use a map here because the channel combo can contain different strings + depending on the state of the other combos. the map contains all possible strings + and the ImportDisposition enum that corresponds to it. + */ + + ustring str = channel_combo.get_active_text(); + DispositionMap::const_iterator x = disposition_map.find (str); + + if (x == disposition_map.end()) { + fatal << string_compose (_("programming error: %1 (%2)"), "unknown string for import disposition", str) << endmsg; /*NOTREACHED*/ } - return (Editing::ImportMode) (n); + return x->second; } void -SoundFileOmega::mode_changed () +SoundFileOmega::reset (int selected_tracks) { - Editing::ImportMode mode = get_mode(); + selected_track_cnt = selected_tracks; + reset_options (); +} - switch (mode) { - case Editing::ImportAsRegion: - split_check.set_sensitive (true); - break; - case Editing::ImportAsTrack: - split_check.set_sensitive (true); - break; - case Editing::ImportToTrack: - split_check.set_sensitive (false); - break; - case Editing::ImportAsTapeTrack: - split_check.set_sensitive (true); - break; +void +SoundFileOmega::file_selection_changed () +{ + if (resetting_ourselves) { + return; + } + + if (!reset_options ()) { + set_response_sensitive (RESPONSE_OK, false); + } else { + if (chooser.get_filenames().size() > 0) { + set_response_sensitive (RESPONSE_OK, true); + } else { + set_response_sensitive (RESPONSE_OK, false); + } } } diff --git a/gtk2_ardour/sfdb_ui.h b/gtk2_ardour/sfdb_ui.h index 34c3f558bb..43f76a9ea2 100644 --- a/gtk2_ardour/sfdb_ui.h +++ b/gtk2_ardour/sfdb_ui.h @@ -22,6 +22,8 @@ #include <string> #include <vector> +#include <map> +#include <glibmm/ustring.h> #include <sigc++/signal.h> @@ -41,32 +43,50 @@ #include "ardour_dialog.h" #include "editing.h" +namespace ARDOUR { + class Session; +}; + +class GainMeter; + class SoundFileBox : public Gtk::VBox { public: - SoundFileBox (); + SoundFileBox (bool persistent); virtual ~SoundFileBox () {}; void set_session (ARDOUR::Session* s); - bool setup_labels (std::string filename); + bool setup_labels (const Glib::ustring& filename); + + void audition(); + bool audition_oneshot(); + bool autoplay () const; protected: ARDOUR::Session* _session; - std::string path; + Glib::ustring path; ARDOUR::SoundFileInfo sf_info; - pid_t current_pid; + Gtk::Table table; Gtk::Label length; Gtk::Label format; Gtk::Label channels; Gtk::Label samplerate; Gtk::Label timecode; + + Gtk::Label channels_value; + Gtk::Label samplerate_value; + Gtk::Label format_text; + AudioClock length_clock; + AudioClock timecode_clock; + Gtk::Frame border_frame; - - Gtk::Entry tags_entry; + Gtk::Label preview_label; + + Gtk::TextView tags_entry; Gtk::VBox main_box; Gtk::VBox path_box; @@ -74,91 +94,137 @@ class SoundFileBox : public Gtk::VBox Gtk::Button play_btn; Gtk::Button stop_btn; + Gtk::CheckButton autoplay_btn; Gtk::Button apply_btn; - + bool tags_entry_left (GdkEventFocus* event); - void play_btn_clicked (); - void stop_btn_clicked (); - void apply_btn_clicked (); - - void audition_status_changed (bool state); + void tags_changed (); + void save_tags (const std::vector<std::string>&); + void stop_audition (); }; class SoundFileBrowser : public ArdourDialog { + private: + class FoundTagColumns : public Gtk::TreeModel::ColumnRecord + { + public: + Gtk::TreeModelColumn<Glib::ustring> pathname; + + FoundTagColumns() { add(pathname); } + }; + + FoundTagColumns found_list_columns; + Glib::RefPtr<Gtk::ListStore> found_list; + public: - SoundFileBrowser (std::string title, ARDOUR::Session* _s = 0); + SoundFileBrowser (Gtk::Window& parent, std::string title, ARDOUR::Session* _s, bool persistent); virtual ~SoundFileBrowser (); virtual void set_session (ARDOUR::Session*); + std::vector<Glib::ustring> get_paths (); + + void clear_selection (); - protected: Gtk::FileChooserWidget chooser; + Gtk::TreeView found_list_view; + + protected: + bool resetting_ourselves; + Gtk::FileFilter custom_filter; Gtk::FileFilter matchall_filter; SoundFileBox preview; + Gtk::HBox hpacker; static Glib::ustring persistent_folder; - class FoundTagColumns : public Gtk::TreeModel::ColumnRecord - { - public: - Gtk::TreeModelColumn<string> pathname; - - FoundTagColumns() { add(pathname); } - }; - - FoundTagColumns found_list_columns; - Glib::RefPtr<Gtk::ListStore> found_list; - Gtk::TreeView found_list_view; Gtk::Entry found_entry; Gtk::Button found_search_btn; - Gtk::Notebook notebook; - + + GainMeter* gm; + Gtk::VBox meter_packer; + void add_gain_meter (); + void remove_gain_meter (); + void meter (); + void start_metering (); + void stop_metering (); + sigc::connection metering_connection; + void update_preview (); void found_list_view_selected (); + void found_list_view_activated (const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn*); void found_search_clicked (); + + void chooser_file_activated (); bool on_custom (const Gtk::FileFilter::Info& filter_info); + + virtual bool reset_options() { return true; } + + protected: + void on_show(); + }; class SoundFileChooser : public SoundFileBrowser { public: - SoundFileChooser (std::string title, ARDOUR::Session* _s = 0); + SoundFileChooser (Gtk::Window& parent, std::string title, ARDOUR::Session* _s = 0); virtual ~SoundFileChooser () {}; - std::string get_filename (); + Glib::ustring get_filename (); + + protected: + void on_hide(); }; class SoundFileOmega : public SoundFileBrowser { + public: - SoundFileOmega (std::string title, ARDOUR::Session* _s); - virtual ~SoundFileOmega () {}; - - /* these are returned by the Dialog::run() method. note - that builtin GTK responses are all negative, leaving - positive values for application-defined responses. - */ - - const static int ResponseImport = 1; - const static int ResponseEmbed = 2; + SoundFileOmega (Gtk::Window& parent, std::string title, ARDOUR::Session* _s, int selected_tracks, bool persistent, + Editing::ImportMode mode_hint = Editing::ImportAsTrack); - std::vector<Glib::ustring> get_paths (); - bool get_split (); + void reset (int selected_tracks); + Gtk::ComboBoxText action_combo; + Gtk::ComboBoxText where_combo; + Gtk::ComboBoxText channel_combo; + Gtk::ComboBoxText src_combo; + + Gtk::CheckButton copy_files_btn; + void set_mode (Editing::ImportMode); - Editing::ImportMode get_mode (); + Editing::ImportMode get_mode() const; + Editing::ImportPosition get_position() const; + Editing::ImportDisposition get_channel_disposition() const; + ARDOUR::SrcQuality get_src_quality() const; protected: - Gtk::CheckButton split_check; - Gtk::ComboBoxText mode_combo; - - void mode_changed (); - - static std::vector<std::string> mode_strings; + void on_hide(); + + private: + uint32_t selected_track_cnt; + + typedef std::map<Glib::ustring,Editing::ImportDisposition> DispositionMap; + DispositionMap disposition_map; + + Gtk::HBox options; + Gtk::VBox block_two; + Gtk::VBox block_three; + Gtk::VBox block_four; + + bool check_info (const std::vector<Glib::ustring>& paths, + bool& same_size, bool& src_needed, bool& multichannel); + + static bool check_link_status (const ARDOUR::Session&, const std::vector<Glib::ustring>& paths); + + void file_selection_changed (); + bool reset_options (); + void reset_options_noret (); + bool bad_file_message (); }; #endif // __ardour_sfdb_ui_h__ diff --git a/gtk2_ardour/streamview.cc b/gtk2_ardour/streamview.cc index 560dcd503f..1a3fb9a084 100644 --- a/gtk2_ardour/streamview.cc +++ b/gtk2_ardour/streamview.cc @@ -153,6 +153,8 @@ StreamView::set_samples_per_unit (gdouble spp) void StreamView::add_region_view (boost::shared_ptr<Region> r) { + // ENSURE_GUI_THREAD (bind (mem_fun (*this, &AudioStreamView::add_region_view), r)); + add_region_view_internal (r, true); } diff --git a/gtk2_ardour/utils.cc b/gtk2_ardour/utils.cc index 83a95fb71f..570a481c11 100644 --- a/gtk2_ardour/utils.cc +++ b/gtk2_ardour/utils.cc @@ -337,6 +337,12 @@ set_color (Gdk::Color& c, int rgb) c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256); } +#ifdef GTKOSX +extern "C" { + gboolean gdk_quartz_possibly_forward (GdkEvent*); +} +#endif + bool key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev) { @@ -348,7 +354,6 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev) #ifdef DEBUG_ACCELERATOR_HANDLING bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0); #endif - if (focus) { if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) { special_handling_of_unmodified_accelerators = true; @@ -444,6 +449,11 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev) cerr << "\tactivate, then propagate\n"; } #endif +#ifdef GTKOSX + if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) { + return true; + } +#endif if (!gtk_window_activate_key (win, ev)) { return gtk_window_propagate_key_event (win, ev); } else { @@ -469,6 +479,11 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev) cerr << "\tpropagation didn't handle, so activate\n"; } #endif +#ifdef GTKOSX + if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) { + return true; + } +#endif return gtk_window_activate_key (win, ev); } else { #ifdef DEBUG_ACCELERATOR_HANDLING @@ -490,23 +505,18 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev) Glib::RefPtr<Gdk::Pixbuf> get_xpm (std::string name) { - if (!xpm_map[name]) { - - SearchPath spath(ARDOUR::ardour_search_path()); - spath += ARDOUR::system_data_search_path(); - - spath.add_subdirectory_to_paths("pixmaps"); + SearchPath spath(ARDOUR::ardour_search_path()); + spath += ARDOUR::system_data_search_path(); - sys::path data_file_path; + spath.add_subdirectory_to_paths("pixmaps"); - if(!find_file_in_search_path (spath, name, data_file_path)) { - fatal << string_compose (_("cannot find pixmap %1"), name) << endmsg; - } + sys::path data_file_path; - xpm_map[name] = Gdk::Pixbuf::create_from_file (data_file_path.to_string()); + if(!find_file_in_search_path (spath, name, data_file_path)) { + fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg; } - - return (xpm_map[name]); + + return Gdk::Pixbuf::create_from_file (data_file_path.to_string()); } Glib::RefPtr<Gdk::Pixbuf> |