2020-01-18 05:38:21 -03:00
/*
* Copyright ( c ) 2018 - 2020 , Andreas Kling < kling @ serenityos . org >
2021-09-13 17:48:22 -03:00
* Copyright ( c ) 2021 , Mustafa Quraish < mustafa @ serenityos . org >
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
*/
2019-10-12 20:08:12 -03:00
# include "HexEditorWidget.h"
2021-01-22 17:22:00 -03:00
# include "FindDialog.h"
2021-05-23 05:36:30 -03:00
# include "GoToOffsetDialog.h"
2021-05-27 12:51:21 -03:00
# include "SearchResultsModel.h"
2019-10-12 20:08:12 -03:00
# include <AK/Optional.h>
# include <AK/StringBuilder.h>
2021-05-22 11:08:05 -03:00
# include <Applications/HexEditor/HexEditorWindowGML.h>
2021-08-26 21:07:43 -03:00
# include <LibConfig/Client.h>
2020-02-06 11:04:03 -03:00
# include <LibCore/File.h>
2021-09-02 08:03:44 -03:00
# include <LibFileSystemAccessClient/Client.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/Action.h>
# include <LibGUI/BoxLayout.h>
# include <LibGUI/Button.h>
# include <LibGUI/FilePicker.h>
# 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>
2021-05-27 12:51:21 -03:00
# include <LibGUI/Model.h>
2021-04-13 11:18:20 -03:00
# include <LibGUI/Statusbar.h>
2021-05-27 12:51:21 -03:00
# include <LibGUI/TableView.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/TextBox.h>
2021-04-13 11:18:20 -03:00
# include <LibGUI/Toolbar.h>
2021-05-22 11:08:05 -03:00
# include <LibGUI/ToolbarContainer.h>
2020-03-08 08:05:14 -03:00
# include <string.h>
2019-10-12 20:08:12 -03:00
2021-05-22 11:08:05 -03:00
REGISTER_WIDGET ( HexEditor , HexEditor ) ;
2019-10-12 20:08:12 -03:00
HexEditorWidget : : HexEditorWidget ( )
{
2021-05-22 11:08:05 -03:00
load_from_gml ( hex_editor_window_gml ) ;
2021-05-24 11:56:27 -03:00
2021-05-22 11:08:05 -03:00
m_toolbar = * find_descendant_of_type_named < GUI : : Toolbar > ( " toolbar " ) ;
m_toolbar_container = * find_descendant_of_type_named < GUI : : ToolbarContainer > ( " toolbar_container " ) ;
m_editor = * find_descendant_of_type_named < HexEditor > ( " editor " ) ;
m_statusbar = * find_descendant_of_type_named < GUI : : Statusbar > ( " statusbar " ) ;
2021-05-27 12:51:21 -03:00
m_search_results = * find_descendant_of_type_named < GUI : : TableView > ( " search_results " ) ;
m_search_results_container = * find_descendant_of_type_named < GUI : : Widget > ( " search_results_container " ) ;
2019-10-12 20:08:12 -03:00
m_editor - > on_status_change = [ this ] ( int position , HexEditor : : EditMode edit_mode , int selection_start , int selection_end ) {
2020-10-06 08:32:00 -03:00
m_statusbar - > set_text ( 0 , String : : formatted ( " Offset: {:#08X} " , position ) ) ;
2020-10-05 13:26:31 -03:00
m_statusbar - > set_text ( 1 , String : : formatted ( " Edit Mode: {} " , edit_mode = = HexEditor : : EditMode : : Hex ? " Hex " : " Text " ) ) ;
m_statusbar - > set_text ( 2 , String : : formatted ( " Selection Start: {} " , selection_start ) ) ;
m_statusbar - > set_text ( 3 , String : : formatted ( " Selection End: {} " , selection_end ) ) ;
2021-06-01 04:48:59 -03:00
m_statusbar - > set_text ( 4 , String : : formatted ( " Selected Bytes: {} " , m_editor - > selection_size ( ) ) ) ;
2019-10-12 20:08:12 -03:00
} ;
m_editor - > on_change = [ this ] {
bool was_dirty = m_document_dirty ;
m_document_dirty = true ;
if ( ! was_dirty )
update_title ( ) ;
} ;
2021-05-27 12:51:21 -03:00
m_search_results - > set_activates_on_selection ( true ) ;
m_search_results - > on_activation = [ this ] ( const GUI : : ModelIndex & index ) {
if ( ! index . is_valid ( ) )
return ;
auto offset = index . data ( GUI : : ModelRole : : Custom ) . to_i32 ( ) ;
m_last_found_index = offset ;
m_editor - > set_position ( offset ) ;
m_editor - > update ( ) ;
} ;
2021-07-21 13:02:15 -03:00
m_new_action = GUI : : Action : : create ( " New " , { Mod_Ctrl , Key_N } , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/new.png " ) , [ this ] ( const GUI : : Action & ) {
2020-07-16 10:54:42 -03:00
String value ;
2021-06-15 13:15:28 -03:00
if ( request_close ( ) & & GUI : : InputBox : : show ( window ( ) , value , " Enter new file size: " , " New file size " ) = = GUI : : InputBox : : ExecOK & & ! value . is_empty ( ) ) {
2020-07-16 10:54:42 -03:00
auto file_size = value . to_int ( ) ;
2020-06-12 16:07:52 -03:00
if ( file_size . has_value ( ) & & file_size . value ( ) > 0 ) {
2019-10-26 17:31:43 -03:00
m_document_dirty = false ;
2021-09-05 19:59:52 -03:00
auto buffer_result = ByteBuffer : : create_zeroed ( file_size . value ( ) ) ;
if ( ! buffer_result . has_value ( ) ) {
GUI : : MessageBox : : show ( window ( ) , " Entered file size is too large. " , " Error " , GUI : : MessageBox : : Type : : Error ) ;
return ;
}
m_editor - > set_buffer ( buffer_result . release_value ( ) ) ;
2021-06-29 15:12:53 -03:00
set_path ( { } ) ;
2019-10-26 17:31:43 -03:00
update_title ( ) ;
} else {
2020-07-15 23:45:11 -03:00
GUI : : MessageBox : : show ( window ( ) , " Invalid file size entered. " , " Error " , GUI : : MessageBox : : Type : : Error ) ;
2019-10-26 17:31:43 -03:00
}
}
} ) ;
2020-02-02 11:07:41 -03:00
m_open_action = GUI : : CommonActions : : make_open_action ( [ this ] ( auto & ) {
2021-09-02 08:03:44 -03:00
auto response = FileSystemAccessClient : : Client : : the ( ) . open_file ( window ( ) - > window_id ( ) ) ;
2019-10-12 20:08:12 -03:00
2021-09-02 08:03:44 -03:00
if ( response . error ! = 0 ) {
if ( response . error ! = - 1 )
GUI : : MessageBox : : show_error ( window ( ) , String : : formatted ( " Opening \" {} \" failed: {} " , * response . chosen_file , strerror ( response . error ) ) ) ;
2019-10-12 20:08:12 -03:00
return ;
2021-09-02 08:03:44 -03:00
}
2019-10-12 20:08:12 -03:00
2021-06-23 04:19:58 -03:00
if ( m_document_dirty ) {
auto save_document_first_result = GUI : : MessageBox : : show ( window ( ) , " Save changes to current document first? " , " Warning " , GUI : : MessageBox : : Type : : Warning , GUI : : MessageBox : : InputType : : YesNoCancel ) ;
if ( save_document_first_result = = GUI : : Dialog : : ExecResult : : ExecYes )
m_save_action - > activate ( ) ;
if ( save_document_first_result ! = GUI : : Dialog : : ExecResult : : ExecNo & & m_document_dirty )
return ;
}
2021-09-02 08:03:44 -03:00
open_file ( * response . fd , * response . chosen_file ) ;
2019-10-12 20:08:12 -03:00
} ) ;
2020-11-01 18:32:27 -03:00
m_save_action = GUI : : CommonActions : : make_save_action ( [ & ] ( auto & ) {
2021-09-02 08:03:44 -03:00
if ( m_path . is_empty ( ) )
return m_save_as_action - > activate ( ) ;
auto response = FileSystemAccessClient : : Client : : the ( ) . request_file ( window ( ) - > window_id ( ) , m_path , Core : : OpenMode : : Truncate | Core : : OpenMode : : WriteOnly ) ;
if ( response . error ! = 0 ) {
if ( response . error ! = - 1 )
GUI : : MessageBox : : show_error ( window ( ) , String : : formatted ( " Unable to save file: {} " , strerror ( response . error ) ) ) ;
2019-10-12 20:08:12 -03:00
return ;
}
2021-09-02 08:03:44 -03:00
if ( ! m_editor - > write_to_file ( * response . fd ) ) {
GUI : : MessageBox : : show ( window ( ) , " Unable to save file. \n " , " Error " , GUI : : MessageBox : : Type : : Error ) ;
} else {
m_document_dirty = false ;
update_title ( ) ;
}
return ;
2019-10-12 20:08:12 -03:00
} ) ;
2020-11-01 18:32:27 -03:00
m_save_as_action = GUI : : CommonActions : : make_save_as_action ( [ & ] ( auto & ) {
2021-09-02 08:03:44 -03:00
auto response = FileSystemAccessClient : : Client : : the ( ) . save_file ( window ( ) - > window_id ( ) , m_name , m_extension ) ;
if ( response . error ! = 0 ) {
if ( response . error ! = - 1 )
GUI : : MessageBox : : show_error ( window ( ) , String : : formatted ( " Saving \" {} \" failed: {} " , * response . chosen_file , strerror ( response . error ) ) ) ;
2019-10-12 20:08:12 -03:00
return ;
2021-06-15 13:15:28 -03:00
}
2019-10-12 20:08:12 -03:00
2021-09-02 08:03:44 -03:00
if ( ! m_editor - > write_to_file ( * response . fd ) ) {
2020-07-15 23:45:11 -03:00
GUI : : MessageBox : : show ( window ( ) , " Unable to save file. \n " , " Error " , GUI : : MessageBox : : Type : : Error ) ;
2019-10-12 20:08:12 -03:00
return ;
}
m_document_dirty = false ;
2021-09-02 08:03:44 -03:00
set_path ( * response . chosen_file ) ;
dbgln ( " Wrote document to {} " , * response . chosen_file ) ;
2019-10-12 20:08:12 -03:00
} ) ;
2021-07-21 13:02:15 -03:00
m_find_action = GUI : : Action : : create ( " &Find " , { Mod_Ctrl , Key_F } , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/find.png " ) , [ & ] ( const GUI : : Action & ) {
2021-05-22 11:08:05 -03:00
auto old_buffer = m_search_buffer ;
2021-05-27 12:51:21 -03:00
bool find_all = false ;
if ( FindDialog : : show ( window ( ) , m_search_text , m_search_buffer , find_all ) = = GUI : : InputBox : : ExecOK ) {
if ( find_all ) {
auto matches = m_editor - > find_all ( m_search_buffer , 0 ) ;
m_search_results - > set_model ( * new SearchResultsModel ( move ( matches ) ) ) ;
m_search_results - > update ( ) ;
if ( matches . is_empty ( ) ) {
GUI : : MessageBox : : show ( window ( ) , String : : formatted ( " Pattern \" {} \" not found in this file " , m_search_text ) , " Not found " , GUI : : MessageBox : : Type : : Warning ) ;
return ;
}
GUI : : MessageBox : : show ( window ( ) , String : : formatted ( " Found {} matches for \" {} \" in this file " , matches . size ( ) , m_search_text ) , String : : formatted ( " {} matches " , matches . size ( ) ) , GUI : : MessageBox : : Type : : Warning ) ;
set_search_results_visible ( true ) ;
} else {
bool same_buffers = false ;
if ( old_buffer . size ( ) = = m_search_buffer . size ( ) ) {
if ( memcmp ( old_buffer . data ( ) , m_search_buffer . data ( ) , old_buffer . size ( ) ) = = 0 )
same_buffers = true ;
}
2021-05-22 11:08:05 -03:00
2021-05-27 12:51:21 -03:00
auto result = m_editor - > find_and_highlight ( m_search_buffer , same_buffers ? last_found_index ( ) : 0 ) ;
2021-05-22 11:08:05 -03:00
2021-05-27 12:51:21 -03:00
if ( result = = - 1 ) {
GUI : : MessageBox : : show ( window ( ) , String : : formatted ( " Pattern \" {} \" not found in this file " , m_search_text ) , " Not found " , GUI : : MessageBox : : Type : : Warning ) ;
return ;
}
2021-05-22 11:08:05 -03:00
2021-05-27 12:51:21 -03:00
m_last_found_index = result ;
2021-05-22 11:08:05 -03:00
}
2021-05-27 12:51:21 -03:00
2021-05-22 11:08:05 -03:00
m_editor - > update ( ) ;
}
} ) ;
2021-07-21 13:02:15 -03:00
m_goto_offset_action = GUI : : Action : : create ( " &Go to Offset ... " , { Mod_Ctrl , Key_G } , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/go-to.png " ) , [ this ] ( const GUI : : Action & ) {
2021-05-23 05:36:30 -03:00
int new_offset ;
auto result = GoToOffsetDialog : : show (
window ( ) ,
m_goto_history ,
new_offset ,
m_editor - > selection_start_offset ( ) ,
m_editor - > buffer_size ( ) ) ;
if ( result = = GUI : : InputBox : : ExecOK ) {
m_editor - > highlight ( new_offset , new_offset ) ;
m_editor - > update ( ) ;
}
} ) ;
2021-05-22 11:08:05 -03:00
m_layout_toolbar_action = GUI : : Action : : create_checkable ( " &Toolbar " , [ & ] ( auto & action ) {
m_toolbar_container - > set_visible ( action . is_checked ( ) ) ;
2021-08-26 21:07:43 -03:00
Config : : write_bool ( " HexEditor " , " Layout " , " ShowToolbar " , action . is_checked ( ) ) ;
2021-05-22 11:08:05 -03:00
} ) ;
2021-05-27 12:51:21 -03:00
m_layout_search_results_action = GUI : : Action : : create_checkable ( " &Search Results " , [ & ] ( auto & action ) {
set_search_results_visible ( action . is_checked ( ) ) ;
} ) ;
2021-05-22 11:08:05 -03:00
m_toolbar - > add_action ( * m_new_action ) ;
m_toolbar - > add_action ( * m_open_action ) ;
m_toolbar - > add_action ( * m_save_action ) ;
m_toolbar - > add_separator ( ) ;
m_toolbar - > add_action ( * m_find_action ) ;
2021-05-23 05:36:30 -03:00
m_toolbar - > add_action ( * m_goto_offset_action ) ;
2021-05-22 11:08:05 -03:00
2021-02-26 09:07:13 -03:00
m_editor - > set_focus ( true ) ;
}
HexEditorWidget : : ~ HexEditorWidget ( )
{
}
2021-07-21 16:21:03 -03:00
void HexEditorWidget : : initialize_menubar ( GUI : : Window & window )
2021-02-26 09:07:13 -03:00
{
2021-07-21 16:21:03 -03:00
auto & file_menu = window . add_menu ( " &File " ) ;
2021-05-01 05:45:39 -03:00
file_menu . add_action ( * m_new_action ) ;
file_menu . add_action ( * m_open_action ) ;
file_menu . add_action ( * m_save_action ) ;
file_menu . add_action ( * m_save_as_action ) ;
file_menu . add_separator ( ) ;
file_menu . add_action ( GUI : : CommonActions : : make_quit_action ( [ this ] ( auto & ) {
2019-10-12 20:08:12 -03:00
if ( ! request_close ( ) )
return ;
2020-07-04 11:52:01 -03:00
GUI : : Application : : the ( ) - > quit ( ) ;
2019-10-12 20:08:12 -03:00
} ) ) ;
2021-07-21 16:21:03 -03:00
auto & edit_menu = window . add_menu ( " &Edit " ) ;
2021-05-22 04:44:10 -03:00
edit_menu . add_action ( GUI : : CommonActions : : make_select_all_action ( [ this ] ( auto & ) {
m_editor - > select_all ( ) ;
m_editor - > update ( ) ;
} ) ) ;
2021-05-21 13:51:06 -03:00
edit_menu . add_action ( GUI : : Action : : create ( " Fill &Selection... " , { Mod_Ctrl , Key_B } , [ & ] ( const GUI : : Action & ) {
2020-07-16 10:54:42 -03:00
String value ;
2021-07-21 16:21:03 -03:00
if ( GUI : : InputBox : : show ( & window , value , " Fill byte (hex): " , " Fill Selection " ) = = GUI : : InputBox : : ExecOK & & ! value . is_empty ( ) ) {
2020-07-16 10:54:42 -03:00
auto fill_byte = strtol ( value . characters ( ) , nullptr , 16 ) ;
2019-10-26 18:27:26 -03:00
m_editor - > fill_selection ( fill_byte ) ;
}
} ) ) ;
2020-04-04 07:18:40 -03:00
edit_menu . add_separator ( ) ;
2021-05-10 05:16:58 -03:00
edit_menu . add_action ( GUI : : Action : : create ( " Copy &Hex " , { Mod_Ctrl , Key_C } , [ & ] ( const GUI : : Action & ) {
2019-10-12 20:08:12 -03:00
m_editor - > copy_selected_hex_to_clipboard ( ) ;
} ) ) ;
2021-07-21 13:02:15 -03:00
edit_menu . add_action ( GUI : : Action : : create ( " Copy &Text " , { Mod_Ctrl | Mod_Shift , Key_C } , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/edit-copy.png " ) , [ & ] ( const GUI : : Action & ) {
2019-10-12 20:08:12 -03:00
m_editor - > copy_selected_text_to_clipboard ( ) ;
} ) ) ;
2021-05-10 05:16:58 -03:00
edit_menu . add_action ( GUI : : Action : : create ( " Copy as &C Code " , { Mod_Alt | Mod_Shift , Key_C } , [ & ] ( const GUI : : Action & ) {
2019-10-23 19:47:42 -03:00
m_editor - > copy_selected_hex_to_clipboard_as_c_code ( ) ;
} ) ) ;
2021-02-26 09:07:13 -03:00
edit_menu . add_separator ( ) ;
2021-05-22 11:08:05 -03:00
edit_menu . add_action ( * m_find_action ) ;
2021-07-21 13:02:15 -03:00
edit_menu . add_action ( GUI : : Action : : create ( " Find &Next " , { Mod_None , Key_F3 } , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/find-next.png " ) , [ & ] ( const GUI : : Action & ) {
2021-05-16 03:47:46 -03:00
if ( m_search_text . is_empty ( ) | | m_search_buffer . is_empty ( ) ) {
2021-07-21 16:21:03 -03:00
GUI : : MessageBox : : show ( & window , " Nothing to search for " , " Not found " , GUI : : MessageBox : : Type : : Warning ) ;
2021-01-22 17:22:00 -03:00
return ;
}
auto result = m_editor - > find_and_highlight ( m_search_buffer , last_found_index ( ) ) ;
if ( ! result ) {
2021-07-21 16:21:03 -03:00
GUI : : MessageBox : : show ( & window , String : : formatted ( " No more matches for \" {} \" found in this file " , m_search_text ) , " Not found " , GUI : : MessageBox : : Type : : Warning ) ;
2021-01-22 17:22:00 -03:00
return ;
}
m_editor - > update ( ) ;
m_last_found_index = result ;
} ) ) ;
2021-05-29 05:49:35 -03:00
2021-07-21 13:02:15 -03:00
edit_menu . add_action ( GUI : : Action : : create ( " Find All &Strings " , { Mod_Ctrl | Mod_Shift , Key_S } , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/find.png " ) , [ & ] ( const GUI : : Action & ) {
2021-05-29 05:49:35 -03:00
int min_length = 4 ;
auto matches = m_editor - > find_all_strings ( min_length ) ;
m_search_results - > set_model ( * new SearchResultsModel ( move ( matches ) ) ) ;
m_search_results - > update ( ) ;
if ( matches . is_empty ( ) ) {
2021-07-21 16:21:03 -03:00
GUI : : MessageBox : : show ( & window , " No strings found in this file " , " Not found " , GUI : : MessageBox : : Type : : Warning ) ;
2021-05-29 05:49:35 -03:00
return ;
}
set_search_results_visible ( true ) ;
m_editor - > update ( ) ;
} ) ) ;
2021-05-23 05:36:30 -03:00
edit_menu . add_separator ( ) ;
edit_menu . add_action ( * m_goto_offset_action ) ;
2021-01-22 17:22:00 -03:00
2021-07-21 16:21:03 -03:00
auto & view_menu = window . add_menu ( " &View " ) ;
2021-05-24 11:56:27 -03:00
2021-08-26 21:07:43 -03:00
auto show_toolbar = Config : : read_bool ( " HexEditor " , " Layout " , " ShowToolbar " , true ) ;
2021-05-24 11:56:27 -03:00
m_layout_toolbar_action - > set_checked ( show_toolbar ) ;
m_toolbar_container - > set_visible ( show_toolbar ) ;
2021-05-22 11:08:05 -03:00
view_menu . add_action ( * m_layout_toolbar_action ) ;
2021-05-27 12:51:21 -03:00
view_menu . add_action ( * m_layout_search_results_action ) ;
view_menu . add_separator ( ) ;
2021-05-24 11:56:27 -03:00
2021-08-26 21:07:43 -03:00
auto bytes_per_row = Config : : read_i32 ( " HexEditor " , " Layout " , " BytesPerRow " , 16 ) ;
2021-05-24 11:56:27 -03:00
m_editor - > set_bytes_per_row ( bytes_per_row ) ;
m_editor - > update ( ) ;
2021-02-26 09:07:13 -03:00
m_bytes_per_row_actions . set_exclusive ( true ) ;
2021-05-10 05:16:58 -03:00
auto & bytes_per_row_menu = view_menu . add_submenu ( " Bytes per &Row " ) ;
2021-02-26 09:07:13 -03:00
for ( int i = 8 ; i < = 32 ; i + = 8 ) {
auto action = GUI : : Action : : create_checkable ( String : : number ( i ) , [ this , i ] ( auto & ) {
m_editor - > set_bytes_per_row ( i ) ;
m_editor - > update ( ) ;
2021-08-26 21:07:43 -03:00
Config : : write_i32 ( " HexEditor " , " Layout " , " BytesPerRow " , i ) ;
2021-02-26 09:07:13 -03:00
} ) ;
m_bytes_per_row_actions . add_action ( action ) ;
bytes_per_row_menu . add_action ( action ) ;
2021-05-24 11:56:27 -03:00
if ( i = = bytes_per_row )
2021-02-26 09:07:13 -03:00
action - > set_checked ( true ) ;
}
2019-10-12 20:08:12 -03:00
2021-07-21 16:21:03 -03:00
auto & help_menu = window . add_menu ( " &Help " ) ;
help_menu . add_action ( GUI : : CommonActions : : make_about_action ( " Hex Editor " , GUI : : Icon : : default_icon ( " app-hex-editor " ) , & window ) ) ;
2019-10-12 20:08:12 -03:00
}
2021-06-29 15:12:53 -03:00
void HexEditorWidget : : set_path ( StringView const & path )
2019-10-12 20:08:12 -03:00
{
2021-06-29 15:12:53 -03:00
if ( path . is_empty ( ) ) {
m_path = { } ;
m_name = { } ;
m_extension = { } ;
} else {
auto lexical_path = LexicalPath ( path ) ;
m_path = lexical_path . string ( ) ;
m_name = lexical_path . title ( ) ;
m_extension = lexical_path . extension ( ) ;
}
2019-10-12 20:08:12 -03:00
update_title ( ) ;
}
void HexEditorWidget : : update_title ( )
{
StringBuilder builder ;
2021-06-23 04:20:23 -03:00
if ( m_path . is_empty ( ) )
builder . append ( " Untitled " ) ;
else
builder . append ( m_path ) ;
2019-10-12 20:08:12 -03:00
if ( m_document_dirty )
builder . append ( " (*) " ) ;
2020-03-13 19:21:35 -03:00
builder . append ( " - Hex Editor " ) ;
2019-10-12 20:08:12 -03:00
window ( ) - > set_title ( builder . to_string ( ) ) ;
}
2021-09-02 08:03:44 -03:00
void HexEditorWidget : : open_file ( int fd , String const & path )
2019-10-12 20:08:12 -03:00
{
2021-09-02 08:03:44 -03:00
VERIFY ( path . starts_with ( " / " sv ) ) ;
auto file = Core : : File : : construct ( ) ;
if ( ! file - > open ( fd , Core : : OpenMode : : ReadOnly , Core : : File : : ShouldCloseFileDescriptor : : Yes ) & & file - > error ( ) ! = ENOENT ) {
2020-10-05 13:26:31 -03:00
GUI : : MessageBox : : show ( window ( ) , String : : formatted ( " Opening \" {} \" failed: {} " , path , strerror ( errno ) ) , " Error " , GUI : : MessageBox : : Type : : Error ) ;
2019-10-12 20:08:12 -03:00
return ;
}
2021-09-02 08:03:44 -03:00
if ( file - > is_device ( ) ) {
GUI : : MessageBox : : show ( window ( ) , String : : formatted ( " Opening \" {} \" failed: Can't open device files " , path ) , " Error " , GUI : : MessageBox : : Type : : Error ) ;
return ;
}
if ( file - > is_directory ( ) ) {
GUI : : MessageBox : : show ( window ( ) , String : : formatted ( " Opening \" {} \" failed: Can't open directories " , path ) , " Error " , GUI : : MessageBox : : Type : : Error ) ;
return ;
}
2019-10-12 20:08:12 -03:00
m_document_dirty = false ;
m_editor - > set_buffer ( file - > read_all ( ) ) ; // FIXME: On really huge files, this is never going to work. Should really create a framework to fetch data from the file on-demand.
2021-06-29 15:12:53 -03:00
set_path ( path ) ;
2019-10-12 20:08:12 -03:00
}
bool HexEditorWidget : : request_close ( )
{
if ( ! m_document_dirty )
return true ;
2021-06-15 13:15:28 -03:00
auto result = GUI : : MessageBox : : show ( window ( ) , " The file has been modified. Save before closing? " , " Save changes " , GUI : : MessageBox : : Type : : Warning , GUI : : MessageBox : : InputType : : YesNoCancel ) ;
if ( result = = GUI : : MessageBox : : ExecCancel )
return false ;
if ( result = = GUI : : MessageBox : : ExecYes ) {
m_save_action - > activate ( ) ;
return m_document_dirty = = false ;
}
return true ;
2019-10-12 20:08:12 -03:00
}
2021-05-27 12:51:21 -03:00
void HexEditorWidget : : set_search_results_visible ( bool visible )
{
m_layout_search_results_action - > set_checked ( visible ) ;
m_search_results_container - > set_visible ( visible ) ;
}
2021-07-12 10:42:00 -03:00
void HexEditorWidget : : drop_event ( GUI : : DropEvent & event )
{
event . accept ( ) ;
if ( event . mime_data ( ) . has_urls ( ) ) {
auto urls = event . mime_data ( ) . urls ( ) ;
if ( urls . is_empty ( ) )
return ;
window ( ) - > move_to_front ( ) ;
2021-09-02 08:03:44 -03:00
// TODO: A drop event should be considered user consent for opening a file
auto file_response = FileSystemAccessClient : : Client : : the ( ) . request_file ( window ( ) - > window_id ( ) , urls . first ( ) . path ( ) , Core : : OpenMode : : ReadOnly ) ;
if ( file_response . error ! = 0 )
return ;
open_file ( * file_response . fd , urls . first ( ) . path ( ) ) ;
2021-07-12 10:42:00 -03:00
}
}