2018-11-15 10:21:55 -05:00
/*
2019-08-02 17:26:43 -04:00
* Copyright ( C ) 2018 - 2019 Ben Loftis < ben @ harrisonconsoles . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
*/
2018-11-15 10:21:55 -05:00
# include <algorithm>
# include <sstream>
2022-01-18 19:59:05 -05:00
# include <string>
2018-11-15 10:21:55 -05:00
# include "ardour/audiofilesource.h"
# include "ardour/region_factory.h"
# include "ardour/session.h"
2018-12-07 13:44:15 -05:00
# include "ardour/session_directory.h"
2022-01-18 19:59:05 -05:00
# include "ardour/smf_source.h"
# include "ardour/source.h"
2018-11-15 10:21:55 -05:00
# include "widgets/choice.h"
2019-05-31 14:36:46 -04:00
# include "context_menu_helper.h"
2018-11-15 10:21:55 -05:00
# include "editing.h"
2022-01-18 19:59:05 -05:00
# include "editor.h"
# include "editor_drag.h"
# include "editor_sources.h"
2018-11-15 10:21:55 -05:00
# include "gui_thread.h"
2022-01-18 19:59:05 -05:00
# include "keyboard.h"
2018-11-15 10:21:55 -05:00
# include "region_view.h"
# include "ui_config.h"
2022-01-18 19:59:05 -05:00
# include "utils.h"
2018-11-15 10:21:55 -05:00
# include "pbd/i18n.h"
using namespace std ;
using namespace ARDOUR ;
using namespace ArdourWidgets ;
using namespace ARDOUR_UI_UTILS ;
using namespace PBD ;
using namespace Gtk ;
using namespace Glib ;
using Gtkmm2ext : : Keyboard ;
EditorSources : : EditorSources ( Editor * e )
: EditorComponent ( e )
{
2022-01-18 19:59:05 -05:00
init ( ) ;
2018-11-15 10:21:55 -05:00
2022-01-18 00:52:57 -05:00
/* setup DnD Receive */
2018-11-15 10:30:22 -05:00
list < TargetEntry > source_list_target_table ;
2018-11-15 10:21:55 -05:00
2018-11-15 10:30:22 -05:00
source_list_target_table . push_back ( TargetEntry ( " text/plain " ) ) ;
source_list_target_table . push_back ( TargetEntry ( " text/uri-list " ) ) ;
source_list_target_table . push_back ( TargetEntry ( " application/x-rootwin-drop " ) ) ;
2018-11-15 10:21:55 -05:00
2018-11-15 10:30:22 -05:00
_display . add_drop_targets ( source_list_target_table ) ;
2022-01-18 19:59:05 -05:00
_display . signal_drag_data_received ( ) . connect ( sigc : : mem_fun ( * this , & EditorSources : : drag_data_received ) ) ;
2018-11-15 10:21:55 -05:00
2022-01-18 19:59:05 -05:00
_change_connection = _display . get_selection ( ) - > signal_changed ( ) . connect ( sigc : : mem_fun ( * this , & EditorSources : : selection_changed ) ) ;
2018-11-15 10:21:55 -05:00
2024-10-18 19:51:44 -04:00
e - > EditorFreeze . connect ( _editor_freeze_connection , MISSING_INVALIDATOR , std : : bind ( & EditorSources : : freeze_tree_model , this ) , gui_context ( ) ) ;
e - > EditorThaw . connect ( _editor_thaw_connection , MISSING_INVALIDATOR , std : : bind ( & EditorSources : : thaw_tree_model , this ) , gui_context ( ) ) ;
2018-11-15 10:21:55 -05:00
}
2019-12-25 12:00:57 -05:00
void
2022-01-18 19:59:05 -05:00
EditorSources : : init ( )
2019-12-25 12:00:57 -05:00
{
2022-01-18 19:59:05 -05:00
int bbt_width , date_width , height ;
2018-11-15 10:21:55 -05:00
2022-01-18 19:59:05 -05:00
Glib : : RefPtr < Pango : : Layout > layout = _display . create_pango_layout ( X_ ( " 000|000|000 " ) ) ;
2022-09-19 21:21:35 -04:00
layout - > get_pixel_size ( bbt_width , height ) ;
2022-01-18 19:59:05 -05:00
Glib : : RefPtr < Pango : : Layout > layout2 = _display . create_pango_layout ( X_ ( " 2018-10-14 12:12:30 " ) ) ;
2022-09-19 21:21:35 -04:00
layout2 - > get_pixel_size ( date_width , height ) ;
2021-08-28 17:29:44 -04:00
2022-01-18 19:59:05 -05:00
add_name_column ( ) ;
2020-02-25 09:42:01 -05:00
2022-01-26 16:19:32 -05:00
setup_col ( append_col ( _columns . channels , " Chans " ) , 1 , ALIGN_START , _ ( " # Ch " ) , _ ( " # Channels in the region " ) ) ;
setup_col ( append_col ( _columns . captd_for , date_width ) , 17 , ALIGN_START , _ ( " Captured For " ) , _ ( " Original Track this was recorded on " ) ) ;
2022-01-26 16:20:26 -05:00
setup_col ( append_col ( _columns . captd_xruns , " 1234567890 " ) , 21 , ALIGN_END , _ ( " # Xruns " ) , _ ( " Number of dropouts that occurred during recording " ) ) ;
2018-11-15 10:33:44 -05:00
2022-01-18 19:59:05 -05:00
add_tag_column ( ) ;
2021-05-07 16:45:28 -04:00
2022-01-26 16:19:32 -05:00
setup_col ( append_col ( _columns . take_id , date_width ) , 18 , ALIGN_START , _ ( " Take ID " ) , _ ( " Take ID " ) ) ;
2022-01-26 16:20:26 -05:00
setup_col ( append_col ( _columns . natural_pos , bbt_width ) , 20 , ALIGN_END , _ ( " Orig Pos " ) , _ ( " Original Position of the file on timeline, when it was recorded " ) ) ;
2021-08-28 17:29:44 -04:00
2022-01-18 19:59:05 -05:00
TreeViewColumn * tvc = append_col ( _columns . path , bbt_width ) ;
2022-01-26 16:19:32 -05:00
setup_col ( tvc , 13 , ALIGN_START , _ ( " Path " ) , _ ( " Path (folder) of the file location " ) ) ;
2022-01-18 19:59:05 -05:00
tvc - > set_expand ( true ) ;
2021-08-28 17:29:44 -04:00
2022-01-18 19:59:05 -05:00
/* make Name and Path columns manually resizable */
_display . get_column ( 0 ) - > set_resizable ( true ) ;
_display . get_column ( 5 ) - > set_resizable ( true ) ;
2018-11-15 10:21:55 -05:00
}
void
EditorSources : : selection_changed ( )
{
2022-01-18 19:59:05 -05:00
if ( _display . get_selection ( ) - > count_selected_rows ( ) > 0 ) {
TreeIter iter ;
TreeView : : Selection : : ListHandle_Path rows = _display . get_selection ( ) - > get_selected_rows ( ) ;
2018-11-15 10:21:55 -05:00
2022-01-18 19:59:05 -05:00
_editor - > get_selection ( ) . clear_regions ( ) ;
2018-11-15 10:21:55 -05:00
2022-01-18 19:59:05 -05:00
for ( TreeView : : Selection : : ListHandle_Path : : iterator i = rows . begin ( ) ; i ! = rows . end ( ) ; + + i ) {
2018-11-15 10:21:55 -05:00
if ( ( iter = _model - > get_iter ( * i ) ) ) {
2020-02-25 09:42:01 -05:00
/* highlight any regions in the editor that use this region's source */
2023-02-16 18:33:28 -05:00
std : : shared_ptr < ARDOUR : : Region > region = ( * iter ) [ _columns . region ] ;
2022-01-18 19:59:05 -05:00
if ( ! region )
continue ;
2019-05-31 14:36:46 -04:00
2023-02-16 18:33:28 -05:00
std : : shared_ptr < ARDOUR : : Source > source = region - > source ( ) ;
2018-11-15 10:24:37 -05:00
if ( source ) {
2023-02-16 18:33:28 -05:00
set < std : : shared_ptr < Region > > regions ;
2020-07-26 11:17:33 -04:00
RegionFactory : : get_regions_using_source ( source , regions ) ;
2018-11-15 10:21:55 -05:00
2023-02-16 18:33:28 -05:00
for ( set < std : : shared_ptr < Region > > : : iterator region = regions . begin ( ) ; region ! = regions . end ( ) ; region + + ) {
2018-11-15 10:24:37 -05:00
_change_connection . block ( true ) ;
2024-08-02 00:58:22 -04:00
_editor - > set_selected_regionview_from_region_list ( * region , SelectionAdd ) ;
2018-11-15 10:24:37 -05:00
_change_connection . block ( false ) ;
}
2018-11-15 10:21:55 -05:00
}
}
}
} else {
2022-01-18 19:59:05 -05:00
_editor - > get_selection ( ) . clear_regions ( ) ;
2018-11-15 10:21:55 -05:00
}
}
void
EditorSources : : show_context_menu ( int button , int time )
{
2019-05-31 14:36:46 -04:00
using namespace Gtk : : Menu_Helpers ;
2022-01-18 19:59:05 -05:00
Gtk : : Menu * menu = ARDOUR_UI_UTILS : : shared_popup_menu ( ) ;
MenuList & items = menu - > items ( ) ;
2019-06-06 21:28:40 -04:00
# ifdef RECOVER_REGIONS_IS_WORKING
2022-01-18 19:59:05 -05:00
items . push_back ( MenuElem ( _ ( " Recover the selected Sources to their original Track & Position " ) ,
sigc : : mem_fun ( * this , & EditorSources : : recover_selected_sources ) ) ) ;
2019-06-06 21:28:40 -04:00
# endif
2022-01-18 19:59:05 -05:00
items . push_back ( MenuElem ( _ ( " Remove the selected Sources " ) ,
sigc : : mem_fun ( * this , & EditorSources : : remove_selected_sources ) ) ) ;
2023-10-31 15:10:49 -04:00
menu - > popup ( button , time ) ;
2019-05-31 14:36:46 -04:00
}
2018-11-15 10:21:55 -05:00
2019-05-31 14:36:46 -04:00
void
EditorSources : : recover_selected_sources ( )
{
2019-06-06 21:28:40 -04:00
ARDOUR : : RegionList to_be_recovered ;
2020-02-25 09:42:01 -05:00
2022-01-18 19:59:05 -05:00
if ( _display . get_selection ( ) - > count_selected_rows ( ) > 0 ) {
TreeIter iter ;
TreeView : : Selection : : ListHandle_Path rows = _display . get_selection ( ) - > get_selected_rows ( ) ;
for ( TreeView : : Selection : : ListHandle_Path : : iterator i = rows . begin ( ) ; i ! = rows . end ( ) ; + + i ) {
2019-05-31 14:36:46 -04:00
if ( ( iter = _model - > get_iter ( * i ) ) ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < ARDOUR : : Region > region = ( * iter ) [ _columns . region ] ;
2019-05-31 14:36:46 -04:00
if ( region ) {
2022-01-18 19:59:05 -05:00
to_be_recovered . push_back ( region ) ;
2019-05-31 14:36:46 -04:00
}
}
}
}
/* ToDo */
2022-01-18 19:59:05 -05:00
_editor - > recover_regions ( to_be_recovered ) ; // this operation should be undo-able
2018-11-15 10:21:55 -05:00
}
2018-10-22 17:46:44 -04:00
void
EditorSources : : remove_selected_sources ( )
{
vector < string > choices ;
2022-01-18 19:59:05 -05:00
string prompt ;
2018-10-22 17:46:44 -04:00
2022-01-18 19:59:05 -05:00
prompt = _ ( " Do you want to remove the selected Sources? "
" \n This operation cannot be undone. "
2023-12-17 14:58:11 -05:00
" \n The source files will not actually be deleted until you execute Session -> Cleanup. " ) ;
2018-10-22 17:46:44 -04:00
choices . push_back ( _ ( " No, do nothing. " ) ) ;
2018-11-08 14:07:52 -05:00
choices . push_back ( _ ( " Only remove the Regions that use these Sources. " ) ) ;
2020-04-15 12:02:21 -04:00
choices . push_back ( _ ( " Yes, remove the Regions and Sources (cannot be undone!) " ) ) ;
2018-10-22 17:46:44 -04:00
2018-11-08 14:07:52 -05:00
Choice prompter ( _ ( " Remove selected Sources " ) , prompt , choices ) ;
2018-10-22 17:46:44 -04:00
int opt = prompter . run ( ) ;
2020-07-26 11:17:33 -04:00
if ( opt > = 1 ) {
2023-05-29 15:15:50 -04:00
struct WeakPtrCompare {
bool operator ( ) ( weak_ptr < ARDOUR : : Source > const & lhs , weak_ptr < ARDOUR : : Source > const & rhs ) const {
auto lptr = lhs . lock ( ) , rptr = rhs . lock ( ) ;
if ( ! rptr ) return false ;
if ( ! lptr ) return true ;
return lptr - > id ( ) < rptr - > id ( ) ;
}
} ;
std : : set < std : : weak_ptr < ARDOUR : : Source > , WeakPtrCompare > to_be_removed ;
2020-02-25 09:42:01 -05:00
2022-01-18 19:59:05 -05:00
if ( _display . get_selection ( ) - > count_selected_rows ( ) > 0 ) {
TreeIter iter ;
TreeView : : Selection : : ListHandle_Path rows = _display . get_selection ( ) - > get_selected_rows ( ) ;
2020-02-25 09:42:01 -05:00
2022-01-18 19:59:05 -05:00
_editor - > get_selection ( ) . clear_regions ( ) ;
2018-10-22 17:46:44 -04:00
2022-01-18 19:59:05 -05:00
for ( TreeView : : Selection : : ListHandle_Path : : iterator i = rows . begin ( ) ; i ! = rows . end ( ) ; + + i ) {
2018-10-22 17:46:44 -04:00
if ( ( iter = _model - > get_iter ( * i ) ) ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < ARDOUR : : Region > region = ( * iter ) [ _columns . region ] ;
2020-02-25 09:42:01 -05:00
2023-05-29 15:15:50 -04:00
if ( ! region ) {
2022-01-18 19:59:05 -05:00
continue ;
2023-05-29 15:15:50 -04:00
}
2018-10-22 17:46:44 -04:00
2023-05-29 15:15:50 -04:00
for ( auto const & source : region - > sources ( ) ) {
2023-02-16 18:33:28 -05:00
set < std : : shared_ptr < Region > > regions ;
2020-07-26 11:17:33 -04:00
RegionFactory : : get_regions_using_source ( source , regions ) ;
2018-10-22 17:46:44 -04:00
2023-05-29 15:15:50 -04:00
for ( auto const & region : regions ) {
2018-10-22 17:46:44 -04:00
_change_connection . block ( true ) ;
2023-05-31 09:53:12 -04:00
/* Note: this ignores regions without RegionView.
* When removing sources ( opt = = 2 ) ,
* Playlist : : region_going_away takes care of that
* ( Source : : drop_references - > Region : : source_deleted ,
* - > Region : : drop_references ) . see f58f5bef55a5aa1
*/
2024-08-02 00:58:22 -04:00
_editor - > set_selected_regionview_from_region_list ( region , SelectionAdd ) ;
2018-10-22 17:46:44 -04:00
_change_connection . block ( false ) ;
}
2020-02-25 09:42:01 -05:00
2023-05-29 15:15:50 -04:00
to_be_removed . insert ( source ) ;
2018-10-22 17:46:44 -04:00
}
}
}
2022-01-18 19:59:05 -05:00
_editor - > remove_regions ( _editor - > get_regions_from_selection_and_entered ( ) , false /*can_ripple*/ , false /*as_part_of_other_command*/ ) ; // this operation is undo-able
2018-11-08 14:07:52 -05:00
2022-01-18 19:59:05 -05:00
if ( opt = = 2 ) {
2023-05-29 15:15:50 -04:00
for ( auto const & s : to_be_removed ) {
_session - > remove_source ( s ) ; // this operation is (currently) not undo-able
2018-11-08 14:07:52 -05:00
}
}
2018-10-22 17:46:44 -04:00
}
}
}
2018-11-15 10:21:55 -05:00
bool
EditorSources : : key_press ( GdkEventKey * ev )
{
2018-10-22 17:46:44 -04:00
switch ( ev - > keyval ) {
2022-01-18 19:59:05 -05:00
case GDK_BackSpace :
remove_selected_sources ( ) ;
return true ;
2019-08-01 16:27:21 -04:00
2022-01-18 19:59:05 -05:00
default :
break ;
2018-10-22 17:46:44 -04:00
}
2018-11-08 11:43:39 -05:00
2022-01-18 19:59:05 -05:00
return RegionListBase : : key_press ( ev ) ;
2018-11-15 10:21:55 -05:00
}
bool
2022-01-18 19:59:05 -05:00
EditorSources : : button_press ( GdkEventButton * ev )
2018-11-15 10:21:55 -05:00
{
if ( Keyboard : : is_context_menu_event ( ev ) ) {
show_context_menu ( ev - > button , ev - > time ) ;
2020-03-31 15:00:45 -04:00
return true ;
2018-11-15 10:21:55 -05:00
}
return false ;
}
void
EditorSources : : drag_data_received ( const RefPtr < Gdk : : DragContext > & context ,
int x , int y ,
const SelectionData & data ,
2022-01-10 17:53:07 -05:00
guint info , guint dtime )
2018-11-15 10:21:55 -05:00
{
2022-01-10 17:53:07 -05:00
vector < string > paths ;
if ( data . get_target ( ) = = " GTK_TREE_MODEL_ROW " ) {
/* something is being dragged over the source list */
_editor - > _drags - > abort ( ) ;
_display . on_drag_data_received ( context , x , y , data , info , dtime ) ;
return ;
}
if ( _session & & convert_drop_to_paths ( paths , data ) ) {
timepos_t pos ;
bool copy = ( ( context - > get_actions ( ) & ( Gdk : : ACTION_COPY | Gdk : : ACTION_LINK | Gdk : : ACTION_MOVE ) ) = = Gdk : : ACTION_COPY ) ;
if ( UIConfiguration : : instance ( ) . get_only_copy_imported_files ( ) | | copy ) {
_editor - > do_import ( paths , Editing : : ImportDistinctFiles , Editing : : ImportAsRegion ,
2024-10-16 00:35:04 -04:00
SrcBest , SMFFileAndTrackName , SMFTempoIgnore , pos ) ;
2022-01-10 17:53:07 -05:00
} else {
2022-01-18 19:59:05 -05:00
_editor - > do_embed ( paths , Editing : : ImportDistinctFiles , Editing : : ImportAsRegion , pos ) ;
2022-01-10 17:53:07 -05:00
}
context - > drag_finish ( true , false , dtime ) ;
}
2018-11-15 10:21:55 -05:00
}
2023-02-16 18:33:28 -05:00
std : : shared_ptr < ARDOUR : : Region >
2018-11-15 10:21:55 -05:00
EditorSources : : get_single_selection ( )
{
2022-01-18 19:59:05 -05:00
Glib : : RefPtr < TreeSelection > selected = _display . get_selection ( ) ;
2018-11-15 10:24:37 -05:00
2022-01-18 19:59:05 -05:00
if ( selected - > count_selected_rows ( ) ! = 1 ) {
2023-02-16 18:33:28 -05:00
return std : : shared_ptr < ARDOUR : : Region > ( ) ;
2018-11-15 10:24:37 -05:00
}
TreeView : : Selection : : ListHandle_Path rows = selected - > get_selected_rows ( ) ;
/* only one row selected, so rows.begin() is it */
2018-11-15 10:21:55 -05:00
2022-01-18 19:59:05 -05:00
TreeIter iter = _model - > get_iter ( * rows . begin ( ) ) ;
2018-11-15 10:24:37 -05:00
if ( ! iter ) {
2023-02-16 18:33:28 -05:00
return std : : shared_ptr < ARDOUR : : Region > ( ) ;
2018-11-15 10:24:37 -05:00
}
2019-05-31 14:36:46 -04:00
return ( * iter ) [ _columns . region ] ;
2018-11-15 10:21:55 -05:00
}