2020-01-18 11:38:21 +03:00
/*
2021-08-26 20:14:12 +03:00
* Copyright ( c ) 2018 - 2021 , Andreas Kling < kling @ serenityos . org >
2021-09-03 13:14:37 +03:00
* Copyright ( c ) 2021 , Sam Atkins < atkinssj @ serenityos . org >
2020-01-18 11:38:21 +03:00
*
2021-04-22 11:24:48 +03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 11:38:21 +03:00
*/
2019-04-02 15:43:56 +03:00
# include "DirectoryView.h"
2020-09-13 22:38:44 +03:00
# include "FileUtils.h"
2020-08-17 21:06:21 +03:00
# include <AK/LexicalPath.h>
2020-05-03 07:30:09 +03:00
# include <AK/NumberFormat.h>
2020-01-28 00:36:06 +03:00
# include <AK/StringBuilder.h>
2021-08-26 20:14:12 +03:00
# include <LibConfig/Client.h>
2020-09-17 15:30:00 +03:00
# include <LibCore/MimeData.h>
2020-08-17 20:52:01 +03:00
# include <LibCore/StandardPaths.h>
2023-03-21 18:35:30 +03:00
# include <LibFileSystem/FileSystem.h>
2020-12-26 01:56:44 +03:00
# include <LibGUI/FileIconProvider.h>
2020-08-17 21:06:21 +03:00
# include <LibGUI/InputBox.h>
2020-09-18 19:02:57 +03:00
# include <LibGUI/Label.h>
2020-07-14 03:58:21 +03:00
# include <LibGUI/MessageBox.h>
2020-09-24 12:31:24 +03:00
# include <LibGUI/ModelEditingDelegate.h>
2020-02-06 22:33:02 +03:00
# include <LibGUI/SortingProxyModel.h>
2020-08-17 20:52:01 +03:00
# include <serenity.h>
# include <spawn.h>
2019-05-09 05:56:52 +03:00
# include <stdio.h>
2019-06-07 12:48:03 +03:00
# include <unistd.h>
2019-05-09 05:56:52 +03:00
2020-09-17 16:26:00 +03:00
namespace FileManager {
2022-12-04 21:02:33 +03:00
void spawn_terminal ( DeprecatedString const & directory )
2021-07-08 12:22:44 +03:00
{
posix_spawn_file_actions_t spawn_actions ;
posix_spawn_file_actions_init ( & spawn_actions ) ;
posix_spawn_file_actions_addchdir ( & spawn_actions , directory . characters ( ) ) ;
pid_t pid ;
2021-06-17 13:23:12 +03:00
char const * argv [ ] = { " Terminal " , nullptr } ;
2021-07-08 12:22:44 +03:00
if ( ( errno = posix_spawn ( & pid , " /bin/Terminal " , & spawn_actions , nullptr , const_cast < char * * > ( argv ) , environ ) ) ) {
perror ( " posix_spawn " ) ;
} else {
if ( disown ( pid ) < 0 )
perror ( " disown " ) ;
}
posix_spawn_file_actions_destroy ( & spawn_actions ) ;
}
2021-06-17 13:23:12 +03:00
NonnullRefPtr < GUI : : Action > LauncherHandler : : create_launch_action ( Function < void ( LauncherHandler const & ) > launch_handler )
2020-07-14 03:58:21 +03:00
{
2020-12-28 12:04:18 +03:00
auto icon = GUI : : FileIconProvider : : icon_for_executable ( details ( ) . executable ) . bitmap_for_size ( 16 ) ;
2020-07-14 03:58:21 +03:00
return GUI : : Action : : create ( details ( ) . name , move ( icon ) , [ this , launch_handler = move ( launch_handler ) ] ( auto & ) {
launch_handler ( * this ) ;
} ) ;
}
2023-03-06 16:17:01 +03:00
RefPtr < LauncherHandler > DirectoryView : : get_default_launch_handler ( Vector < NonnullRefPtr < LauncherHandler > > const & handlers )
2020-07-14 03:58:21 +03:00
{
2020-07-14 18:36:00 +03:00
// If this is an application, pick it first
for ( size_t i = 0 ; i < handlers . size ( ) ; i + + ) {
2023-03-06 16:17:01 +03:00
if ( handlers [ i ] - > details ( ) . launcher_type = = Desktop : : Launcher : : LauncherType : : Application )
2020-07-14 18:36:00 +03:00
return handlers [ i ] ;
}
2020-07-14 03:58:21 +03:00
// If there's a handler preferred by the user, pick this first
for ( size_t i = 0 ; i < handlers . size ( ) ; i + + ) {
2023-03-06 16:17:01 +03:00
if ( handlers [ i ] - > details ( ) . launcher_type = = Desktop : : Launcher : : LauncherType : : UserPreferred )
2020-07-14 03:58:21 +03:00
return handlers [ i ] ;
}
// Otherwise, use the user's default, if available
for ( size_t i = 0 ; i < handlers . size ( ) ; i + + ) {
2023-03-06 16:17:01 +03:00
if ( handlers [ i ] - > details ( ) . launcher_type = = Desktop : : Launcher : : LauncherType : : UserDefault )
2020-07-14 03:58:21 +03:00
return handlers [ i ] ;
}
// If still no match, use the first one we find
if ( ! handlers . is_empty ( ) ) {
return handlers [ 0 ] ;
}
return { } ;
}
2023-03-06 16:17:01 +03:00
Vector < NonnullRefPtr < LauncherHandler > > DirectoryView : : get_launch_handlers ( URL const & url )
2020-07-14 03:58:21 +03:00
{
2023-03-06 16:17:01 +03:00
Vector < NonnullRefPtr < LauncherHandler > > handlers ;
2020-07-14 03:58:21 +03:00
for ( auto & h : Desktop : : Launcher : : get_handlers_with_details_for_url ( url ) ) {
2021-04-23 17:46:57 +03:00
handlers . append ( adopt_ref ( * new LauncherHandler ( h ) ) ) ;
2020-07-14 03:58:21 +03:00
}
return handlers ;
}
2023-03-06 16:17:01 +03:00
Vector < NonnullRefPtr < LauncherHandler > > DirectoryView : : get_launch_handlers ( DeprecatedString const & path )
2020-07-14 03:58:21 +03:00
{
2022-09-29 02:30:58 +03:00
return get_launch_handlers ( URL : : create_with_file_scheme ( path ) ) ;
2020-07-14 03:58:21 +03:00
}
2021-06-17 13:23:12 +03:00
void DirectoryView : : handle_activation ( GUI : : ModelIndex const & index )
2019-05-09 05:56:52 +03:00
{
if ( ! index . is_valid ( ) )
return ;
2021-08-11 18:42:03 +03:00
2020-08-17 23:49:52 +03:00
auto & node = this - > node ( index ) ;
2020-08-17 23:02:21 +03:00
auto path = node . full_path ( ) ;
2020-01-28 00:11:26 +03:00
struct stat st ;
if ( stat ( path . characters ( ) , & st ) < 0 ) {
perror ( " stat " ) ;
2022-12-14 18:43:05 +03:00
auto error_message = DeprecatedString : : formatted ( " Could not stat {}: {} " , path , strerror ( errno ) ) ;
GUI : : MessageBox : : show ( window ( ) , error_message , " File Manager " sv , GUI : : MessageBox : : Type : : Error ) ;
2020-01-28 00:11:26 +03:00
return ;
}
if ( S_ISDIR ( st . st_mode ) ) {
2020-08-17 20:52:01 +03:00
if ( is_desktop ( ) ) {
2022-09-29 02:30:58 +03:00
Desktop : : Launcher : : open ( URL : : create_with_file_scheme ( path ) ) ;
2020-08-17 20:52:01 +03:00
return ;
}
2019-07-15 07:49:28 +03:00
open ( path ) ;
2019-05-09 05:56:52 +03:00
return ;
}
2020-01-28 00:11:26 +03:00
2022-09-29 02:30:58 +03:00
auto url = URL : : create_with_file_scheme ( path ) ;
2020-07-14 03:58:21 +03:00
auto launcher_handlers = get_launch_handlers ( url ) ;
auto default_launcher = get_default_launch_handler ( launcher_handlers ) ;
2021-06-27 18:11:39 +03:00
2020-08-17 20:52:01 +03:00
if ( default_launcher ) {
2021-06-27 18:11:39 +03:00
auto launch_origin_rect = current_view ( ) . to_widget_rect ( current_view ( ) . content_rect ( index ) ) . translated ( current_view ( ) . screen_relative_rect ( ) . location ( ) ) ;
2022-12-04 21:02:33 +03:00
setenv ( " __libgui_launch_origin_rect " , DeprecatedString : : formatted ( " {},{},{},{} " , launch_origin_rect . x ( ) , launch_origin_rect . y ( ) , launch_origin_rect . width ( ) , launch_origin_rect . height ( ) ) . characters ( ) , 1 ) ;
2020-08-17 20:52:01 +03:00
launch ( url , * default_launcher ) ;
2021-06-27 18:11:39 +03:00
unsetenv ( " __libgui_launch_origin_rect " ) ;
2020-07-14 03:58:21 +03:00
} else {
2022-12-04 21:02:33 +03:00
auto error_message = DeprecatedString : : formatted ( " Could not open {} " , path ) ;
2022-07-11 20:32:29 +03:00
GUI : : MessageBox : : show ( window ( ) , error_message , " File Manager " sv , GUI : : MessageBox : : Type : : Error ) ;
2020-07-14 03:58:21 +03:00
}
2020-04-18 22:58:04 +03:00
}
2019-03-01 15:54:28 +03:00
2020-08-17 20:52:01 +03:00
DirectoryView : : DirectoryView ( Mode mode )
: m_mode ( mode )
2021-01-01 03:55:30 +03:00
, m_model ( GUI : : FileSystemModel : : create ( { } ) )
2021-12-24 15:12:04 +03:00
, m_sorting_model ( MUST ( GUI : : SortingProxyModel : : create ( m_model ) ) )
2019-03-01 15:54:28 +03:00
{
2019-03-23 05:53:51 +03:00
set_active_widget ( nullptr ) ;
2021-09-14 22:08:57 +03:00
set_grabbable_margins ( 2 ) ;
2019-03-23 05:53:51 +03:00
2020-08-17 21:06:21 +03:00
setup_actions ( ) ;
2020-09-18 19:02:57 +03:00
m_error_label = add < GUI : : Label > ( ) ;
2020-12-31 03:49:05 +03:00
m_error_label - > set_font ( m_error_label - > font ( ) . bold_variant ( ) ) ;
2020-09-18 19:02:57 +03:00
2020-08-17 20:52:01 +03:00
setup_model ( ) ;
2020-01-10 19:17:14 +03:00
2020-08-17 20:52:01 +03:00
setup_icon_view ( ) ;
if ( mode ! = Mode : : Desktop ) {
setup_columns_view ( ) ;
setup_table_view ( ) ;
}
2019-03-23 05:53:51 +03:00
2020-08-17 20:52:01 +03:00
set_view_mode ( ViewMode : : Icon ) ;
}
2019-03-23 05:53:51 +03:00
2021-06-17 13:23:12 +03:00
GUI : : FileSystemModel : : Node const & DirectoryView : : node ( GUI : : ModelIndex const & index ) const
2020-08-17 23:49:52 +03:00
{
return model ( ) . node ( m_sorting_model - > map_to_source ( index ) ) ;
}
2020-08-17 20:52:01 +03:00
void DirectoryView : : setup_model ( )
{
2021-06-17 13:23:12 +03:00
m_model - > on_directory_change_error = [ this ] ( int , char const * error_string ) {
2020-09-18 19:02:57 +03:00
auto failed_path = m_model - > root_path ( ) ;
2022-12-04 21:02:33 +03:00
auto error_message = DeprecatedString : : formatted ( " Could not read {}: \n {} " , failed_path , error_string ) ;
2020-09-18 19:02:57 +03:00
m_error_label - > set_text ( error_message ) ;
set_active_widget ( m_error_label ) ;
2020-09-18 19:24:49 +03:00
m_mkdir_action - > set_enabled ( false ) ;
m_touch_action - > set_enabled ( false ) ;
add_path_to_history ( model ( ) . root_path ( ) ) ;
if ( on_path_change )
2021-03-07 18:04:59 +03:00
on_path_change ( failed_path , false , false ) ;
2020-04-21 22:53:13 +03:00
} ;
2021-06-17 13:23:12 +03:00
m_model - > on_rename_error = [ this ] ( int , char const * error_string ) {
2022-12-04 21:02:33 +03:00
GUI : : MessageBox : : show_error ( window ( ) , DeprecatedString : : formatted ( " Unable to rename file: {} " , error_string ) ) ;
2021-07-16 22:11:39 +03:00
} ;
2020-04-21 22:53:13 +03:00
m_model - > on_complete = [ this ] {
2020-08-17 20:52:01 +03:00
if ( m_table_view )
m_table_view - > selection ( ) . clear ( ) ;
if ( m_icon_view )
m_icon_view - > selection ( ) . clear ( ) ;
2020-04-21 22:53:13 +03:00
add_path_to_history ( model ( ) . root_path ( ) ) ;
2020-08-17 21:06:21 +03:00
bool can_write_in_path = access ( model ( ) . root_path ( ) . characters ( ) , W_OK ) = = 0 ;
m_mkdir_action - > set_enabled ( can_write_in_path ) ;
2020-08-17 21:10:19 +03:00
m_touch_action - > set_enabled ( can_write_in_path ) ;
2020-08-17 21:06:21 +03:00
2019-08-20 20:43:12 +03:00
if ( on_path_change )
2021-03-07 18:04:59 +03:00
on_path_change ( model ( ) . root_path ( ) , true , can_write_in_path ) ;
2019-03-23 05:53:51 +03:00
} ;
2022-08-30 22:57:30 +03:00
m_model - > on_root_path_removed = [ this ] {
// Change model root to the first existing parent directory.
LexicalPath model_root ( model ( ) . root_path ( ) ) ;
while ( model_root . string ( ) ! = " / " ) {
model_root = model_root . parent ( ) ;
2023-03-21 18:35:30 +03:00
if ( FileSystem : : is_directory ( model_root . string ( ) ) )
2022-08-30 22:57:30 +03:00
break ;
}
open ( model_root . string ( ) ) ;
} ;
2020-07-11 15:47:26 +03:00
m_model - > register_client ( * this ) ;
2019-09-13 23:00:47 +03:00
2019-06-07 12:48:03 +03:00
m_model - > on_thumbnail_progress = [ this ] ( int done , int total ) {
2019-03-25 06:25:25 +03:00
if ( on_thumbnail_progress )
on_thumbnail_progress ( done , total ) ;
} ;
2020-10-22 19:26:59 +03:00
if ( is_desktop ( ) )
m_model - > set_root_path ( Core : : StandardPaths : : desktop_directory ( ) ) ;
2020-08-17 20:52:01 +03:00
}
void DirectoryView : : setup_icon_view ( )
{
m_icon_view = add < GUI : : IconView > ( ) ;
2021-01-01 04:03:58 +03:00
m_icon_view - > set_should_hide_unnecessary_scrollbars ( true ) ;
2020-12-28 22:14:17 +03:00
m_icon_view - > set_selection_mode ( GUI : : AbstractView : : SelectionMode : : MultiSelection ) ;
2020-09-24 12:31:24 +03:00
m_icon_view - > set_editable ( true ) ;
m_icon_view - > set_edit_triggers ( GUI : : AbstractView : : EditTrigger : : EditKeyPressed ) ;
m_icon_view - > aid_create_editing_delegate = [ ] ( auto & ) {
return make < GUI : : StringModelEditingDelegate > ( ) ;
} ;
2020-08-17 20:52:01 +03:00
if ( is_desktop ( ) ) {
m_icon_view - > set_frame_shape ( Gfx : : FrameShape : : NoFrame ) ;
2021-07-07 19:09:35 +03:00
m_icon_view - > set_frame_thickness ( 0 ) ;
2020-08-17 20:52:01 +03:00
m_icon_view - > set_scrollbars_enabled ( false ) ;
m_icon_view - > set_fill_with_background_color ( false ) ;
2020-12-16 14:16:14 +03:00
m_icon_view - > set_draw_item_text_with_shadow ( true ) ;
2020-12-27 16:45:22 +03:00
m_icon_view - > set_flow_direction ( GUI : : IconView : : FlowDirection : : TopToBottom ) ;
2022-02-03 18:01:24 +03:00
m_icon_view - > set_accepts_command_palette ( false ) ;
2020-08-17 20:52:01 +03:00
}
2019-03-25 06:25:25 +03:00
2020-08-17 20:52:01 +03:00
m_icon_view - > set_model ( m_sorting_model ) ;
m_icon_view - > set_model_column ( GUI : : FileSystemModel : : Column : : Name ) ;
2020-08-15 21:05:57 +03:00
m_icon_view - > on_activation = [ & ] ( auto & index ) {
2020-08-17 23:49:52 +03:00
handle_activation ( index ) ;
2019-05-09 05:56:52 +03:00
} ;
2020-08-17 20:52:01 +03:00
m_icon_view - > on_selection_change = [ this ] {
2020-09-13 22:38:44 +03:00
handle_selection_change ( ) ;
2020-08-17 20:52:01 +03:00
} ;
m_icon_view - > on_context_menu_request = [ this ] ( auto & index , auto & event ) {
if ( on_context_menu_request )
2020-08-17 23:49:52 +03:00
on_context_menu_request ( index , event ) ;
2020-08-17 20:52:01 +03:00
} ;
m_icon_view - > on_drop = [ this ] ( auto & index , auto & event ) {
2020-09-17 15:30:00 +03:00
handle_drop ( index , event ) ;
2020-08-17 20:52:01 +03:00
} ;
}
void DirectoryView : : setup_columns_view ( )
{
m_columns_view = add < GUI : : ColumnsView > ( ) ;
2021-01-01 04:03:58 +03:00
m_columns_view - > set_should_hide_unnecessary_scrollbars ( true ) ;
2020-12-28 22:14:17 +03:00
m_columns_view - > set_selection_mode ( GUI : : AbstractView : : SelectionMode : : MultiSelection ) ;
2020-09-24 12:31:24 +03:00
m_columns_view - > set_editable ( true ) ;
m_columns_view - > set_edit_triggers ( GUI : : AbstractView : : EditTrigger : : EditKeyPressed ) ;
m_columns_view - > aid_create_editing_delegate = [ ] ( auto & ) {
return make < GUI : : StringModelEditingDelegate > ( ) ;
} ;
2020-08-17 20:52:01 +03:00
m_columns_view - > set_model ( m_sorting_model ) ;
m_columns_view - > set_model_column ( GUI : : FileSystemModel : : Column : : Name ) ;
2020-08-15 21:05:57 +03:00
m_columns_view - > on_activation = [ & ] ( auto & index ) {
2020-08-17 23:49:52 +03:00
handle_activation ( index ) ;
2020-01-10 19:17:14 +03:00
} ;
2020-08-17 20:52:01 +03:00
m_columns_view - > on_selection_change = [ this ] {
2020-09-13 22:38:44 +03:00
handle_selection_change ( ) ;
2020-08-17 20:52:01 +03:00
} ;
m_columns_view - > on_context_menu_request = [ this ] ( auto & index , auto & event ) {
if ( on_context_menu_request )
2020-08-17 23:49:52 +03:00
on_context_menu_request ( index , event ) ;
2020-08-17 20:52:01 +03:00
} ;
m_columns_view - > on_drop = [ this ] ( auto & index , auto & event ) {
2020-09-17 15:30:00 +03:00
handle_drop ( index , event ) ;
2020-08-17 20:52:01 +03:00
} ;
}
void DirectoryView : : setup_table_view ( )
{
m_table_view = add < GUI : : TableView > ( ) ;
2021-01-01 04:03:58 +03:00
m_table_view - > set_should_hide_unnecessary_scrollbars ( true ) ;
2020-12-28 22:14:17 +03:00
m_table_view - > set_selection_mode ( GUI : : AbstractView : : SelectionMode : : MultiSelection ) ;
2020-09-24 12:31:24 +03:00
m_table_view - > set_editable ( true ) ;
m_table_view - > set_edit_triggers ( GUI : : AbstractView : : EditTrigger : : EditKeyPressed ) ;
m_table_view - > aid_create_editing_delegate = [ ] ( auto & ) {
return make < GUI : : StringModelEditingDelegate > ( ) ;
} ;
2020-08-17 20:52:01 +03:00
2020-09-24 12:31:24 +03:00
m_table_view - > set_model ( m_sorting_model ) ;
2020-08-17 20:52:01 +03:00
m_table_view - > set_key_column_and_sort_order ( GUI : : FileSystemModel : : Column : : Name , GUI : : SortOrder : : Ascending ) ;
2021-08-31 02:13:30 +03:00
m_table_view - > set_column_visible ( GUI : : FileSystemModel : : Column : : Inode , false ) ;
m_table_view - > set_column_visible ( GUI : : FileSystemModel : : Column : : SymlinkTarget , false ) ;
2019-06-07 12:48:03 +03:00
m_table_view - > on_activation = [ & ] ( auto & index ) {
2020-08-17 23:49:52 +03:00
handle_activation ( index ) ;
2019-05-09 05:56:52 +03:00
} ;
2019-09-12 19:59:13 +03:00
m_table_view - > on_selection_change = [ this ] {
2020-09-13 22:38:44 +03:00
handle_selection_change ( ) ;
2019-09-10 16:37:55 +03:00
} ;
2019-09-13 23:00:47 +03:00
m_table_view - > on_context_menu_request = [ this ] ( auto & index , auto & event ) {
if ( on_context_menu_request )
2020-08-17 23:49:52 +03:00
on_context_menu_request ( index , event ) ;
2019-09-13 23:00:47 +03:00
} ;
2020-02-13 23:55:05 +03:00
m_table_view - > on_drop = [ this ] ( auto & index , auto & event ) {
2020-09-17 15:30:00 +03:00
handle_drop ( index , event ) ;
2020-02-13 23:55:05 +03:00
} ;
2019-03-01 15:54:28 +03:00
}
2019-03-23 05:53:51 +03:00
DirectoryView : : ~ DirectoryView ( )
2019-03-01 15:54:28 +03:00
{
2020-07-11 15:47:26 +03:00
m_model - > unregister_client ( * this ) ;
}
2020-08-13 21:06:14 +03:00
void DirectoryView : : model_did_update ( unsigned flags )
2020-07-11 15:47:26 +03:00
{
2021-04-29 23:23:52 +03:00
if ( flags & GUI : : Model : : UpdateFlag : : InvalidateAllIndices ) {
2020-07-11 15:47:26 +03:00
for_each_view_implementation ( [ ] ( auto & view ) {
view . selection ( ) . clear ( ) ;
} ) ;
}
update_statusbar ( ) ;
2019-03-01 15:54:28 +03:00
}
2022-12-04 21:02:33 +03:00
void DirectoryView : : set_view_mode_from_string ( DeprecatedString const & mode )
2021-08-26 20:14:12 +03:00
{
if ( m_mode = = Mode : : Desktop )
return ;
2022-07-11 20:32:29 +03:00
if ( mode . contains ( " Table " sv ) ) {
2021-08-26 20:14:12 +03:00
set_view_mode ( DirectoryView : : ViewMode : : Table ) ;
m_view_as_table_action - > set_checked ( true ) ;
2022-07-11 20:32:29 +03:00
} else if ( mode . contains ( " Columns " sv ) ) {
2021-08-26 20:14:12 +03:00
set_view_mode ( DirectoryView : : ViewMode : : Columns ) ;
m_view_as_columns_action - > set_checked ( true ) ;
} else {
set_view_mode ( DirectoryView : : ViewMode : : Icon ) ;
m_view_as_icons_action - > set_checked ( true ) ;
}
}
2022-12-04 21:02:33 +03:00
void DirectoryView : : config_string_did_change ( DeprecatedString const & domain , DeprecatedString const & group , DeprecatedString const & key , DeprecatedString const & value )
2021-08-26 20:15:55 +03:00
{
if ( domain ! = " FileManager " | | group ! = " DirectoryView " )
return ;
if ( key = = " ViewMode " ) {
set_view_mode_from_string ( value ) ;
return ;
}
}
2019-03-23 05:53:51 +03:00
void DirectoryView : : set_view_mode ( ViewMode mode )
2019-03-01 15:54:28 +03:00
{
2019-03-23 05:53:51 +03:00
if ( m_view_mode = = mode )
return ;
m_view_mode = mode ;
update ( ) ;
2020-05-01 03:09:04 +03:00
if ( mode = = ViewMode : : Table ) {
2019-03-23 05:53:51 +03:00
set_active_widget ( m_table_view ) ;
return ;
}
2020-01-10 19:17:14 +03:00
if ( mode = = ViewMode : : Columns ) {
set_active_widget ( m_columns_view ) ;
return ;
}
2019-03-23 05:53:51 +03:00
if ( mode = = ViewMode : : Icon ) {
2020-05-01 03:09:04 +03:00
set_active_widget ( m_icon_view ) ;
2019-03-23 05:53:51 +03:00
return ;
}
2021-02-23 22:42:32 +03:00
VERIFY_NOT_REACHED ( ) ;
2019-03-01 15:54:28 +03:00
}
2022-12-04 21:02:33 +03:00
void DirectoryView : : add_path_to_history ( DeprecatedString path )
2019-05-24 00:15:57 +03:00
{
2020-04-21 22:53:13 +03:00
if ( m_path_history . size ( ) & & m_path_history . at ( m_path_history_position ) = = path )
return ;
2019-05-24 00:15:57 +03:00
if ( m_path_history_position < m_path_history . size ( ) )
m_path_history . resize ( m_path_history_position + 1 ) ;
2021-04-17 01:46:13 +03:00
m_path_history . append ( move ( path ) ) ;
2019-05-24 00:15:57 +03:00
m_path_history_position = m_path_history . size ( ) - 1 ;
}
2022-12-04 21:02:33 +03:00
bool DirectoryView : : open ( DeprecatedString const & path )
2019-03-01 15:54:28 +03:00
{
2023-03-24 12:56:53 +03:00
auto error_or_real_path = FileSystem : : real_path ( path ) ;
if ( error_or_real_path . is_error ( ) | | ! FileSystem : : is_directory ( path ) )
2021-08-11 18:43:52 +03:00
return false ;
2021-01-24 02:43:33 +03:00
2023-03-24 12:56:53 +03:00
auto real_path = error_or_real_path . release_value ( ) ;
if ( Core : : System : : chdir ( real_path ) . is_error ( ) ) {
2021-08-27 00:40:58 +03:00
perror ( " chdir " ) ;
2023-03-24 12:56:53 +03:00
return false ;
2021-08-27 00:40:58 +03:00
}
2023-03-24 12:56:53 +03:00
if ( model ( ) . root_path ( ) = = real_path . to_deprecated_string ( ) ) {
2021-08-11 18:43:52 +03:00
refresh ( ) ;
} else {
set_active_widget ( & current_view ( ) ) ;
2023-03-24 12:56:53 +03:00
model ( ) . set_root_path ( real_path . to_deprecated_string ( ) ) ;
2020-04-19 19:21:02 +03:00
}
2021-08-11 18:43:52 +03:00
return true ;
2019-03-01 15:54:28 +03:00
}
2021-11-11 02:55:02 +03:00
void DirectoryView : : set_status_message ( StringView message )
2019-03-01 15:54:28 +03:00
{
if ( on_status_message )
on_status_message ( message ) ;
}
2019-03-02 11:16:57 +03:00
2019-03-23 05:53:51 +03:00
void DirectoryView : : open_parent_directory ( )
2019-03-02 11:16:57 +03:00
{
2021-08-11 18:43:52 +03:00
open ( " .. " ) ;
2019-03-02 11:16:57 +03:00
}
2019-03-21 00:31:21 +03:00
2019-03-23 05:53:51 +03:00
void DirectoryView : : refresh ( )
2019-03-21 00:31:21 +03:00
{
2021-05-25 17:13:19 +03:00
model ( ) . invalidate ( ) ;
2019-03-21 00:31:21 +03:00
}
2019-05-24 00:15:57 +03:00
void DirectoryView : : open_previous_directory ( )
{
2021-08-11 18:43:52 +03:00
if ( m_path_history_position > 0 )
open ( m_path_history [ - - m_path_history_position ] ) ;
2019-05-24 00:15:57 +03:00
}
void DirectoryView : : open_next_directory ( )
{
2021-08-11 18:43:52 +03:00
if ( m_path_history_position < m_path_history . size ( ) - 1 )
open ( m_path_history [ + + m_path_history_position ] ) ;
2019-05-24 00:15:57 +03:00
}
2019-09-12 19:59:13 +03:00
void DirectoryView : : update_statusbar ( )
{
2020-10-22 19:52:54 +03:00
// If we're triggered during widget construction, just ignore it.
if ( m_view_mode = = ViewMode : : Invalid )
return ;
2021-04-18 01:48:07 +03:00
StringBuilder builder ;
2019-09-12 19:59:13 +03:00
if ( current_view ( ) . selection ( ) . is_empty ( ) ) {
2021-04-18 01:48:07 +03:00
int total_item_count = model ( ) . row_count ( ) ;
size_t total_size = model ( ) . node ( { } ) . total_size ;
builder . appendff ( " {} item{} ({}) " , total_item_count , total_item_count ! = 1 ? " s " : " " , human_readable_size ( total_size ) ) ;
set_status_message ( builder . string_view ( ) ) ;
2019-09-12 19:59:13 +03:00
return ;
}
int selected_item_count = current_view ( ) . selection ( ) . size ( ) ;
size_t selected_byte_count = 0 ;
current_view ( ) . selection ( ) . for_each_index ( [ & ] ( auto & index ) {
2021-06-17 13:23:12 +03:00
auto const & node = this - > node ( index ) ;
2021-03-26 00:01:29 +03:00
selected_byte_count + = node . size ;
2019-09-12 19:59:13 +03:00
} ) ;
2021-04-18 01:48:07 +03:00
builder . appendff ( " {} item{} selected ({}) " , selected_item_count , selected_item_count ! = 1 ? " s " : " " , human_readable_size ( selected_byte_count ) ) ;
2020-01-28 00:36:06 +03:00
if ( selected_item_count = = 1 ) {
2020-08-17 23:49:52 +03:00
auto & node = this - > node ( current_view ( ) . selection ( ) . first ( ) ) ;
2020-01-28 00:36:06 +03:00
if ( ! node . symlink_target . is_empty ( ) ) {
2022-07-11 20:32:29 +03:00
builder . append ( " → " sv ) ;
2020-01-28 00:36:06 +03:00
builder . append ( node . symlink_target ) ;
}
}
2021-04-18 01:48:07 +03:00
set_status_message ( builder . string_view ( ) ) ;
2019-09-12 19:59:13 +03:00
}
2020-08-12 21:27:23 +03:00
void DirectoryView : : set_should_show_dotfiles ( bool show_dotfiles )
{
m_model - > set_should_show_dotfiles ( show_dotfiles ) ;
}
2020-08-17 20:52:01 +03:00
2021-06-17 13:23:12 +03:00
void DirectoryView : : launch ( URL const & , LauncherHandler const & launcher_handler ) const
2020-08-17 20:52:01 +03:00
{
pid_t child ;
2022-06-16 12:44:30 +03:00
posix_spawnattr_t spawn_attributes ;
posix_spawnattr_init ( & spawn_attributes ) ;
posix_spawnattr_setpgroup ( & spawn_attributes , getsid ( 0 ) ) ;
short current_flag ;
posix_spawnattr_getflags ( & spawn_attributes , & current_flag ) ;
posix_spawnattr_setflags ( & spawn_attributes , static_cast < short > ( current_flag | POSIX_SPAWN_SETPGROUP ) ) ;
2020-08-17 20:52:01 +03:00
if ( launcher_handler . details ( ) . launcher_type = = Desktop : : Launcher : : LauncherType : : Application ) {
2021-08-02 16:21:43 +03:00
posix_spawn_file_actions_t spawn_actions ;
posix_spawn_file_actions_init ( & spawn_actions ) ;
posix_spawn_file_actions_addchdir ( & spawn_actions , path ( ) . characters ( ) ) ;
2021-06-17 13:23:12 +03:00
char const * argv [ ] = { launcher_handler . details ( ) . name . characters ( ) , nullptr } ;
2022-11-04 10:10:42 +03:00
errno = posix_spawn ( & child , launcher_handler . details ( ) . executable . characters ( ) , & spawn_actions , & spawn_attributes , const_cast < char * * > ( argv ) , environ ) ;
if ( errno ) {
perror ( " posix_spawn " ) ;
} else if ( disown ( child ) < 0 ) {
2020-08-17 20:52:01 +03:00
perror ( " disown " ) ;
2022-11-04 10:10:42 +03:00
}
2021-08-02 16:21:43 +03:00
posix_spawn_file_actions_destroy ( & spawn_actions ) ;
2020-08-17 20:52:01 +03:00
} else {
for ( auto & path : selected_file_paths ( ) ) {
2021-06-17 13:23:12 +03:00
char const * argv [ ] = { launcher_handler . details ( ) . name . characters ( ) , path . characters ( ) , nullptr } ;
2022-11-04 10:10:42 +03:00
if ( ( errno = posix_spawn ( & child , launcher_handler . details ( ) . executable . characters ( ) , nullptr , & spawn_attributes , const_cast < char * * > ( argv ) , environ ) ) )
continue ;
2020-08-17 20:52:01 +03:00
if ( disown ( child ) < 0 )
perror ( " disown " ) ;
}
}
}
2022-12-04 21:02:33 +03:00
Vector < DeprecatedString > DirectoryView : : selected_file_paths ( ) const
2020-08-17 20:52:01 +03:00
{
2022-12-04 21:02:33 +03:00
Vector < DeprecatedString > paths ;
2020-08-17 20:52:01 +03:00
auto & view = current_view ( ) ;
auto & model = * view . model ( ) ;
2021-06-17 13:23:12 +03:00
view . selection ( ) . for_each_index ( [ & ] ( GUI : : ModelIndex const & index ) {
2020-08-17 20:52:01 +03:00
auto parent_index = model . parent_index ( index ) ;
auto name_index = model . index ( index . row ( ) , GUI : : FileSystemModel : : Column : : Name , parent_index ) ;
2022-12-06 04:12:49 +03:00
auto path = name_index . data ( GUI : : ModelRole : : Custom ) . to_deprecated_string ( ) ;
2020-08-17 20:52:01 +03:00
paths . append ( path ) ;
} ) ;
return paths ;
}
2020-08-17 21:06:21 +03:00
2020-09-13 22:38:44 +03:00
void DirectoryView : : do_delete ( bool should_confirm )
{
auto paths = selected_file_paths ( ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( ! paths . is_empty ( ) ) ;
2021-06-17 16:41:40 +03:00
delete_paths ( paths , should_confirm , window ( ) ) ;
2022-12-16 17:43:57 +03:00
current_view ( ) . selection ( ) . clear ( ) ;
2020-09-13 22:38:44 +03:00
}
2022-01-05 06:45:55 +03:00
bool DirectoryView : : can_modify_current_selection ( )
{
2021-10-09 18:55:55 +03:00
auto selections = current_view ( ) . selection ( ) . indices ( ) ;
// FIXME: remove once Clang formats this properly.
// clang-format off
return selections . first_matching ( [ & ] ( auto & index ) {
2023-03-21 18:35:30 +03:00
return FileSystem : : can_delete_or_move ( node ( index ) . full_path ( ) ) ;
2021-10-09 18:55:55 +03:00
} ) . has_value ( ) ;
// clang-format on
2022-01-05 06:45:55 +03:00
}
2020-09-13 22:38:44 +03:00
void DirectoryView : : handle_selection_change ( )
{
update_statusbar ( ) ;
2022-01-05 06:45:55 +03:00
bool can_modify = can_modify_current_selection ( ) ;
2021-07-13 01:24:48 +03:00
m_delete_action - > set_enabled ( can_modify ) ;
m_force_delete_action - > set_enabled ( can_modify ) ;
2022-02-18 23:04:38 +03:00
m_rename_action - > set_enabled ( can_modify ) ;
2020-09-13 22:38:44 +03:00
if ( on_selection_change )
2020-12-01 08:32:35 +03:00
on_selection_change ( current_view ( ) ) ;
2020-09-13 22:38:44 +03:00
}
2020-08-17 21:06:21 +03:00
void DirectoryView : : setup_actions ( )
{
2023-01-20 22:06:05 +03:00
m_mkdir_action = GUI : : Action : : create ( " &New Directory... " , { Mod_Ctrl | Mod_Shift , Key_N } , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/mkdir.png " sv ) . release_value_but_fixme_should_propagate_errors ( ) , [ & ] ( GUI : : Action const & ) {
2022-12-04 21:02:33 +03:00
DeprecatedString value ;
2022-12-30 23:13:54 +03:00
if ( GUI : : InputBox : : show ( window ( ) , value , " Enter name: " sv , " New directory " sv , GUI : : InputType : : NonemptyText ) = = GUI : : InputBox : : ExecResult : : OK ) {
2022-12-04 21:02:33 +03:00
auto new_dir_path = LexicalPath : : canonicalized_path ( DeprecatedString : : formatted ( " {}/{} " , path ( ) , value ) ) ;
2020-08-17 21:06:21 +03:00
int rc = mkdir ( new_dir_path . characters ( ) , 0777 ) ;
if ( rc < 0 ) {
auto saved_errno = errno ;
2022-12-04 21:02:33 +03:00
GUI : : MessageBox : : show ( window ( ) , DeprecatedString : : formatted ( " mkdir( \" {} \" ) failed: {} " , new_dir_path , strerror ( saved_errno ) ) , " Error " sv , GUI : : MessageBox : : Type : : Error ) ;
2020-08-17 21:06:21 +03:00
}
}
} ) ;
2020-08-17 21:10:19 +03:00
2023-01-20 22:06:05 +03:00
m_touch_action = GUI : : Action : : create ( " New &File... " , { Mod_Ctrl | Mod_Shift , Key_F } , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/new.png " sv ) . release_value_but_fixme_should_propagate_errors ( ) , [ & ] ( GUI : : Action const & ) {
2022-12-04 21:02:33 +03:00
DeprecatedString value ;
2022-12-30 23:13:54 +03:00
if ( GUI : : InputBox : : show ( window ( ) , value , " Enter name: " sv , " New file " sv , GUI : : InputType : : NonemptyText ) = = GUI : : InputBox : : ExecResult : : OK ) {
2022-12-04 21:02:33 +03:00
auto new_file_path = LexicalPath : : canonicalized_path ( DeprecatedString : : formatted ( " {}/{} " , path ( ) , value ) ) ;
2020-08-17 21:10:19 +03:00
struct stat st ;
int rc = stat ( new_file_path . characters ( ) , & st ) ;
if ( ( rc < 0 & & errno ! = ENOENT ) ) {
auto saved_errno = errno ;
2022-12-04 21:02:33 +03:00
GUI : : MessageBox : : show ( window ( ) , DeprecatedString : : formatted ( " stat( \" {} \" ) failed: {} " , new_file_path , strerror ( saved_errno ) ) , " Error " sv , GUI : : MessageBox : : Type : : Error ) ;
2020-08-17 21:10:19 +03:00
return ;
}
if ( rc = = 0 ) {
2022-12-04 21:02:33 +03:00
GUI : : MessageBox : : show ( window ( ) , DeprecatedString : : formatted ( " {}: Already exists " , new_file_path ) , " Error " sv , GUI : : MessageBox : : Type : : Error ) ;
2020-08-17 21:10:19 +03:00
return ;
}
int fd = creat ( new_file_path . characters ( ) , 0666 ) ;
if ( fd < 0 ) {
auto saved_errno = errno ;
2022-12-04 21:02:33 +03:00
GUI : : MessageBox : : show ( window ( ) , DeprecatedString : : formatted ( " creat( \" {} \" ) failed: {} " , new_file_path , strerror ( saved_errno ) ) , " Error " sv , GUI : : MessageBox : : Type : : Error ) ;
2020-08-17 21:10:19 +03:00
return ;
}
rc = close ( fd ) ;
2021-02-23 22:42:32 +03:00
VERIFY ( rc > = 0 ) ;
2020-08-17 21:10:19 +03:00
}
} ) ;
2020-09-13 15:58:23 +03:00
2023-01-20 22:06:05 +03:00
m_open_terminal_action = GUI : : Action : : create ( " Open &Terminal Here " , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/app-terminal.png " sv ) . release_value_but_fixme_should_propagate_errors ( ) , [ & ] ( auto & ) {
2021-07-08 12:22:44 +03:00
spawn_terminal ( path ( ) ) ;
2020-09-13 15:58:23 +03:00
} ) ;
2020-09-13 22:38:44 +03:00
m_delete_action = GUI : : CommonActions : : make_delete_action ( [ this ] ( auto & ) { do_delete ( true ) ; } , window ( ) ) ;
2021-07-13 01:24:48 +03:00
m_rename_action = GUI : : CommonActions : : make_rename_action ( [ this ] ( auto & ) {
2022-01-05 06:45:55 +03:00
if ( can_modify_current_selection ( ) )
current_view ( ) . begin_editing ( current_view ( ) . cursor_index ( ) ) ;
2021-07-13 01:24:48 +03:00
} ,
window ( ) ) ;
2020-09-13 22:38:44 +03:00
m_force_delete_action = GUI : : Action : : create (
2021-04-09 17:44:08 +03:00
" Delete Without Confirmation " , { Mod_Shift , Key_Delete } ,
2020-09-13 22:38:44 +03:00
[ this ] ( auto & ) { do_delete ( false ) ; } ,
window ( ) ) ;
2021-08-26 20:14:12 +03:00
m_view_as_icons_action = GUI : : Action : : create_checkable (
2023-01-20 22:06:05 +03:00
" View as &Icons " , { Mod_Ctrl , KeyCode : : Key_1 } , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/icon-view.png " sv ) . release_value_but_fixme_should_propagate_errors ( ) , [ & ] ( GUI : : Action const & ) {
2021-08-26 20:14:12 +03:00
set_view_mode ( DirectoryView : : ViewMode : : Icon ) ;
2022-07-11 20:32:29 +03:00
Config : : write_string ( " FileManager " sv , " DirectoryView " sv , " ViewMode " sv , " Icon " sv ) ;
2021-08-26 20:14:12 +03:00
} ,
window ( ) ) ;
m_view_as_table_action = GUI : : Action : : create_checkable (
2023-01-20 22:06:05 +03:00
" View as &Table " , { Mod_Ctrl , KeyCode : : Key_2 } , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/table-view.png " sv ) . release_value_but_fixme_should_propagate_errors ( ) , [ & ] ( GUI : : Action const & ) {
2021-08-26 20:14:12 +03:00
set_view_mode ( DirectoryView : : ViewMode : : Table ) ;
2022-07-11 20:32:29 +03:00
Config : : write_string ( " FileManager " sv , " DirectoryView " sv , " ViewMode " sv , " Table " sv ) ;
2021-08-26 20:14:12 +03:00
} ,
window ( ) ) ;
m_view_as_columns_action = GUI : : Action : : create_checkable (
2023-01-20 22:06:05 +03:00
" View as &Columns " , { Mod_Ctrl , KeyCode : : Key_3 } , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/columns-view.png " sv ) . release_value_but_fixme_should_propagate_errors ( ) , [ & ] ( GUI : : Action const & ) {
2021-08-26 20:14:12 +03:00
set_view_mode ( DirectoryView : : ViewMode : : Columns ) ;
2022-07-11 20:32:29 +03:00
Config : : write_string ( " FileManager " sv , " DirectoryView " sv , " ViewMode " sv , " Columns " sv ) ;
2021-08-26 20:14:12 +03:00
} ,
window ( ) ) ;
2022-02-03 17:40:14 +03:00
if ( m_mode = = Mode : : Desktop ) {
m_view_as_icons_action - > set_enabled ( false ) ;
m_view_as_table_action - > set_enabled ( false ) ;
m_view_as_columns_action - > set_enabled ( false ) ;
}
2020-08-17 21:06:21 +03:00
}
2020-09-17 15:30:00 +03:00
2021-06-17 13:23:12 +03:00
void DirectoryView : : handle_drop ( GUI : : ModelIndex const & index , GUI : : DropEvent const & event )
2020-09-17 15:30:00 +03:00
{
2023-02-07 00:08:53 +03:00
auto const & target_node = node ( index ) ;
2021-06-17 14:36:06 +03:00
2023-02-07 00:08:53 +03:00
bool const has_accepted_drop = : : FileManager : : handle_drop ( event , target_node . full_path ( ) , window ( ) ) . release_value_but_fixme_should_propagate_errors ( ) ;
2021-06-17 14:36:06 +03:00
2023-02-07 00:08:53 +03:00
if ( has_accepted_drop & & on_accepted_drop )
2020-12-08 19:51:53 +03:00
on_accepted_drop ( ) ;
}
2020-09-17 16:26:00 +03:00
}