2020-01-18 05:38:21 -03:00
/*
2021-04-05 18:12:53 -03:00
* Copyright ( c ) 2018 - 2021 , Andreas Kling < kling @ serenityos . org >
2023-01-22 16:54:23 -03:00
* Copyright ( c ) 2021 - 2023 , Sam Atkins < atkinssj @ serenityos . org >
2021-08-27 11:55:27 -03:00
* Copyright ( c ) 2021 , Mustafa Quraish < mustafa @ cs . toronto . edu >
2023-01-25 16:05:26 -03:00
* Copyright ( c ) 2022 - 2023 , the SerenityOS developers .
2020-01-18 05:38:21 -03:00
*
2021-04-22 05:24:48 -03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 05:38:21 -03:00
*/
2020-09-17 09:34:47 -03:00
# include "DesktopWidget.h"
2019-06-07 06:48:03 -03:00
# include "DirectoryView.h"
2019-09-10 10:21:58 -03:00
# include "FileUtils.h"
2021-01-01 20:31:30 -03:00
# include "PropertiesWindow.h"
2020-05-26 08:52:44 -03:00
# include <AK/LexicalPath.h>
2019-09-10 10:21:58 -03:00
# include <AK/StringBuilder.h>
2022-12-22 03:22:05 -03:00
# include <AK/Try.h>
2020-02-13 17:55:05 -03:00
# include <AK/URL.h>
2020-12-20 08:17:59 -03:00
# include <Applications/FileManager/FileManagerWindowGML.h>
2021-08-25 14:39:30 -03:00
# include <LibConfig/Client.h>
2021-08-27 11:55:27 -03:00
# include <LibConfig/Listener.h>
2021-03-05 16:43:39 -03:00
# include <LibCore/ArgsParser.h>
2023-01-22 16:54:23 -03:00
# include <LibCore/Process.h>
2020-04-19 14:57:05 -03:00
# include <LibCore/StandardPaths.h>
2021-11-23 06:59:50 -03:00
# include <LibCore/System.h>
2020-04-26 16:30:01 -03:00
# include <LibDesktop/Launcher.h>
2023-03-21 12:35:30 -03:00
# include <LibFileSystem/FileSystem.h>
2023-03-21 12:36:18 -03:00
# include <LibFileSystem/TempFile.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/Action.h>
# include <LibGUI/ActionGroup.h>
# include <LibGUI/Application.h>
# include <LibGUI/BoxLayout.h>
# include <LibGUI/Clipboard.h>
2020-08-14 17:17:46 -03:00
# include <LibGUI/Desktop.h>
2020-09-16 15:57:41 -03:00
# include <LibGUI/FileIconProvider.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/FileSystemModel.h>
2021-12-27 14:16:37 -03:00
# include <LibGUI/InputBox.h>
2020-02-14 21:56:30 -03:00
# include <LibGUI/Menu.h>
2021-04-13 11:18:20 -03:00
# include <LibGUI/Menubar.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/MessageBox.h>
2020-04-18 17:04:41 -03:00
# include <LibGUI/Painter.h>
2023-02-06 14:30:38 -03:00
# include <LibGUI/PathBreadcrumbbar.h>
2021-04-13 11:18:20 -03:00
# include <LibGUI/Progressbar.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/Splitter.h>
2021-04-13 11:18:20 -03:00
# include <LibGUI/Statusbar.h>
# include <LibGUI/Toolbar.h>
# include <LibGUI/ToolbarContainer.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/TreeView.h>
# include <LibGUI/Widget.h>
# include <LibGUI/Window.h>
2020-04-18 17:04:41 -03:00
# include <LibGfx/Palette.h>
2021-11-22 19:25:57 -03:00
# include <LibMain/Main.h>
2020-08-10 15:29:08 -03:00
# include <pthread.h>
2019-03-01 11:47:07 -03:00
# include <signal.h>
2019-02-09 06:22:04 -02:00
# include <stdio.h>
2020-03-08 08:05:14 -03:00
# include <string.h>
2021-04-25 10:36:27 -03:00
# include <sys/wait.h>
2019-06-07 06:48:03 -03:00
# include <unistd.h>
2019-02-09 06:22:04 -02:00
2020-09-17 10:26:00 -03:00
using namespace FileManager ;
2021-11-24 18:26:18 -03:00
static ErrorOr < int > run_in_desktop_mode ( ) ;
2022-12-04 15:02:33 -03:00
static ErrorOr < int > run_in_windowed_mode ( DeprecatedString const & initial_location , DeprecatedString const & entry_focused_on_init ) ;
static void do_copy ( Vector < DeprecatedString > const & selected_file_paths , FileOperation file_operation ) ;
static void do_paste ( DeprecatedString const & target_directory , GUI : : Window * window ) ;
static void do_create_link ( Vector < DeprecatedString > const & selected_file_paths , GUI : : Window * window ) ;
static void do_create_archive ( Vector < DeprecatedString > const & selected_file_paths , GUI : : Window * window ) ;
static void do_set_wallpaper ( DeprecatedString const & file_path , GUI : : Window * window ) ;
static void do_unzip_archive ( Vector < DeprecatedString > const & selected_file_paths , GUI : : Window * window ) ;
static void show_properties ( DeprecatedString const & container_dir_path , DeprecatedString const & path , Vector < DeprecatedString > const & selected , GUI : : Window * window ) ;
2023-03-06 10:17:01 -03:00
static bool add_launch_handler_actions_to_menu ( RefPtr < GUI : : Menu > & menu , DirectoryView const & directory_view , DeprecatedString const & full_path , RefPtr < GUI : : Action > & default_action , Vector < NonnullRefPtr < LauncherHandler > > & current_file_launch_handlers ) ;
2020-04-18 17:04:41 -03:00
2021-11-22 19:25:57 -03:00
ErrorOr < int > serenity_main ( Main : : Arguments arguments )
2019-02-09 06:22:04 -02:00
{
2021-11-27 19:26:34 -03:00
TRY ( Core : : System : : pledge ( " stdio thread recvfd sendfd unix cpath rpath wpath fattr proc exec sigaction " ) ) ;
2020-01-11 18:13:38 -03:00
2021-11-22 19:25:57 -03:00
struct sigaction act = { } ;
2019-03-01 11:47:07 -03:00
act . sa_flags = SA_NOCLDWAIT ;
act . sa_handler = SIG_IGN ;
2021-11-23 06:59:50 -03:00
TRY ( Core : : System : : sigaction ( SIGCHLD , & act , nullptr ) ) ;
2019-03-01 11:47:07 -03:00
2021-03-05 16:43:39 -03:00
Core : : ArgsParser args_parser ;
2021-11-24 18:26:18 -03:00
bool is_desktop_mode { false } ;
bool is_selection_mode { false } ;
bool ignore_path_resolution { false } ;
2022-12-04 15:02:33 -03:00
DeprecatedString initial_location ;
2021-03-05 16:43:39 -03:00
args_parser . add_option ( is_desktop_mode , " Run in desktop mode " , " desktop " , ' d ' ) ;
args_parser . add_option ( is_selection_mode , " Show entry in parent folder " , " select " , ' s ' ) ;
args_parser . add_option ( ignore_path_resolution , " Use raw path, do not resolve real path " , " raw " , ' r ' ) ;
args_parser . add_positional_argument ( initial_location , " Path to open " , " path " , Core : : ArgsParser : : Required : : No ) ;
2021-11-22 21:03:05 -03:00
args_parser . parse ( arguments ) ;
2021-03-05 16:43:39 -03:00
2023-05-05 01:24:53 -03:00
auto app = TRY ( GUI : : Application : : create ( arguments ) ) ;
2019-02-09 06:22:04 -02:00
2021-11-27 19:26:34 -03:00
TRY ( Core : : System : : pledge ( " stdio thread recvfd sendfd cpath rpath wpath fattr proc exec unix " ) ) ;
2020-01-12 07:59:11 -03:00
2021-08-25 15:03:58 -03:00
Config : : pledge_domains ( { " FileManager " , " WindowManager " } ) ;
2021-08-26 14:15:55 -03:00
Config : : monitor_domain ( " FileManager " ) ;
2021-08-27 11:55:27 -03:00
Config : : monitor_domain ( " WindowManager " ) ;
2021-08-25 15:03:58 -03:00
2021-03-05 16:43:39 -03:00
if ( is_desktop_mode )
2021-08-25 14:39:30 -03:00
return run_in_desktop_mode ( ) ;
2020-04-18 17:04:41 -03:00
// our initial location is defined as, in order of precedence:
2021-03-05 16:43:39 -03:00
// 1. the command-line path argument (e.g. FileManager /bin)
2021-08-02 11:19:47 -03:00
// 2. the current directory
// 3. the user's home directory
// 4. the root directory
2020-04-18 17:04:41 -03:00
2023-03-17 21:15:16 -03:00
LexicalPath path ( initial_location ) ;
2021-03-05 16:43:39 -03:00
if ( ! initial_location . is_empty ( ) ) {
2023-03-24 06:56:53 -03:00
if ( auto error_or_path = FileSystem : : real_path ( initial_location ) ; ! ignore_path_resolution & & ! error_or_path . is_error ( ) )
initial_location = error_or_path . release_value ( ) . to_deprecated_string ( ) ;
2021-03-05 16:43:39 -03:00
2023-03-21 12:35:30 -03:00
if ( ! FileSystem : : is_directory ( initial_location ) ) {
2023-03-17 21:15:16 -03:00
// We want to extract zips to a temporary directory when FileManager is launched with a .zip file as its first argument
if ( path . has_extension ( " .zip " sv ) ) {
2023-03-21 12:36:18 -03:00
auto temp_directory = FileSystem : : TempFile : : create_temp_directory ( ) ;
2023-03-17 21:15:16 -03:00
if ( temp_directory . is_error ( ) ) {
dbgln ( " Failed to create temporary directory during zip extraction: {} " , temp_directory . error ( ) ) ;
GUI : : MessageBox : : show_error ( app - > active_window ( ) , " Failed to create temporary directory! " sv ) ;
return - 1 ;
}
auto temp_directory_path = temp_directory . value ( ) - > path ( ) ;
auto result = Core : : Process : : spawn ( " /bin/unzip " sv , Array { " -d " sv , temp_directory_path , initial_location } ) ;
if ( result . is_error ( ) ) {
dbgln ( " Failed to extract {} to {}: {} " , initial_location , temp_directory_path , result . error ( ) ) ;
auto message = TRY ( String : : formatted ( " Failed to extract {} to {} " , initial_location , temp_directory_path ) ) ;
GUI : : MessageBox : : show_error ( app - > active_window ( ) , message ) ;
return - 1 ;
}
return run_in_windowed_mode ( temp_directory_path . to_deprecated_string ( ) , path . basename ( ) ) ;
}
2021-03-05 16:43:39 -03:00
is_selection_mode = true ;
2023-03-17 21:15:16 -03:00
}
2020-04-30 17:38:03 -03:00
}
2020-04-18 17:04:41 -03:00
2023-03-24 06:56:53 -03:00
if ( auto error_or_cwd = FileSystem : : current_working_directory ( ) ; initial_location . is_empty ( ) & & ! error_or_cwd . is_error ( ) )
initial_location = error_or_cwd . release_value ( ) . to_deprecated_string ( ) ;
2021-08-02 11:19:47 -03:00
2020-04-18 17:04:41 -03:00
if ( initial_location . is_empty ( ) )
2020-04-19 14:57:05 -03:00
initial_location = Core : : StandardPaths : : home_directory ( ) ;
2020-04-18 17:04:41 -03:00
if ( initial_location . is_empty ( ) )
initial_location = " / " ;
2022-12-04 15:02:33 -03:00
DeprecatedString focused_entry ;
2021-03-05 16:43:39 -03:00
if ( is_selection_mode ) {
initial_location = path . dirname ( ) ;
focused_entry = path . basename ( ) ;
}
2021-02-28 15:54:03 -03:00
2021-08-25 14:39:30 -03:00
return run_in_windowed_mode ( initial_location , focused_entry ) ;
2020-04-18 17:04:41 -03:00
}
2022-12-04 15:02:33 -03:00
void do_copy ( Vector < DeprecatedString > const & selected_file_paths , FileOperation file_operation )
2020-12-10 04:07:01 -03:00
{
2022-04-23 18:05:06 -03:00
VERIFY ( ! selected_file_paths . is_empty ( ) ) ;
2020-12-10 04:07:01 -03:00
StringBuilder copy_text ;
2021-06-22 09:56:53 -03:00
if ( file_operation = = FileOperation : : Move ) {
2022-07-11 14:32:29 -03:00
copy_text . append ( " #cut \n " sv ) ; // This exploits the comment lines in the text/uri-list specification, which might be a bit hackish
2020-12-28 12:50:51 -03:00
}
2020-12-10 04:07:01 -03:00
for ( auto & path : selected_file_paths ) {
2022-09-28 20:30:58 -03:00
auto url = URL : : create_with_file_scheme ( path ) ;
2020-12-10 04:07:01 -03:00
copy_text . appendff ( " {} \n " , url ) ;
}
2023-01-26 15:58:09 -03:00
GUI : : Clipboard : : the ( ) . set_data ( copy_text . to_deprecated_string ( ) . bytes ( ) , " text/uri-list " ) ;
2020-12-10 04:07:01 -03:00
}
2022-12-04 15:02:33 -03:00
void do_paste ( DeprecatedString const & target_directory , GUI : : Window * window )
2020-12-10 04:07:01 -03:00
{
2021-11-20 11:22:01 -03:00
auto data_and_type = GUI : : Clipboard : : the ( ) . fetch_data_and_type ( ) ;
2020-12-10 04:07:01 -03:00
if ( data_and_type . mime_type ! = " text/uri-list " ) {
dbgln ( " Cannot paste clipboard type {} " , data_and_type . mime_type ) ;
return ;
}
2022-12-04 15:02:33 -03:00
auto copied_lines = DeprecatedString : : copy ( data_and_type . data ) . split ( ' \n ' ) ;
2020-12-10 04:07:01 -03:00
if ( copied_lines . is_empty ( ) ) {
dbgln ( " No files to paste " ) ;
return ;
}
2021-06-17 11:00:01 -03:00
FileOperation file_operation = FileOperation : : Copy ;
if ( copied_lines [ 0 ] = = " #cut " ) { // cut operation encoded as a text/uri-list comment
2021-06-22 09:56:53 -03:00
file_operation = FileOperation : : Move ;
2020-12-28 12:50:51 -03:00
copied_lines . remove ( 0 ) ;
}
2022-12-04 15:02:33 -03:00
Vector < DeprecatedString > source_paths ;
2020-12-10 04:07:01 -03:00
for ( auto & uri_as_string : copied_lines ) {
if ( uri_as_string . is_empty ( ) )
continue ;
URL url = uri_as_string ;
2022-09-28 20:30:58 -03:00
if ( ! url . is_valid ( ) | | url . scheme ( ) ! = " file " ) {
2020-12-10 04:07:01 -03:00
dbgln ( " Cannot paste URI {} " , uri_as_string ) ;
continue ;
}
2023-04-14 16:12:03 -03:00
source_paths . append ( url . serialize_path ( ) ) ;
2020-12-10 04:07:01 -03:00
}
2021-06-17 11:00:01 -03:00
2022-03-12 09:44:37 -03:00
if ( ! source_paths . is_empty ( ) ) {
if ( auto result = run_file_operation ( file_operation , source_paths , target_directory , window ) ; result . is_error ( ) )
dbgln ( " Failed to paste files: {} " , result . error ( ) ) ;
}
2020-12-10 04:07:01 -03:00
}
2022-12-04 15:02:33 -03:00
void do_create_link ( Vector < DeprecatedString > const & selected_file_paths , GUI : : Window * window )
2020-12-22 12:57:59 -03:00
{
auto path = selected_file_paths . first ( ) ;
2022-12-04 15:02:33 -03:00
auto destination = DeprecatedString : : formatted ( " {}/{} " , Core : : StandardPaths : : desktop_directory ( ) , LexicalPath : : basename ( path ) ) ;
2023-03-21 12:35:30 -03:00
if ( auto result = FileSystem : : link_file ( destination , path ) ; result . is_error ( ) ) {
2022-12-04 15:02:33 -03:00
GUI : : MessageBox : : show ( window , DeprecatedString : : formatted ( " Could not create desktop shortcut: \n {} " , result . error ( ) ) , " File Manager " sv ,
2020-12-22 12:57:59 -03:00
GUI : : MessageBox : : Type : : Error ) ;
}
}
2022-12-04 15:02:33 -03:00
void do_create_archive ( Vector < DeprecatedString > const & selected_file_paths , GUI : : Window * window )
2021-12-27 14:16:37 -03:00
{
2023-04-16 17:02:07 -03:00
String archive_name ;
2022-07-11 14:32:29 -03:00
if ( GUI : : InputBox : : show ( window , archive_name , " Enter name: " sv , " Create Archive " sv ) ! = GUI : : InputBox : : ExecResult : : OK )
2021-12-27 14:16:37 -03:00
return ;
auto output_directory_path = LexicalPath ( selected_file_paths . first ( ) ) ;
StringBuilder path_builder ;
path_builder . append ( output_directory_path . dirname ( ) ) ;
2022-07-11 17:10:18 -03:00
path_builder . append ( ' / ' ) ;
2021-12-27 14:16:37 -03:00
if ( archive_name . is_empty ( ) ) {
path_builder . append ( output_directory_path . parent ( ) . basename ( ) ) ;
2022-07-11 14:32:29 -03:00
path_builder . append ( " .zip " sv ) ;
2021-12-27 14:16:37 -03:00
} else {
path_builder . append ( archive_name ) ;
2023-04-16 17:02:07 -03:00
if ( ! AK : : StringUtils : : ends_with ( archive_name , " .zip " sv , CaseSensitivity : : CaseSensitive ) )
2022-07-11 14:32:29 -03:00
path_builder . append ( " .zip " sv ) ;
2021-12-27 14:16:37 -03:00
}
2023-01-26 15:58:09 -03:00
auto output_path = path_builder . to_deprecated_string ( ) ;
2021-12-27 14:16:37 -03:00
pid_t zip_pid = fork ( ) ;
if ( zip_pid < 0 ) {
perror ( " fork " ) ;
VERIFY_NOT_REACHED ( ) ;
}
if ( ! zip_pid ) {
2022-12-04 15:02:33 -03:00
Vector < DeprecatedString > relative_paths ;
2021-12-27 14:16:37 -03:00
Vector < char const * > arg_list ;
arg_list . append ( " /bin/zip " ) ;
arg_list . append ( " -r " ) ;
arg_list . append ( " -f " ) ;
arg_list . append ( output_path . characters ( ) ) ;
for ( auto const & path : selected_file_paths ) {
relative_paths . append ( LexicalPath : : relative_path ( path , output_directory_path . dirname ( ) ) ) ;
arg_list . append ( relative_paths . last ( ) . characters ( ) ) ;
}
arg_list . append ( nullptr ) ;
int rc = execvp ( " /bin/zip " , const_cast < char * const * > ( arg_list . data ( ) ) ) ;
if ( rc < 0 ) {
perror ( " execvp " ) ;
_exit ( 1 ) ;
}
} else {
int status ;
int rc = waitpid ( zip_pid , & status , 0 ) ;
if ( rc < 0 | | ! WIFEXITED ( status ) | | WEXITSTATUS ( status ) ! = 0 )
2022-07-11 14:32:29 -03:00
GUI : : MessageBox : : show ( window , " Could not create archive " sv , " Archive Error " sv , GUI : : MessageBox : : Type : : Error ) ;
2021-12-27 14:16:37 -03:00
}
}
2022-12-04 15:02:33 -03:00
void do_set_wallpaper ( DeprecatedString const & file_path , GUI : : Window * window )
2022-08-01 20:50:02 -03:00
{
auto show_error = [ & ] {
2022-12-04 15:02:33 -03:00
GUI : : MessageBox : : show ( window , DeprecatedString : : formatted ( " Failed to set {} as wallpaper. " , file_path ) , " Failed to set wallpaper " sv , GUI : : MessageBox : : Type : : Error ) ;
2022-08-01 20:50:02 -03:00
} ;
2023-01-20 16:06:05 -03:00
auto bitmap_or_error = Gfx : : Bitmap : : load_from_file ( file_path ) ;
2022-08-01 20:50:02 -03:00
if ( bitmap_or_error . is_error ( ) ) {
show_error ( ) ;
return ;
}
if ( ! GUI : : Desktop : : the ( ) . set_wallpaper ( bitmap_or_error . release_value ( ) , file_path ) )
show_error ( ) ;
}
2022-12-04 15:02:33 -03:00
void do_unzip_archive ( Vector < DeprecatedString > const & selected_file_paths , GUI : : Window * window )
2021-04-25 10:36:27 -03:00
{
2022-12-04 15:02:33 -03:00
DeprecatedString archive_file_path = selected_file_paths . first ( ) ;
DeprecatedString output_directory_path = archive_file_path . substring ( 0 , archive_file_path . length ( ) - 4 ) ;
2021-04-25 10:36:27 -03:00
pid_t unzip_pid = fork ( ) ;
if ( unzip_pid < 0 ) {
perror ( " fork " ) ;
VERIFY_NOT_REACHED ( ) ;
}
if ( ! unzip_pid ) {
2021-06-13 11:59:16 -03:00
int rc = execlp ( " /bin/unzip " , " /bin/unzip " , " -d " , output_directory_path . characters ( ) , archive_file_path . characters ( ) , nullptr ) ;
2021-04-25 10:36:27 -03:00
if ( rc < 0 ) {
perror ( " execlp " ) ;
_exit ( 1 ) ;
}
} else {
// FIXME: this could probably be tied in with the new file operation progress tracking
int status ;
int rc = waitpid ( unzip_pid , & status , 0 ) ;
if ( rc < 0 | | ! WIFEXITED ( status ) | | WEXITSTATUS ( status ) ! = 0 )
2022-07-11 14:32:29 -03:00
GUI : : MessageBox : : show ( window , " Could not extract archive " sv , " Extract Archive Error " sv , GUI : : MessageBox : : Type : : Error ) ;
2021-04-25 10:36:27 -03:00
}
}
2022-12-04 15:02:33 -03:00
void show_properties ( DeprecatedString const & container_dir_path , DeprecatedString const & path , Vector < DeprecatedString > const & selected , GUI : : Window * window )
2020-12-10 04:07:01 -03:00
{
2023-01-25 16:05:26 -03:00
ErrorOr < RefPtr < PropertiesWindow > > properties_or_error = nullptr ;
2020-12-10 04:07:01 -03:00
if ( selected . is_empty ( ) ) {
2023-01-25 16:05:26 -03:00
properties_or_error = window - > try_add < PropertiesWindow > ( path , true ) ;
2020-12-10 04:07:01 -03:00
} else {
2023-01-25 16:05:26 -03:00
properties_or_error = window - > try_add < PropertiesWindow > ( selected . first ( ) , access ( container_dir_path . characters ( ) , W_OK ) ! = 0 ) ;
2020-12-10 04:07:01 -03:00
}
2023-01-25 16:05:26 -03:00
if ( properties_or_error . is_error ( ) ) {
GUI : : MessageBox : : show ( window , " Could not show properties " sv , " Properties Error " sv , GUI : : MessageBox : : Type : : Error ) ;
return ;
}
auto properties = properties_or_error . release_value ( ) ;
2021-01-01 20:31:30 -03:00
properties - > on_close = [ properties = properties . ptr ( ) ] {
properties - > remove_from_parent ( ) ;
} ;
properties - > center_on_screen ( ) ;
properties - > show ( ) ;
2020-12-10 04:07:01 -03:00
}
2023-03-06 10:17:01 -03:00
bool add_launch_handler_actions_to_menu ( RefPtr < GUI : : Menu > & menu , DirectoryView const & directory_view , DeprecatedString const & full_path , RefPtr < GUI : : Action > & default_action , Vector < NonnullRefPtr < LauncherHandler > > & current_file_launch_handlers )
2021-03-03 08:00:32 -03:00
{
current_file_launch_handlers = directory_view . get_launch_handlers ( full_path ) ;
bool added_open_menu_items = false ;
auto default_file_handler = directory_view . get_default_launch_handler ( current_file_launch_handlers ) ;
if ( default_file_handler ) {
auto file_open_action = default_file_handler - > create_launch_action ( [ & , full_path = move ( full_path ) ] ( auto & launcher_handler ) {
2022-09-28 20:30:58 -03:00
directory_view . launch ( URL : : create_with_file_scheme ( full_path ) , launcher_handler ) ;
2021-03-03 08:00:32 -03:00
} ) ;
if ( default_file_handler - > details ( ) . launcher_type = = Desktop : : Launcher : : LauncherType : : Application )
2022-12-04 15:02:33 -03:00
file_open_action - > set_text ( DeprecatedString : : formatted ( " Run {} " , file_open_action - > text ( ) ) ) ;
2021-03-03 08:00:32 -03:00
else
2022-12-04 15:02:33 -03:00
file_open_action - > set_text ( DeprecatedString : : formatted ( " Open in {} " , file_open_action - > text ( ) ) ) ;
2021-03-03 08:00:32 -03:00
default_action = file_open_action ;
menu - > add_action ( move ( file_open_action ) ) ;
added_open_menu_items = true ;
} else {
default_action . clear ( ) ;
}
if ( current_file_launch_handlers . size ( ) > 1 ) {
added_open_menu_items = true ;
2023-08-14 04:04:41 -03:00
auto file_open_with_menu = menu - > add_submenu ( " Open with " _string ) ;
2021-03-03 08:00:32 -03:00
for ( auto & handler : current_file_launch_handlers ) {
2023-03-06 10:17:01 -03:00
if ( handler = = default_file_handler )
2021-03-03 08:00:32 -03:00
continue ;
2023-08-14 04:04:41 -03:00
file_open_with_menu - > add_action ( handler - > create_launch_action ( [ & , full_path = move ( full_path ) ] ( auto & launcher_handler ) {
2022-09-28 20:30:58 -03:00
directory_view . launch ( URL : : create_with_file_scheme ( full_path ) , launcher_handler ) ;
2021-03-03 08:00:32 -03:00
} ) ) ;
}
}
return added_open_menu_items ;
}
2021-11-24 18:26:18 -03:00
ErrorOr < int > run_in_desktop_mode ( )
2020-04-18 17:04:41 -03:00
{
2023-01-22 16:54:23 -03:00
( void ) Core : : Process : : set_name ( " FileManager (Desktop) " sv , Core : : Process : : SetThreadName : : Yes ) ;
2020-08-10 15:29:08 -03:00
2023-09-16 08:51:33 -03:00
auto window = GUI : : Window : : construct ( ) ;
2020-04-18 17:04:41 -03:00
window - > set_title ( " Desktop Manager " ) ;
window - > set_window_type ( GUI : : WindowType : : Desktop ) ;
window - > set_has_alpha_channel ( true ) ;
2023-04-16 17:06:31 -03:00
auto desktop_icon = TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/desktop.png " sv ) ) ;
window - > set_icon ( desktop_icon ) ;
2023-09-18 21:13:48 -03:00
auto desktop_widget = window - > set_main_widget < FileManager : : DesktopWidget > ( ) ;
2023-08-13 09:52:36 -03:00
desktop_widget - > set_layout < GUI : : VerticalBoxLayout > ( ) ;
2020-04-18 17:04:41 -03:00
2021-11-24 18:26:18 -03:00
auto directory_view = TRY ( desktop_widget - > try_add < DirectoryView > ( DirectoryView : : Mode : : Desktop ) ) ;
directory_view - > set_name ( " directory_view " ) ;
2020-04-18 17:04:41 -03:00
2021-07-17 07:21:09 -03:00
auto cut_action = GUI : : CommonActions : : make_cut_action (
2020-12-10 04:07:01 -03:00
[ & ] ( auto & ) {
2021-11-24 18:26:18 -03:00
auto paths = directory_view - > selected_file_paths ( ) ;
2022-04-23 18:05:06 -03:00
VERIFY ( ! paths . is_empty ( ) ) ;
2020-12-10 04:07:01 -03:00
2021-06-22 09:56:53 -03:00
do_copy ( paths , FileOperation : : Move ) ;
2020-12-10 04:07:01 -03:00
} ,
window ) ;
2021-07-17 07:21:09 -03:00
cut_action - > set_enabled ( false ) ;
2020-12-10 04:07:01 -03:00
2021-07-17 07:21:09 -03:00
auto copy_action = GUI : : CommonActions : : make_copy_action (
2020-12-28 12:50:51 -03:00
[ & ] ( auto & ) {
2021-11-24 18:26:18 -03:00
auto paths = directory_view - > selected_file_paths ( ) ;
2022-04-23 18:05:06 -03:00
VERIFY ( ! paths . is_empty ( ) ) ;
2020-12-28 12:50:51 -03:00
2021-06-17 10:41:40 -03:00
do_copy ( paths , FileOperation : : Copy ) ;
2020-12-28 12:50:51 -03:00
} ,
window ) ;
2021-07-17 07:21:09 -03:00
copy_action - > set_enabled ( false ) ;
2020-12-28 12:50:51 -03:00
2021-12-27 14:16:37 -03:00
auto create_archive_action
= GUI : : Action : : create (
" Create &Archive " ,
2023-01-20 16:06:05 -03:00
TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/filetype-archive.png " sv ) ) ,
2021-12-27 14:16:37 -03:00
[ & ] ( GUI : : Action const & ) {
auto paths = directory_view - > selected_file_paths ( ) ;
if ( paths . is_empty ( ) )
return ;
do_create_archive ( paths , directory_view - > window ( ) ) ;
} ,
window ) ;
2021-04-25 10:36:27 -03:00
auto unzip_archive_action
= GUI : : Action : : create (
" E&xtract Here " ,
2021-06-17 07:23:12 -03:00
[ & ] ( GUI : : Action const & ) {
2021-11-24 18:26:18 -03:00
auto paths = directory_view - > selected_file_paths ( ) ;
2021-04-25 10:36:27 -03:00
if ( paths . is_empty ( ) )
return ;
2021-11-24 18:26:18 -03:00
do_unzip_archive ( paths , directory_view - > window ( ) ) ;
2021-04-25 10:36:27 -03:00
} ,
window ) ;
2022-08-01 20:50:02 -03:00
auto set_wallpaper_action
= GUI : : Action : : create (
" Set as Desktop &Wallpaper " ,
2023-01-20 16:06:05 -03:00
TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/app-display-settings.png " sv ) ) ,
2022-08-01 20:50:02 -03:00
[ & ] ( GUI : : Action const & ) {
auto paths = directory_view - > selected_file_paths ( ) ;
if ( paths . is_empty ( ) )
return ;
do_set_wallpaper ( paths . first ( ) , directory_view - > window ( ) ) ;
} ,
window ) ;
2021-11-24 18:26:18 -03:00
directory_view - > on_selection_change = [ & ] ( GUI : : AbstractView const & view ) {
2020-12-28 12:50:51 -03:00
cut_action - > set_enabled ( ! view . selection ( ) . is_empty ( ) ) ;
2021-07-17 07:21:09 -03:00
copy_action - > set_enabled ( ! view . selection ( ) . is_empty ( ) ) ;
2020-12-10 04:07:01 -03:00
} ;
2021-04-04 17:39:41 -03:00
auto properties_action = GUI : : CommonActions : : make_properties_action (
[ & ] ( auto & ) {
2022-12-04 15:02:33 -03:00
DeprecatedString path = directory_view - > path ( ) ;
Vector < DeprecatedString > selected = directory_view - > selected_file_paths ( ) ;
2020-12-10 04:07:01 -03:00
2021-11-24 18:26:18 -03:00
show_properties ( path , path , selected , directory_view - > window ( ) ) ;
2021-04-04 17:39:41 -03:00
} ,
window ) ;
2020-12-10 04:07:01 -03:00
auto paste_action = GUI : : CommonActions : : make_paste_action (
2021-06-17 07:23:12 -03:00
[ & ] ( GUI : : Action const & ) {
2021-11-24 18:26:18 -03:00
do_paste ( directory_view - > path ( ) , directory_view - > window ( ) ) ;
2020-12-10 04:07:01 -03:00
} ,
window ) ;
2021-11-24 18:26:18 -03:00
paste_action - > set_enabled ( GUI : : Clipboard : : the ( ) . fetch_mime_type ( ) = = " text/uri-list " & & access ( directory_view - > path ( ) . characters ( ) , W_OK ) = = 0 ) ;
2020-12-10 04:07:01 -03:00
2022-12-04 15:02:33 -03:00
GUI : : Clipboard : : the ( ) . on_change = [ & ] ( DeprecatedString const & data_type ) {
2021-11-24 18:26:18 -03:00
paste_action - > set_enabled ( data_type = = " text/uri-list " & & access ( directory_view - > path ( ) . characters ( ) , W_OK ) = = 0 ) ;
2020-12-10 04:07:01 -03:00
} ;
2023-09-18 02:00:51 -03:00
auto desktop_view_context_menu = GUI : : Menu : : construct ( " Directory View " _string ) ;
2020-04-22 15:27:35 -03:00
2023-01-20 16:06:05 -03:00
auto file_manager_action = GUI : : Action : : create ( " Open in File &Manager " , { } , TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/app-file-manager.png " sv ) ) , [ & ] ( auto & ) {
2021-11-24 18:26:18 -03:00
auto paths = directory_view - > selected_file_paths ( ) ;
2021-08-30 16:29:08 -03:00
if ( paths . is_empty ( ) ) {
2022-09-28 20:30:58 -03:00
Desktop : : Launcher : : open ( URL : : create_with_file_scheme ( directory_view - > path ( ) ) ) ;
2021-08-30 16:29:08 -03:00
return ;
}
for ( auto & path : paths ) {
2023-03-21 12:35:30 -03:00
if ( FileSystem : : is_directory ( path ) )
2022-09-28 20:30:58 -03:00
Desktop : : Launcher : : open ( URL : : create_with_file_scheme ( path ) ) ;
2021-08-30 16:29:08 -03:00
}
2020-04-22 16:10:38 -03:00
} ) ;
2023-01-20 16:06:05 -03:00
auto open_terminal_action = GUI : : Action : : create ( " Open in &Terminal " , { } , TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/app-terminal.png " sv ) ) , [ & ] ( auto & ) {
2021-11-24 18:26:18 -03:00
auto paths = directory_view - > selected_file_paths ( ) ;
2021-08-30 16:31:43 -03:00
if ( paths . is_empty ( ) ) {
2023-03-13 17:29:33 -03:00
spawn_terminal ( window , directory_view - > path ( ) ) ;
2021-08-30 16:31:43 -03:00
return ;
}
for ( auto & path : paths ) {
2023-03-21 12:35:30 -03:00
if ( FileSystem : : is_directory ( path ) ) {
2023-03-13 17:29:33 -03:00
spawn_terminal ( window , path ) ;
2021-08-30 16:31:43 -03:00
}
}
} ) ;
2023-01-20 16:06:05 -03:00
auto display_properties_action = GUI : : Action : : create ( " &Display Settings " , { } , TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/app-display-settings.png " sv ) ) , [ & ] ( GUI : : Action const & ) {
2022-09-28 20:30:58 -03:00
Desktop : : Launcher : : open ( URL : : create_with_file_scheme ( " /bin/DisplaySettings " ) ) ;
2020-04-22 15:46:10 -03:00
} ) ;
2023-08-14 05:14:27 -03:00
desktop_view_context_menu - > add_action ( directory_view - > mkdir_action ( ) ) ;
desktop_view_context_menu - > add_action ( directory_view - > touch_action ( ) ) ;
desktop_view_context_menu - > add_action ( paste_action ) ;
2023-08-14 02:19:40 -03:00
desktop_view_context_menu - > add_separator ( ) ;
2023-08-14 05:14:27 -03:00
desktop_view_context_menu - > add_action ( file_manager_action ) ;
desktop_view_context_menu - > add_action ( open_terminal_action ) ;
2023-08-14 02:19:40 -03:00
desktop_view_context_menu - > add_separator ( ) ;
2023-08-14 05:14:27 -03:00
desktop_view_context_menu - > add_action ( display_properties_action ) ;
2021-11-24 18:26:18 -03:00
2023-09-18 02:00:51 -03:00
auto desktop_context_menu = GUI : : Menu : : construct ( " Directory View Directory " _string ) ;
2021-11-24 18:26:18 -03:00
2023-08-14 05:14:27 -03:00
desktop_context_menu - > add_action ( file_manager_action ) ;
desktop_context_menu - > add_action ( open_terminal_action ) ;
2023-08-14 02:19:40 -03:00
desktop_context_menu - > add_separator ( ) ;
2023-08-14 05:14:27 -03:00
desktop_context_menu - > add_action ( cut_action ) ;
desktop_context_menu - > add_action ( copy_action ) ;
desktop_context_menu - > add_action ( paste_action ) ;
desktop_context_menu - > add_action ( directory_view - > delete_action ( ) ) ;
desktop_context_menu - > add_action ( directory_view - > rename_action ( ) ) ;
2023-08-14 02:19:40 -03:00
desktop_context_menu - > add_separator ( ) ;
2023-08-14 05:14:27 -03:00
desktop_context_menu - > add_action ( properties_action ) ;
2020-12-10 04:07:01 -03:00
2021-03-03 08:00:32 -03:00
RefPtr < GUI : : Menu > file_context_menu ;
2023-03-06 10:17:01 -03:00
Vector < NonnullRefPtr < LauncherHandler > > current_file_handlers ;
2021-03-03 08:00:32 -03:00
RefPtr < GUI : : Action > file_context_menu_action_default_action ;
2021-11-24 18:26:18 -03:00
directory_view - > on_context_menu_request = [ & ] ( GUI : : ModelIndex const & index , GUI : : ContextMenuEvent const & event ) {
2021-03-03 08:00:32 -03:00
if ( index . is_valid ( ) ) {
2021-11-24 18:26:18 -03:00
auto & node = directory_view - > node ( index ) ;
2021-03-03 08:00:32 -03:00
if ( node . is_directory ( ) ) {
2021-08-30 16:29:08 -03:00
desktop_context_menu - > popup ( event . screen_position ( ) , file_manager_action ) ;
2021-03-03 08:00:32 -03:00
} else {
2023-08-07 06:12:38 -03:00
file_context_menu = GUI : : Menu : : construct ( " Directory View File " _string ) ;
2021-08-30 15:11:44 -03:00
bool added_open_menu_items = add_launch_handler_actions_to_menu ( file_context_menu , directory_view , node . full_path ( ) , file_context_menu_action_default_action , current_file_handlers ) ;
if ( added_open_menu_items )
file_context_menu - > add_separator ( ) ;
2021-03-03 08:00:32 -03:00
file_context_menu - > add_action ( cut_action ) ;
2021-07-17 07:21:09 -03:00
file_context_menu - > add_action ( copy_action ) ;
2021-03-03 08:00:32 -03:00
file_context_menu - > add_action ( paste_action ) ;
2021-11-24 18:26:18 -03:00
file_context_menu - > add_action ( directory_view - > delete_action ( ) ) ;
file_context_menu - > add_action ( directory_view - > rename_action ( ) ) ;
2021-12-27 14:16:37 -03:00
file_context_menu - > add_action ( create_archive_action ) ;
2021-03-03 08:00:32 -03:00
file_context_menu - > add_separator ( ) ;
2022-08-01 20:50:02 -03:00
if ( Gfx : : Bitmap : : is_path_a_supported_image_format ( node . name ) ) {
file_context_menu - > add_action ( set_wallpaper_action ) ;
file_context_menu - > add_separator ( ) ;
}
2022-07-11 14:32:29 -03:00
if ( node . full_path ( ) . ends_with ( " .zip " sv , AK : : CaseSensitivity : : CaseInsensitive ) ) {
2021-04-25 10:36:27 -03:00
file_context_menu - > add_action ( unzip_archive_action ) ;
file_context_menu - > add_separator ( ) ;
}
2021-03-03 08:00:32 -03:00
file_context_menu - > add_action ( properties_action ) ;
2021-04-06 12:21:57 -03:00
file_context_menu - > popup ( event . screen_position ( ) , file_context_menu_action_default_action ) ;
2021-03-03 08:00:32 -03:00
}
} else {
2020-04-22 15:27:35 -03:00
desktop_view_context_menu - > popup ( event . screen_position ( ) ) ;
2021-03-03 08:00:32 -03:00
}
2020-04-22 15:27:35 -03:00
} ;
2021-08-27 11:55:27 -03:00
struct BackgroundWallpaperListener : Config : : Listener {
2023-06-26 16:05:53 -03:00
virtual void config_string_did_change ( StringView domain , StringView group , StringView key , StringView value ) override
2021-08-27 11:55:27 -03:00
{
2022-02-13 15:00:57 -03:00
if ( domain = = " WindowManager " & & group = = " Background " & & key = = " Wallpaper " ) {
2022-12-07 12:37:45 -03:00
if ( value . is_empty ( ) ) {
GUI : : Desktop : : the ( ) . set_wallpaper ( nullptr , { } ) ;
return ;
}
2023-01-20 16:06:05 -03:00
auto wallpaper_bitmap_or_error = Gfx : : Bitmap : : load_from_file ( value ) ;
2022-02-13 15:00:57 -03:00
if ( wallpaper_bitmap_or_error . is_error ( ) )
dbgln ( " Failed to load wallpaper bitmap from path: {} " , wallpaper_bitmap_or_error . error ( ) ) ;
else
GUI : : Desktop : : the ( ) . set_wallpaper ( wallpaper_bitmap_or_error . release_value ( ) , { } ) ;
}
2021-08-27 11:55:27 -03:00
}
} wallpaper_listener ;
2022-07-11 14:32:29 -03:00
auto selected_wallpaper = Config : : read_string ( " WindowManager " sv , " Background " sv , " Wallpaper " sv , " " sv ) ;
2022-08-05 21:21:06 -03:00
RefPtr < Gfx : : Bitmap > wallpaper_bitmap { } ;
2020-08-14 17:17:46 -03:00
if ( ! selected_wallpaper . is_empty ( ) ) {
2023-01-20 16:06:05 -03:00
wallpaper_bitmap = TRY ( Gfx : : Bitmap : : load_from_file ( selected_wallpaper ) ) ;
2020-08-14 17:17:46 -03:00
}
2022-08-05 21:21:06 -03:00
// This sets the wallpaper at startup, even if there is no wallpaper, the
// desktop should still show the background color. It's fine to pass a
// nullptr to Desktop::set_wallpaper.
GUI : : Desktop : : the ( ) . set_wallpaper ( wallpaper_bitmap , { } ) ;
2020-08-14 17:17:46 -03:00
2020-04-18 17:04:41 -03:00
window - > show ( ) ;
2020-07-04 11:52:01 -03:00
return GUI : : Application : : the ( ) - > exec ( ) ;
2020-04-18 17:04:41 -03:00
}
2022-12-04 15:02:33 -03:00
ErrorOr < int > run_in_windowed_mode ( DeprecatedString const & initial_location , DeprecatedString const & entry_focused_on_init )
2020-04-18 17:04:41 -03:00
{
2023-09-16 08:51:33 -03:00
auto window = GUI : : Window : : construct ( ) ;
2019-05-27 08:52:28 -03:00
window - > set_title ( " File Manager " ) ;
2019-11-20 17:52:15 -03:00
2023-09-18 21:13:48 -03:00
auto widget = window - > set_main_widget < GUI : : Widget > ( ) ;
2023-01-07 09:38:23 -03:00
TRY ( widget - > load_from_gml ( file_manager_window_gml ) ) ;
2020-04-23 12:44:49 -03:00
2021-11-24 18:26:18 -03:00
auto & toolbar_container = * widget - > find_descendant_of_type_named < GUI : : ToolbarContainer > ( " toolbar_container " ) ;
auto & main_toolbar = * widget - > find_descendant_of_type_named < GUI : : Toolbar > ( " main_toolbar " ) ;
2019-03-02 20:34:40 -03:00
2021-11-24 18:26:18 -03:00
auto & breadcrumb_toolbar = * widget - > find_descendant_of_type_named < GUI : : Toolbar > ( " breadcrumb_toolbar " ) ;
Userland+LibGUI: Add shorthand versions of the Margins constructor
This allows for typing [8] instead of [8, 8, 8, 8] to specify the same
margin on all edges, for example. The constructors follow CSS' style of
specifying margins. The added constructors are:
- Margins(int all): Sets the same margin on all edges.
- Margins(int vertical, int horizontal): Sets the first argument to top
and bottom margins, and the second argument to left and right margins.
- Margins(int top, int vertical, int bottom): Sets the first argument to
the top margin, the second argument to the left and right margins,
and the third argument to the bottom margin.
2021-08-16 21:11:38 -03:00
breadcrumb_toolbar . layout ( ) - > set_margins ( { 0 , 6 } ) ;
2023-02-06 14:30:38 -03:00
auto & breadcrumbbar = * widget - > find_descendant_of_type_named < GUI : : PathBreadcrumbbar > ( " breadcrumbbar " ) ;
2020-12-14 16:14:50 -03:00
2021-11-24 18:26:18 -03:00
auto & splitter = * widget - > find_descendant_of_type_named < GUI : : HorizontalSplitter > ( " splitter " ) ;
auto & tree_view = * widget - > find_descendant_of_type_named < GUI : : TreeView > ( " tree_view " ) ;
2020-12-20 08:17:59 -03:00
2020-09-16 14:56:58 -03:00
auto directories_model = GUI : : FileSystemModel : : create ( { } , GUI : : FileSystemModel : : Mode : : DirectoriesOnly ) ;
2020-03-04 15:07:55 -03:00
tree_view . set_model ( directories_model ) ;
2021-04-05 06:57:47 -03:00
tree_view . set_column_visible ( GUI : : FileSystemModel : : Column : : Icon , false ) ;
tree_view . set_column_visible ( GUI : : FileSystemModel : : Column : : Size , false ) ;
2021-08-30 20:11:05 -03:00
tree_view . set_column_visible ( GUI : : FileSystemModel : : Column : : User , false ) ;
2021-04-05 06:57:47 -03:00
tree_view . set_column_visible ( GUI : : FileSystemModel : : Column : : Group , false ) ;
tree_view . set_column_visible ( GUI : : FileSystemModel : : Column : : Permissions , false ) ;
tree_view . set_column_visible ( GUI : : FileSystemModel : : Column : : ModificationTime , false ) ;
tree_view . set_column_visible ( GUI : : FileSystemModel : : Column : : Inode , false ) ;
tree_view . set_column_visible ( GUI : : FileSystemModel : : Column : : SymlinkTarget , false ) ;
2020-09-19 09:09:59 -03:00
bool is_reacting_to_tree_view_selection_change = false ;
2021-11-24 18:26:18 -03:00
auto directory_view = TRY ( splitter . try_add < DirectoryView > ( DirectoryView : : Mode : : Normal ) ) ;
directory_view - > set_name ( " directory_view " ) ;
2020-03-04 15:07:55 -03:00
2020-09-16 14:56:58 -03:00
// Open the root directory. FIXME: This is awkward.
tree_view . toggle_index ( directories_model - > index ( 0 , 0 , { } ) ) ;
2021-11-24 18:26:18 -03:00
auto & statusbar = * widget - > find_descendant_of_type_named < GUI : : Statusbar > ( " statusbar " ) ;
2020-03-04 15:07:55 -03:00
2021-04-18 11:43:45 -03:00
GUI : : Application : : the ( ) - > on_action_enter = [ & statusbar ] ( GUI : : Action & action ) {
2023-06-04 07:10:25 -03:00
statusbar . set_override_text ( action . status_tip ( ) ) ;
2021-04-18 11:43:45 -03:00
} ;
GUI : : Application : : the ( ) - > on_action_leave = [ & statusbar ] ( GUI : : Action & ) {
statusbar . set_override_text ( { } ) ;
} ;
2021-11-24 18:26:18 -03:00
auto & progressbar = * widget - > find_descendant_of_type_named < GUI : : Progressbar > ( " progressbar " ) ;
2021-04-13 11:18:20 -03:00
progressbar . set_format ( GUI : : Progressbar : : Format : : ValueSlashMax ) ;
2023-04-29 17:47:52 -03:00
progressbar . set_frame_style ( Gfx : : FrameStyle : : SunkenPanel ) ;
2020-03-04 15:07:55 -03:00
2019-12-02 21:54:29 -03:00
auto refresh_tree_view = [ & ] {
2021-05-25 11:13:19 -03:00
directories_model - > invalidate ( ) ;
2019-12-02 21:54:29 -03:00
2021-11-24 18:26:18 -03:00
auto current_path = directory_view - > path ( ) ;
2019-12-02 21:54:29 -03:00
2019-12-12 06:49:17 -03:00
struct stat st ;
// If the directory no longer exists, we find a parent that does.
2020-01-27 18:11:26 -03:00
while ( stat ( current_path . characters ( ) , & st ) ! = 0 ) {
2021-11-24 18:26:18 -03:00
directory_view - > open_parent_directory ( ) ;
current_path = directory_view - > path ( ) ;
2020-01-10 12:58:00 -03:00
if ( current_path = = directories_model - > root_path ( ) ) {
2019-12-12 06:49:17 -03:00
break ;
}
}
2020-01-10 12:58:00 -03:00
// Reselect the existing folder in the tree.
2020-02-02 11:07:41 -03:00
auto new_index = directories_model - > index ( current_path , GUI : : FileSystemModel : : Column : : Name ) ;
2020-12-08 13:51:53 -03:00
if ( new_index . is_valid ( ) ) {
tree_view . expand_all_parents_of ( new_index ) ;
tree_view . set_cursor ( new_index , GUI : : AbstractView : : SelectionUpdate : : Set , true ) ;
}
2019-12-02 21:54:29 -03:00
2021-11-24 18:26:18 -03:00
directory_view - > refresh ( ) ;
2019-12-02 21:54:29 -03:00
} ;
2023-09-18 02:00:51 -03:00
auto directory_context_menu = GUI : : Menu : : construct ( " Directory View Directory " _string ) ;
auto directory_view_context_menu = GUI : : Menu : : construct ( " Directory View " _string ) ;
auto tree_view_directory_context_menu = GUI : : Menu : : construct ( " Tree View Directory " _string ) ;
2019-12-12 06:49:17 -03:00
2023-01-20 16:06:05 -03:00
auto open_parent_directory_action = GUI : : Action : : create ( " Open &Parent Directory " , { Mod_Alt , Key_Up } , TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/open-parent-directory.png " sv ) ) , [ & ] ( GUI : : Action const & ) {
2021-11-24 18:26:18 -03:00
directory_view - > open_parent_directory ( ) ;
2019-03-01 22:20:11 -03:00
} ) ;
2022-08-18 15:44:15 -03:00
auto open_child_directory_action = GUI : : Action : : create ( " Open &Child Directory " , { Mod_Alt , Key_Down } , [ & ] ( GUI : : Action const & ) {
2023-02-06 14:30:38 -03:00
breadcrumbbar . select_child_segment ( ) ;
2022-08-18 15:44:15 -03:00
} ) ;
2021-02-12 19:39:57 -03:00
RefPtr < GUI : : Action > layout_toolbar_action ;
RefPtr < GUI : : Action > layout_location_action ;
RefPtr < GUI : : Action > layout_statusbar_action ;
RefPtr < GUI : : Action > layout_folderpane_action ;
2022-07-11 14:32:29 -03:00
auto show_toolbar = Config : : read_bool ( " FileManager " sv , " Layout " sv , " ShowToolbar " sv , true ) ;
2021-04-09 11:44:08 -03:00
layout_toolbar_action = GUI : : Action : : create_checkable ( " &Toolbar " , [ & ] ( auto & action ) {
2021-02-12 19:39:57 -03:00
if ( action . is_checked ( ) ) {
main_toolbar . set_visible ( true ) ;
toolbar_container . set_visible ( true ) ;
} else {
main_toolbar . set_visible ( false ) ;
2023-02-06 14:30:38 -03:00
if ( ! breadcrumb_toolbar . is_visible ( ) )
2021-02-12 19:39:57 -03:00
toolbar_container . set_visible ( false ) ;
}
show_toolbar = action . is_checked ( ) ;
2022-07-11 14:32:29 -03:00
Config : : write_bool ( " FileManager " sv , " Layout " sv , " ShowToolbar " sv , action . is_checked ( ) ) ;
2021-02-12 19:39:57 -03:00
} ) ;
layout_toolbar_action - > set_checked ( show_toolbar ) ;
main_toolbar . set_visible ( show_toolbar ) ;
2022-07-11 14:32:29 -03:00
auto show_location = Config : : read_bool ( " FileManager " sv , " Layout " sv , " ShowLocationBar " sv , true ) ;
2021-04-09 11:44:08 -03:00
layout_location_action = GUI : : Action : : create_checkable ( " &Location Bar " , [ & ] ( auto & action ) {
2021-02-12 19:39:57 -03:00
if ( action . is_checked ( ) ) {
breadcrumb_toolbar . set_visible ( true ) ;
toolbar_container . set_visible ( true ) ;
} else {
breadcrumb_toolbar . set_visible ( false ) ;
if ( ! main_toolbar . is_visible ( ) )
toolbar_container . set_visible ( false ) ;
}
show_location = action . is_checked ( ) ;
2022-07-11 14:32:29 -03:00
Config : : write_bool ( " FileManager " sv , " Layout " sv , " ShowLocationBar " sv , action . is_checked ( ) ) ;
2021-02-12 19:39:57 -03:00
} ) ;
layout_location_action - > set_checked ( show_location ) ;
breadcrumb_toolbar . set_visible ( show_location ) ;
2022-04-24 09:10:52 -03:00
toolbar_container . set_visible ( show_location | | show_toolbar ) ;
2021-02-12 19:39:57 -03:00
2021-04-09 11:44:08 -03:00
layout_statusbar_action = GUI : : Action : : create_checkable ( " &Status Bar " , [ & ] ( auto & action ) {
2021-02-12 19:39:57 -03:00
action . is_checked ( ) ? statusbar . set_visible ( true ) : statusbar . set_visible ( false ) ;
2022-07-11 14:32:29 -03:00
Config : : write_bool ( " FileManager " sv , " Layout " sv , " ShowStatusbar " sv , action . is_checked ( ) ) ;
2021-02-12 19:39:57 -03:00
} ) ;
2022-07-11 14:32:29 -03:00
auto show_statusbar = Config : : read_bool ( " FileManager " sv , " Layout " sv , " ShowStatusbar " sv , true ) ;
2021-02-12 19:39:57 -03:00
layout_statusbar_action - > set_checked ( show_statusbar ) ;
statusbar . set_visible ( show_statusbar ) ;
2021-04-09 11:44:08 -03:00
layout_folderpane_action = GUI : : Action : : create_checkable ( " &Folder Pane " , { Mod_Ctrl , Key_P } , [ & ] ( auto & action ) {
2021-02-12 19:39:57 -03:00
action . is_checked ( ) ? tree_view . set_visible ( true ) : tree_view . set_visible ( false ) ;
2022-07-11 14:32:29 -03:00
Config : : write_bool ( " FileManager " sv , " Layout " sv , " ShowFolderPane " sv , action . is_checked ( ) ) ;
2021-02-12 19:39:57 -03:00
} ) ;
2022-07-11 14:32:29 -03:00
auto show_folderpane = Config : : read_bool ( " FileManager " sv , " Layout " sv , " ShowFolderPane " sv , true ) ;
2021-02-12 19:39:57 -03:00
layout_folderpane_action - > set_checked ( show_folderpane ) ;
tree_view . set_visible ( show_folderpane ) ;
2023-02-06 14:30:38 -03:00
breadcrumbbar . on_hide_location_box = [ & ] {
2021-02-12 19:39:57 -03:00
if ( show_location )
breadcrumb_toolbar . set_visible ( true ) ;
2022-04-24 09:10:52 -03:00
if ( ! ( show_location | | show_toolbar ) )
2021-02-12 19:39:57 -03:00
toolbar_container . set_visible ( false ) ;
} ;
2020-02-02 11:07:41 -03:00
auto view_type_action_group = make < GUI : : ActionGroup > ( ) ;
2019-07-09 17:10:03 -03:00
view_type_action_group - > set_exclusive ( true ) ;
2021-11-24 18:26:18 -03:00
view_type_action_group - > add_action ( directory_view - > view_as_icons_action ( ) ) ;
view_type_action_group - > add_action ( directory_view - > view_as_table_action ( ) ) ;
view_type_action_group - > add_action ( directory_view - > view_as_columns_action ( ) ) ;
2019-11-20 17:52:15 -03:00
2019-12-12 06:49:17 -03:00
auto tree_view_selected_file_paths = [ & ] {
2022-12-04 15:02:33 -03:00
Vector < DeprecatedString > paths ;
2019-12-12 06:49:17 -03:00
auto & view = tree_view ;
2021-06-17 07:23:12 -03:00
view . selection ( ) . for_each_index ( [ & ] ( GUI : : ModelIndex const & index ) {
2020-01-10 12:58:00 -03:00
paths . append ( directories_model - > full_path ( index ) ) ;
2019-12-12 06:49:17 -03:00
} ) ;
return paths ;
} ;
2021-04-09 11:44:08 -03:00
auto select_all_action = GUI : : CommonActions : : make_select_all_action ( [ & ] ( auto & ) {
2021-11-24 18:26:18 -03:00
directory_view - > current_view ( ) . select_all ( ) ;
2020-01-07 06:29:21 -03:00
} ) ;
2021-07-17 07:21:09 -03:00
auto cut_action = GUI : : CommonActions : : make_cut_action (
2020-08-17 14:52:01 -03:00
[ & ] ( auto & ) {
2021-11-24 18:26:18 -03:00
auto paths = directory_view - > selected_file_paths ( ) ;
2020-12-10 04:07:01 -03:00
if ( paths . is_empty ( ) )
2020-02-13 17:54:27 -03:00
paths = tree_view_selected_file_paths ( ) ;
2022-04-23 18:05:06 -03:00
VERIFY ( ! paths . is_empty ( ) ) ;
2020-04-26 19:03:17 -03:00
2021-06-22 09:56:53 -03:00
do_copy ( paths , FileOperation : : Move ) ;
2020-12-10 04:07:01 -03:00
refresh_tree_view ( ) ;
2020-02-13 17:54:27 -03:00
} ,
window ) ;
2021-07-17 07:21:09 -03:00
cut_action - > set_enabled ( false ) ;
2019-09-10 10:21:58 -03:00
2021-07-17 07:21:09 -03:00
auto copy_action = GUI : : CommonActions : : make_copy_action (
2020-12-28 12:50:51 -03:00
[ & ] ( auto & ) {
2021-11-24 18:26:18 -03:00
auto paths = directory_view - > selected_file_paths ( ) ;
2020-12-28 12:50:51 -03:00
if ( paths . is_empty ( ) )
paths = tree_view_selected_file_paths ( ) ;
2022-04-23 18:05:06 -03:00
VERIFY ( ! paths . is_empty ( ) ) ;
2020-12-28 12:50:51 -03:00
2021-06-17 10:41:40 -03:00
do_copy ( paths , FileOperation : : Copy ) ;
2020-12-28 12:50:51 -03:00
refresh_tree_view ( ) ;
} ,
window ) ;
2021-07-17 07:21:09 -03:00
copy_action - > set_enabled ( false ) ;
2020-12-28 12:50:51 -03:00
2023-08-02 07:13:59 -03:00
auto copy_path_action = GUI : : Action : : create (
" Copy Path " , [ & ] ( GUI : : Action const & ) {
Vector < DeprecatedString > selected_paths ;
if ( directory_view - > active_widget ( ) - > is_focused ( ) ) {
selected_paths = directory_view - > selected_file_paths ( ) ;
} else if ( tree_view . is_focused ( ) ) {
selected_paths = tree_view_selected_file_paths ( ) ;
}
VERIFY ( ! selected_paths . is_empty ( ) ) ;
StringBuilder joined_paths_builder ;
joined_paths_builder . join ( ' \n ' , selected_paths . span ( ) ) ;
GUI : : Clipboard : : the ( ) . set_plain_text ( joined_paths_builder . string_view ( ) ) ;
} ,
window ) ;
2021-06-25 13:47:11 -03:00
auto open_in_new_window_action
= GUI : : Action : : create (
" Open in New &Window " ,
{ } ,
2023-01-20 16:06:05 -03:00
TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/app-file-manager.png " sv ) ) ,
2021-06-25 13:47:11 -03:00
[ & ] ( GUI : : Action const & action ) {
2022-12-04 15:02:33 -03:00
Vector < DeprecatedString > paths ;
2021-06-25 13:47:11 -03:00
if ( action . activator ( ) = = tree_view_directory_context_menu )
paths = tree_view_selected_file_paths ( ) ;
else
2021-11-24 18:26:18 -03:00
paths = directory_view - > selected_file_paths ( ) ;
2021-06-25 13:47:11 -03:00
for ( auto & path : paths ) {
2023-03-21 12:35:30 -03:00
if ( FileSystem : : is_directory ( path ) )
2022-09-28 20:30:58 -03:00
Desktop : : Launcher : : open ( URL : : create_with_file_scheme ( path ) ) ;
2021-06-25 13:47:11 -03:00
}
} ,
window ) ;
2021-07-08 06:22:44 -03:00
auto open_in_new_terminal_action
= GUI : : Action : : create (
" Open in &Terminal " ,
{ } ,
2023-01-20 16:06:05 -03:00
TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/app-terminal.png " sv ) ) ,
2021-07-08 06:22:44 -03:00
[ & ] ( GUI : : Action const & action ) {
2022-12-04 15:02:33 -03:00
Vector < DeprecatedString > paths ;
2021-07-08 06:22:44 -03:00
if ( action . activator ( ) = = tree_view_directory_context_menu )
paths = tree_view_selected_file_paths ( ) ;
else
2021-11-24 18:26:18 -03:00
paths = directory_view - > selected_file_paths ( ) ;
2021-07-08 06:22:44 -03:00
for ( auto & path : paths ) {
2023-03-21 12:35:30 -03:00
if ( FileSystem : : is_directory ( path ) ) {
2023-03-13 17:29:33 -03:00
spawn_terminal ( window , path ) ;
2021-07-08 06:22:44 -03:00
}
}
} ,
window ) ;
2020-12-22 12:57:59 -03:00
auto shortcut_action
= GUI : : Action : : create (
2021-04-09 11:44:08 -03:00
" Create Desktop &Shortcut " ,
2020-12-22 12:57:59 -03:00
{ } ,
2023-01-20 16:06:05 -03:00
TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/filetype-symlink.png " sv ) ) ,
2021-06-17 07:23:12 -03:00
[ & ] ( GUI : : Action const & ) {
2021-11-24 18:26:18 -03:00
auto paths = directory_view - > selected_file_paths ( ) ;
2020-12-22 12:57:59 -03:00
if ( paths . is_empty ( ) ) {
return ;
}
2021-11-24 18:26:18 -03:00
do_create_link ( paths , directory_view - > window ( ) ) ;
2020-12-22 12:57:59 -03:00
} ,
window ) ;
2021-12-27 14:16:37 -03:00
auto create_archive_action
= GUI : : Action : : create (
" Create &Archive " ,
2023-01-20 16:06:05 -03:00
TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/filetype-archive.png " sv ) ) ,
2021-12-27 14:16:37 -03:00
[ & ] ( GUI : : Action const & ) {
auto paths = directory_view - > selected_file_paths ( ) ;
if ( paths . is_empty ( ) )
return ;
do_create_archive ( paths , directory_view - > window ( ) ) ;
refresh_tree_view ( ) ;
} ,
window ) ;
2021-04-25 10:36:27 -03:00
auto unzip_archive_action
= GUI : : Action : : create (
" E&xtract Here " ,
2021-06-17 07:23:12 -03:00
[ & ] ( GUI : : Action const & ) {
2021-11-24 18:26:18 -03:00
auto paths = directory_view - > selected_file_paths ( ) ;
2021-04-25 10:36:27 -03:00
if ( paths . is_empty ( ) )
return ;
2021-11-24 18:26:18 -03:00
do_unzip_archive ( paths , directory_view - > window ( ) ) ;
2021-04-25 10:36:27 -03:00
refresh_tree_view ( ) ;
} ,
window ) ;
2022-08-01 20:50:02 -03:00
auto set_wallpaper_action
= GUI : : Action : : create (
" Set as Desktop &Wallpaper " ,
2023-01-20 16:06:05 -03:00
TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/app-display-settings.png " sv ) ) ,
2022-08-01 20:50:02 -03:00
[ & ] ( GUI : : Action const & ) {
auto paths = directory_view - > selected_file_paths ( ) ;
if ( paths . is_empty ( ) )
return ;
do_set_wallpaper ( paths . first ( ) , directory_view - > window ( ) ) ;
} ,
window ) ;
2021-04-04 17:39:41 -03:00
auto properties_action = GUI : : CommonActions : : make_properties_action (
[ & ] ( auto & action ) {
2022-12-04 15:02:33 -03:00
DeprecatedString container_dir_path ;
DeprecatedString path ;
Vector < DeprecatedString > selected ;
2021-11-24 18:26:18 -03:00
if ( action . activator ( ) = = directory_context_menu | | directory_view - > active_widget ( ) - > is_focused ( ) ) {
path = directory_view - > path ( ) ;
2021-04-04 17:39:41 -03:00
container_dir_path = path ;
2021-11-24 18:26:18 -03:00
selected = directory_view - > selected_file_paths ( ) ;
2021-04-04 17:39:41 -03:00
} else {
path = directories_model - > full_path ( tree_view . selection ( ) . first ( ) ) ;
2021-06-29 11:46:16 -03:00
container_dir_path = LexicalPath : : basename ( path ) ;
2021-04-04 17:39:41 -03:00
selected = tree_view_selected_file_paths ( ) ;
}
2020-02-13 17:54:27 -03:00
2021-11-24 18:26:18 -03:00
show_properties ( container_dir_path , path , selected , directory_view - > window ( ) ) ;
2021-04-04 17:39:41 -03:00
} ,
window ) ;
2019-11-20 17:52:15 -03:00
2020-04-27 13:06:42 -03:00
auto paste_action = GUI : : CommonActions : : make_paste_action (
2021-06-17 07:23:12 -03:00
[ & ] ( GUI : : Action const & action ) {
2022-12-04 15:02:33 -03:00
DeprecatedString target_directory ;
2020-12-10 04:07:01 -03:00
if ( action . activator ( ) = = directory_context_menu )
2021-11-24 18:26:18 -03:00
target_directory = directory_view - > selected_file_paths ( ) [ 0 ] ;
2020-12-10 04:07:01 -03:00
else
2021-11-24 18:26:18 -03:00
target_directory = directory_view - > path ( ) ;
do_paste ( target_directory , directory_view - > window ( ) ) ;
2020-12-10 04:07:01 -03:00
refresh_tree_view ( ) ;
2020-04-27 13:06:42 -03:00
} ,
window ) ;
auto folder_specific_paste_action = GUI : : CommonActions : : make_paste_action (
2021-06-17 07:23:12 -03:00
[ & ] ( GUI : : Action const & action ) {
2022-12-04 15:02:33 -03:00
DeprecatedString target_directory ;
2020-12-10 04:07:01 -03:00
if ( action . activator ( ) = = directory_context_menu )
2021-11-24 18:26:18 -03:00
target_directory = directory_view - > selected_file_paths ( ) [ 0 ] ;
2020-12-10 04:07:01 -03:00
else
2021-11-24 18:26:18 -03:00
target_directory = directory_view - > path ( ) ;
do_paste ( target_directory , directory_view - > window ( ) ) ;
2020-12-10 04:07:01 -03:00
refresh_tree_view ( ) ;
2020-04-27 13:06:42 -03:00
} ,
window ) ;
2020-02-13 17:54:27 -03:00
auto go_back_action = GUI : : CommonActions : : make_go_back_action (
[ & ] ( auto & ) {
2021-11-24 18:26:18 -03:00
directory_view - > open_previous_directory ( ) ;
2020-02-13 17:54:27 -03:00
} ,
window ) ;
auto go_forward_action = GUI : : CommonActions : : make_go_forward_action (
[ & ] ( auto & ) {
2021-11-24 18:26:18 -03:00
directory_view - > open_next_directory ( ) ;
2020-02-13 17:54:27 -03:00
} ,
window ) ;
auto go_home_action = GUI : : CommonActions : : make_go_home_action (
[ & ] ( auto & ) {
2021-11-24 18:26:18 -03:00
directory_view - > open ( Core : : StandardPaths : : home_directory ( ) ) ;
2020-02-13 17:54:27 -03:00
} ,
window ) ;
2019-07-21 04:19:09 -03:00
2022-12-04 15:02:33 -03:00
GUI : : Clipboard : : the ( ) . on_change = [ & ] ( DeprecatedString const & data_type ) {
2021-11-24 18:26:18 -03:00
auto current_location = directory_view - > path ( ) ;
2020-05-15 16:35:03 -03:00
paste_action - > set_enabled ( data_type = = " text/uri-list " & & access ( current_location . characters ( ) , W_OK ) = = 0 ) ;
2020-04-27 13:06:42 -03:00
} ;
2020-09-13 16:38:44 -03:00
auto tree_view_delete_action = GUI : : CommonActions : : make_delete_action (
[ & ] ( auto & ) {
2021-06-17 10:41:40 -03:00
delete_paths ( tree_view_selected_file_paths ( ) , true , window ) ;
2020-12-08 13:51:53 -03:00
refresh_tree_view ( ) ;
2020-09-13 16:38:44 -03:00
} ,
& tree_view ) ;
// This is a little awkward. The menu action does something different depending on which view has focus.
// It would be nice to find a good abstraction for this instead of creating a branching action like this.
auto focus_dependent_delete_action = GUI : : CommonActions : : make_delete_action ( [ & ] ( auto & ) {
if ( tree_view . is_focused ( ) )
tree_view_delete_action - > activate ( ) ;
else
2021-11-24 18:26:18 -03:00
directory_view - > delete_action ( ) . activate ( ) ;
2020-12-08 13:51:53 -03:00
refresh_tree_view ( ) ;
} ) ;
2020-12-10 01:18:29 -03:00
focus_dependent_delete_action - > set_enabled ( false ) ;
2020-12-08 13:51:53 -03:00
2023-01-20 16:06:05 -03:00
auto mkdir_action = GUI : : Action : : create ( " &New Directory... " , { Mod_Ctrl | Mod_Shift , Key_N } , TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/mkdir.png " sv ) ) , [ & ] ( GUI : : Action const & ) {
2021-11-24 18:26:18 -03:00
directory_view - > mkdir_action ( ) . activate ( ) ;
2020-12-08 13:51:53 -03:00
refresh_tree_view ( ) ;
} ) ;
2023-01-20 16:06:05 -03:00
auto touch_action = GUI : : Action : : create ( " New &File... " , { Mod_Ctrl | Mod_Shift , Key_F } , TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/new.png " sv ) ) , [ & ] ( GUI : : Action const & ) {
2021-11-24 18:26:18 -03:00
directory_view - > touch_action ( ) . activate ( ) ;
2020-12-08 13:51:53 -03:00
refresh_tree_view ( ) ;
2020-09-13 16:38:44 -03:00
} ) ;
2023-08-14 05:44:42 -03:00
auto file_menu = window - > add_menu ( " &File " _string ) ;
2023-08-14 05:14:27 -03:00
file_menu - > add_action ( mkdir_action ) ;
file_menu - > add_action ( touch_action ) ;
file_menu - > add_action ( focus_dependent_delete_action ) ;
file_menu - > add_action ( directory_view - > rename_action ( ) ) ;
2023-08-14 02:19:40 -03:00
file_menu - > add_separator ( ) ;
2023-08-14 05:14:27 -03:00
file_menu - > add_action ( properties_action ) ;
2023-08-14 02:19:40 -03:00
file_menu - > add_separator ( ) ;
2023-08-14 05:14:27 -03:00
file_menu - > add_action ( GUI : : CommonActions : : make_quit_action ( [ ] ( auto & ) {
2020-07-04 11:52:01 -03:00
GUI : : Application : : the ( ) - > quit ( ) ;
2023-08-14 05:14:27 -03:00
} ) ) ;
2020-04-04 07:18:40 -03:00
2023-08-14 05:44:42 -03:00
auto edit_menu = window - > add_menu ( " &Edit " _string ) ;
2023-08-14 05:14:27 -03:00
edit_menu - > add_action ( cut_action ) ;
edit_menu - > add_action ( copy_action ) ;
edit_menu - > add_action ( paste_action ) ;
2023-08-14 02:19:40 -03:00
edit_menu - > add_separator ( ) ;
2023-08-14 05:14:27 -03:00
edit_menu - > add_action ( select_all_action ) ;
2021-05-12 16:32:54 -03:00
2022-08-17 18:55:01 -03:00
auto show_dotfiles_in_view = [ & ] ( bool show_dotfiles ) {
directory_view - > set_should_show_dotfiles ( show_dotfiles ) ;
directories_model - > set_should_show_dotfiles ( show_dotfiles ) ;
} ;
2022-04-24 07:25:46 -03:00
auto show_dotfiles_action = GUI : : Action : : create_checkable ( " &Show Dotfiles " , { Mod_Ctrl , Key_H } , [ & ] ( auto & action ) {
2022-08-17 18:55:01 -03:00
show_dotfiles_in_view ( action . is_checked ( ) ) ;
2020-12-08 13:51:53 -03:00
refresh_tree_view ( ) ;
2022-07-11 14:32:29 -03:00
Config : : write_bool ( " FileManager " sv , " DirectoryView " sv , " ShowDotFiles " sv , action . is_checked ( ) ) ;
2020-10-08 17:34:48 -03:00
} ) ;
2022-07-11 14:32:29 -03:00
auto show_dotfiles = Config : : read_bool ( " FileManager " sv , " DirectoryView " sv , " ShowDotFiles " sv , false ) ;
2022-08-17 18:55:01 -03:00
show_dotfiles | = initial_location . contains ( " /. " sv ) ;
2022-04-24 07:25:46 -03:00
show_dotfiles_action - > set_checked ( show_dotfiles ) ;
2022-08-17 18:55:01 -03:00
show_dotfiles_in_view ( show_dotfiles ) ;
2022-01-03 19:23:03 -03:00
2023-08-14 05:44:42 -03:00
auto view_menu = window - > add_menu ( " &View " _string ) ;
2023-08-14 04:04:41 -03:00
auto layout_menu = view_menu - > add_submenu ( " &Layout " _string ) ;
2023-08-14 05:14:27 -03:00
layout_menu - > add_action ( * layout_toolbar_action ) ;
layout_menu - > add_action ( * layout_location_action ) ;
layout_menu - > add_action ( * layout_statusbar_action ) ;
layout_menu - > add_action ( * layout_folderpane_action ) ;
2021-02-12 19:39:57 -03:00
2023-08-14 02:19:40 -03:00
view_menu - > add_separator ( ) ;
2021-02-12 19:39:57 -03:00
2023-08-14 05:14:27 -03:00
view_menu - > add_action ( directory_view - > view_as_icons_action ( ) ) ;
view_menu - > add_action ( directory_view - > view_as_table_action ( ) ) ;
view_menu - > add_action ( directory_view - > view_as_columns_action ( ) ) ;
2023-08-14 02:19:40 -03:00
view_menu - > add_separator ( ) ;
2023-08-14 05:14:27 -03:00
view_menu - > add_action ( show_dotfiles_action ) ;
2020-04-04 07:18:40 -03:00
2023-01-20 16:06:05 -03:00
auto go_to_location_action = GUI : : Action : : create ( " Go to &Location... " , { Mod_Ctrl , Key_L } , Key_F6 , TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/go-to.png " sv ) ) , [ & ] ( auto & ) {
2021-02-12 19:39:57 -03:00
toolbar_container . set_visible ( true ) ;
2023-02-06 14:30:38 -03:00
breadcrumb_toolbar . set_visible ( true ) ;
breadcrumbbar . show_location_text_box ( ) ;
2021-01-09 00:08:40 -03:00
} ) ;
2023-08-14 05:44:42 -03:00
auto go_menu = window - > add_menu ( " &Go " _string ) ;
2023-08-14 05:14:27 -03:00
go_menu - > add_action ( go_back_action ) ;
go_menu - > add_action ( go_forward_action ) ;
go_menu - > add_action ( open_parent_directory_action ) ;
go_menu - > add_action ( open_child_directory_action ) ;
go_menu - > add_action ( go_home_action ) ;
go_menu - > add_action ( go_to_location_action ) ;
2023-08-14 02:19:40 -03:00
go_menu - > add_separator ( ) ;
2023-08-14 05:14:27 -03:00
go_menu - > add_action ( directory_view - > open_terminal_action ( ) ) ;
2021-11-24 18:26:18 -03:00
2023-08-14 05:44:42 -03:00
auto help_menu = window - > add_menu ( " &Help " _string ) ;
2023-08-14 05:14:27 -03:00
help_menu - > add_action ( GUI : : CommonActions : : make_command_palette_action ( window ) ) ;
2023-09-13 16:34:46 -03:00
help_menu - > add_action ( GUI : : CommonActions : : make_about_action ( " File Manager " _string , GUI : : Icon : : default_icon ( " app-file-manager " sv ) , window ) ) ;
2021-11-24 18:26:18 -03:00
2023-08-15 05:36:28 -03:00
main_toolbar . add_action ( go_back_action ) ;
main_toolbar . add_action ( go_forward_action ) ;
main_toolbar . add_action ( open_parent_directory_action ) ;
main_toolbar . add_action ( go_home_action ) ;
main_toolbar . add_separator ( ) ;
main_toolbar . add_action ( directory_view - > open_terminal_action ( ) ) ;
main_toolbar . add_separator ( ) ;
main_toolbar . add_action ( mkdir_action ) ;
main_toolbar . add_action ( touch_action ) ;
main_toolbar . add_separator ( ) ;
main_toolbar . add_action ( focus_dependent_delete_action ) ;
main_toolbar . add_action ( directory_view - > rename_action ( ) ) ;
main_toolbar . add_separator ( ) ;
main_toolbar . add_action ( cut_action ) ;
main_toolbar . add_action ( copy_action ) ;
main_toolbar . add_action ( paste_action ) ;
main_toolbar . add_separator ( ) ;
main_toolbar . add_action ( directory_view - > view_as_icons_action ( ) ) ;
main_toolbar . add_action ( directory_view - > view_as_table_action ( ) ) ;
main_toolbar . add_action ( directory_view - > view_as_columns_action ( ) ) ;
2021-11-24 18:26:18 -03:00
2023-02-06 14:30:38 -03:00
breadcrumbbar . on_path_change = [ & ] ( auto selected_path ) {
2023-03-21 12:35:30 -03:00
if ( FileSystem : : is_directory ( selected_path ) ) {
2022-08-21 16:32:31 -03:00
directory_view - > open ( selected_path ) ;
} else {
dbgln ( " Breadcrumb path '{}' doesn't exist " , selected_path ) ;
2023-02-06 14:30:38 -03:00
breadcrumbbar . set_current_path ( directory_view - > path ( ) ) ;
2022-08-21 16:32:31 -03:00
}
} ;
2022-12-04 15:02:33 -03:00
directory_view - > on_path_change = [ & ] ( DeprecatedString const & new_path , bool can_read_in_path , bool can_write_in_path ) {
2020-09-16 15:57:41 -03:00
auto icon = GUI : : FileIconProvider : : icon_for_path ( new_path ) ;
auto * bitmap = icon . bitmap_for_size ( 16 ) ;
window - > set_icon ( bitmap ) ;
2020-07-19 16:37:25 -03:00
2022-12-04 15:02:33 -03:00
window - > set_title ( DeprecatedString : : formatted ( " {} - File Manager " , new_path ) ) ;
2020-12-14 16:14:50 -03:00
2023-02-06 14:30:38 -03:00
breadcrumbbar . set_current_path ( new_path ) ;
2020-12-14 16:14:50 -03:00
2020-09-19 09:09:59 -03:00
if ( ! is_reacting_to_tree_view_selection_change ) {
auto new_index = directories_model - > index ( new_path , GUI : : FileSystemModel : : Column : : Name ) ;
if ( new_index . is_valid ( ) ) {
tree_view . expand_all_parents_of ( new_index ) ;
tree_view . set_cursor ( new_index , GUI : : AbstractView : : SelectionUpdate : : Set ) ;
}
2019-12-12 06:49:17 -03:00
}
2019-05-23 18:15:57 -03:00
2021-07-25 18:11:47 -03:00
mkdir_action - > set_enabled ( can_write_in_path ) ;
touch_action - > set_enabled ( can_write_in_path ) ;
2021-11-20 11:22:01 -03:00
paste_action - > set_enabled ( can_write_in_path & & GUI : : Clipboard : : the ( ) . fetch_mime_type ( ) = = " text/uri-list " ) ;
2021-11-24 18:26:18 -03:00
go_forward_action - > set_enabled ( directory_view - > path_history_position ( ) < directory_view - > path_history_size ( ) - 1 ) ;
go_back_action - > set_enabled ( directory_view - > path_history_position ( ) > 0 ) ;
2023-02-06 12:42:13 -03:00
open_parent_directory_action - > set_enabled ( breadcrumbbar . has_parent_segment ( ) ) ;
open_child_directory_action - > set_enabled ( breadcrumbbar . has_child_segment ( ) ) ;
2021-11-24 18:26:18 -03:00
directory_view - > view_as_table_action ( ) . set_enabled ( can_read_in_path ) ;
directory_view - > view_as_icons_action ( ) . set_enabled ( can_read_in_path ) ;
directory_view - > view_as_columns_action ( ) . set_enabled ( can_read_in_path ) ;
2019-02-09 06:22:04 -02:00
} ;
2021-11-24 18:26:18 -03:00
directory_view - > on_accepted_drop = [ & ] {
2020-12-08 13:51:53 -03:00
refresh_tree_view ( ) ;
} ;
2021-11-24 18:26:18 -03:00
directory_view - > on_status_message = [ & ] ( StringView message ) {
2023-06-04 05:24:38 -03:00
statusbar . set_text ( String : : from_utf8 ( message ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2019-02-10 08:07:13 -02:00
} ;
2021-11-24 18:26:18 -03:00
directory_view - > on_thumbnail_progress = [ & ] ( int done , int total ) {
2019-03-25 00:25:25 -03:00
if ( done = = total ) {
2020-03-04 15:07:55 -03:00
progressbar . set_visible ( false ) ;
2019-03-25 00:25:25 -03:00
return ;
}
2020-03-04 15:07:55 -03:00
progressbar . set_range ( 0 , total ) ;
progressbar . set_value ( done ) ;
progressbar . set_visible ( true ) ;
2019-03-25 00:25:25 -03:00
} ;
2021-11-24 18:26:18 -03:00
directory_view - > on_selection_change = [ & ] ( GUI : : AbstractView & view ) {
2020-08-14 07:04:03 -03:00
auto & selection = view . selection ( ) ;
2022-05-09 16:00:26 -03:00
cut_action - > set_enabled ( ! selection . is_empty ( ) & & access ( directory_view - > path ( ) . characters ( ) , W_OK ) = = 0 ) ;
2021-07-17 07:21:09 -03:00
copy_action - > set_enabled ( ! selection . is_empty ( ) ) ;
2023-08-02 07:13:59 -03:00
copy_path_action - > set_text ( selection . size ( ) > 1 ? " Copy Paths " : " Copy Path " ) ;
2020-12-10 01:18:29 -03:00
focus_dependent_delete_action - > set_enabled ( ( ! tree_view . selection ( ) . is_empty ( ) & & tree_view . is_focused ( ) )
2022-05-09 16:00:26 -03:00
| | ( ! directory_view - > current_view ( ) . selection ( ) . is_empty ( ) & & access ( directory_view - > path ( ) . characters ( ) , W_OK ) = = 0 ) ) ;
2019-09-13 17:09:01 -03:00
} ;
2023-01-20 16:06:05 -03:00
auto directory_open_action = GUI : : Action : : create ( " Open " , TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/open.png " sv ) ) , [ & ] ( auto & ) {
2021-11-24 18:26:18 -03:00
directory_view - > open ( directory_view - > selected_file_paths ( ) . first ( ) ) ;
2021-08-30 16:29:08 -03:00
} ) ;
2023-08-14 05:14:27 -03:00
directory_context_menu - > add_action ( directory_open_action ) ;
directory_context_menu - > add_action ( open_in_new_window_action ) ;
directory_context_menu - > add_action ( open_in_new_terminal_action ) ;
2023-08-14 02:19:40 -03:00
directory_context_menu - > add_separator ( ) ;
2023-08-14 05:14:27 -03:00
directory_context_menu - > add_action ( cut_action ) ;
directory_context_menu - > add_action ( copy_action ) ;
directory_context_menu - > add_action ( copy_path_action ) ;
directory_context_menu - > add_action ( folder_specific_paste_action ) ;
directory_context_menu - > add_action ( directory_view - > delete_action ( ) ) ;
directory_context_menu - > add_action ( directory_view - > rename_action ( ) ) ;
directory_context_menu - > add_action ( shortcut_action ) ;
directory_context_menu - > add_action ( create_archive_action ) ;
2023-08-14 02:19:40 -03:00
directory_context_menu - > add_separator ( ) ;
2023-08-14 05:14:27 -03:00
directory_context_menu - > add_action ( properties_action ) ;
2021-11-24 18:26:18 -03:00
2023-08-14 05:14:27 -03:00
directory_view_context_menu - > add_action ( mkdir_action ) ;
directory_view_context_menu - > add_action ( touch_action ) ;
directory_view_context_menu - > add_action ( paste_action ) ;
directory_view_context_menu - > add_action ( directory_view - > open_terminal_action ( ) ) ;
2023-08-14 02:19:40 -03:00
directory_view_context_menu - > add_separator ( ) ;
2023-08-14 05:14:27 -03:00
directory_view_context_menu - > add_action ( show_dotfiles_action ) ;
2023-08-14 02:19:40 -03:00
directory_view_context_menu - > add_separator ( ) ;
2023-08-14 05:14:27 -03:00
directory_view_context_menu - > add_action ( properties_action ) ;
2021-11-24 18:26:18 -03:00
2023-08-14 05:14:27 -03:00
tree_view_directory_context_menu - > add_action ( open_in_new_window_action ) ;
tree_view_directory_context_menu - > add_action ( open_in_new_terminal_action ) ;
2023-08-14 02:19:40 -03:00
tree_view_directory_context_menu - > add_separator ( ) ;
2023-08-14 05:14:27 -03:00
tree_view_directory_context_menu - > add_action ( mkdir_action ) ;
tree_view_directory_context_menu - > add_action ( touch_action ) ;
tree_view_directory_context_menu - > add_action ( cut_action ) ;
tree_view_directory_context_menu - > add_action ( copy_action ) ;
tree_view_directory_context_menu - > add_action ( copy_path_action ) ;
tree_view_directory_context_menu - > add_action ( paste_action ) ;
tree_view_directory_context_menu - > add_action ( tree_view_delete_action ) ;
2023-08-14 02:19:40 -03:00
tree_view_directory_context_menu - > add_separator ( ) ;
2023-08-14 05:14:27 -03:00
tree_view_directory_context_menu - > add_action ( properties_action ) ;
2019-12-12 06:49:17 -03:00
2020-07-13 21:58:21 -03:00
RefPtr < GUI : : Menu > file_context_menu ;
2023-03-06 10:17:01 -03:00
Vector < NonnullRefPtr < LauncherHandler > > current_file_handlers ;
2022-05-13 11:03:21 -03:00
RefPtr < GUI : : Action > file_context_menu_action_default_action ;
2020-07-13 21:58:21 -03:00
2021-11-24 18:26:18 -03:00
directory_view - > on_context_menu_request = [ & ] ( GUI : : ModelIndex const & index , GUI : : ContextMenuEvent const & event ) {
2019-12-08 02:39:45 -03:00
if ( index . is_valid ( ) ) {
2021-11-24 18:26:18 -03:00
auto & node = directory_view - > node ( index ) ;
2019-12-08 02:39:45 -03:00
2020-04-27 13:06:42 -03:00
if ( node . is_directory ( ) ) {
2021-11-20 11:22:01 -03:00
auto should_get_enabled = access ( node . full_path ( ) . characters ( ) , W_OK ) = = 0 & & GUI : : Clipboard : : the ( ) . fetch_mime_type ( ) = = " text/uri-list " ;
2020-04-27 13:06:42 -03:00
folder_specific_paste_action - > set_enabled ( should_get_enabled ) ;
2021-08-30 16:29:08 -03:00
directory_context_menu - > popup ( event . screen_position ( ) , directory_open_action ) ;
2020-04-27 13:06:42 -03:00
} else {
2023-08-07 06:12:38 -03:00
file_context_menu = GUI : : Menu : : construct ( " Directory View File " _string ) ;
2021-08-30 15:11:44 -03:00
bool added_launch_file_handlers = add_launch_handler_actions_to_menu ( file_context_menu , directory_view , node . full_path ( ) , file_context_menu_action_default_action , current_file_handlers ) ;
if ( added_launch_file_handlers )
file_context_menu - > add_separator ( ) ;
2020-12-28 12:50:51 -03:00
file_context_menu - > add_action ( cut_action ) ;
2021-07-17 07:21:09 -03:00
file_context_menu - > add_action ( copy_action ) ;
2023-08-02 07:13:59 -03:00
file_context_menu - > add_action ( copy_path_action ) ;
2020-07-13 21:58:21 -03:00
file_context_menu - > add_action ( paste_action ) ;
2021-11-24 18:26:18 -03:00
file_context_menu - > add_action ( directory_view - > delete_action ( ) ) ;
file_context_menu - > add_action ( directory_view - > rename_action ( ) ) ;
2020-12-22 12:57:59 -03:00
file_context_menu - > add_action ( shortcut_action ) ;
2021-12-27 14:16:37 -03:00
file_context_menu - > add_action ( create_archive_action ) ;
2020-07-13 21:58:21 -03:00
file_context_menu - > add_separator ( ) ;
2020-08-04 09:09:42 -03:00
2022-08-01 20:50:02 -03:00
if ( Gfx : : Bitmap : : is_path_a_supported_image_format ( node . name ) ) {
file_context_menu - > add_action ( set_wallpaper_action ) ;
file_context_menu - > add_separator ( ) ;
}
2022-07-11 14:32:29 -03:00
if ( node . full_path ( ) . ends_with ( " .zip " sv , AK : : CaseSensitivity : : CaseInsensitive ) ) {
2021-04-25 10:36:27 -03:00
file_context_menu - > add_action ( unzip_archive_action ) ;
file_context_menu - > add_separator ( ) ;
}
2020-07-13 21:58:21 -03:00
file_context_menu - > add_action ( properties_action ) ;
file_context_menu - > popup ( event . screen_position ( ) , file_context_menu_action_default_action ) ;
2020-04-27 13:06:42 -03:00
}
2019-12-08 02:39:45 -03:00
} else {
directory_view_context_menu - > popup ( event . screen_position ( ) ) ;
}
2019-09-13 17:00:47 -03:00
} ;
2021-05-25 17:48:43 -03:00
tree_view . on_selection_change = [ & ] {
2021-07-23 18:02:24 -03:00
focus_dependent_delete_action - > set_enabled ( ( ! tree_view . selection ( ) . is_empty ( ) & & tree_view . is_focused ( ) )
2021-11-24 18:26:18 -03:00
| | ! directory_view - > current_view ( ) . selection ( ) . is_empty ( ) ) ;
2021-07-23 18:02:24 -03:00
if ( tree_view . selection ( ) . is_empty ( ) )
return ;
2020-07-10 10:51:15 -03:00
if ( directories_model - > m_previously_selected_index . is_valid ( ) )
directories_model - > update_node_on_selection ( directories_model - > m_previously_selected_index , false ) ;
2021-07-23 18:02:24 -03:00
auto const & index = tree_view . selection ( ) . first ( ) ;
2020-07-10 10:51:15 -03:00
directories_model - > update_node_on_selection ( index , true ) ;
directories_model - > m_previously_selected_index = index ;
2021-07-23 18:02:24 -03:00
auto path = directories_model - > full_path ( index ) ;
2021-11-24 18:26:18 -03:00
if ( directory_view - > path ( ) = = path )
2019-12-12 06:49:17 -03:00
return ;
2020-09-19 09:09:59 -03:00
TemporaryChange change ( is_reacting_to_tree_view_selection_change , true ) ;
2021-11-24 18:26:18 -03:00
directory_view - > open ( path ) ;
2020-12-28 12:50:51 -03:00
cut_action - > set_enabled ( ! tree_view . selection ( ) . is_empty ( ) ) ;
2021-07-17 07:21:09 -03:00
copy_action - > set_enabled ( ! tree_view . selection ( ) . is_empty ( ) ) ;
2021-11-24 18:26:18 -03:00
directory_view - > delete_action ( ) . set_enabled ( ! tree_view . selection ( ) . is_empty ( ) ) ;
2019-12-12 06:49:17 -03:00
} ;
2021-06-17 07:23:12 -03:00
tree_view . on_focus_change = [ & ] ( bool has_focus , [[maybe_unused]] GUI : : FocusSource const source ) {
2020-12-10 01:18:29 -03:00
focus_dependent_delete_action - > set_enabled ( ( ! tree_view . selection ( ) . is_empty ( ) & & has_focus )
2021-11-24 18:26:18 -03:00
| | ! directory_view - > current_view ( ) . selection ( ) . is_empty ( ) ) ;
2020-12-10 01:18:29 -03:00
} ;
2021-06-17 07:23:12 -03:00
tree_view . on_context_menu_request = [ & ] ( GUI : : ModelIndex const & index , GUI : : ContextMenuEvent const & event ) {
2019-12-12 06:49:17 -03:00
if ( index . is_valid ( ) ) {
tree_view_directory_context_menu - > popup ( event . screen_position ( ) ) ;
}
} ;
2023-02-06 14:30:38 -03:00
breadcrumbbar . on_paths_drop = [ & ] ( auto path , GUI : : DropEvent const & event ) {
bool const has_accepted_drop = handle_drop ( event , path , window ) . release_value_but_fixme_should_propagate_errors ( ) ;
2023-02-06 18:08:53 -03:00
if ( has_accepted_drop )
refresh_tree_view ( ) ;
2021-01-08 14:59:50 -03:00
} ;
2021-06-17 07:23:12 -03:00
tree_view . on_drop = [ & ] ( GUI : : ModelIndex const & index , GUI : : DropEvent const & event ) {
2023-02-06 18:08:53 -03:00
auto const & target_node = directories_model - > node ( index ) ;
bool const has_accepted_drop = handle_drop ( event , target_node . full_path ( ) , window ) . release_value_but_fixme_should_propagate_errors ( ) ;
if ( has_accepted_drop ) {
refresh_tree_view ( ) ;
const_cast < GUI : : DropEvent & > ( event ) . accept ( ) ;
}
2021-01-08 14:59:50 -03:00
} ;
2021-11-24 18:26:18 -03:00
directory_view - > open ( initial_location ) ;
directory_view - > set_focus ( true ) ;
2019-02-09 06:22:04 -02:00
2021-11-20 11:22:01 -03:00
paste_action - > set_enabled ( GUI : : Clipboard : : the ( ) . fetch_mime_type ( ) = = " text/uri-list " & & access ( initial_location . characters ( ) , W_OK ) = = 0 ) ;
2020-04-26 13:20:57 -03:00
2023-08-27 14:12:52 -03:00
window - > restore_size_and_position ( " FileManager " sv , " Window " sv , { { 640 , 480 } } ) ;
window - > save_size_and_position_on_close ( " FileManager " sv , " Window " sv ) ;
2021-06-06 16:56:18 -03:00
2021-07-20 10:27:29 -03:00
window - > show ( ) ;
2022-07-11 14:32:29 -03:00
directory_view - > set_view_mode_from_string ( Config : : read_string ( " FileManager " sv , " DirectoryView " sv , " ViewMode " sv , " Icon " sv ) ) ;
2019-11-04 13:27:56 -03:00
2021-02-28 15:54:03 -03:00
if ( ! entry_focused_on_init . is_empty ( ) ) {
2021-11-24 18:26:18 -03:00
auto matches = directory_view - > current_view ( ) . model ( ) - > matches ( entry_focused_on_init , GUI : : Model : : MatchesFlag : : MatchFull | GUI : : Model : : MatchesFlag : : FirstMatchOnly ) ;
2021-02-28 15:54:03 -03:00
if ( ! matches . is_empty ( ) )
2021-11-24 18:26:18 -03:00
directory_view - > current_view ( ) . set_cursor ( matches . first ( ) , GUI : : AbstractView : : SelectionUpdate : : Set ) ;
2021-02-28 15:54:03 -03:00
}
2020-07-04 11:52:01 -03:00
return GUI : : Application : : the ( ) - > exec ( ) ;
2020-04-22 16:10:38 -03:00
}