2022-04-09 00:24:37 -03:00
/*
* Copyright ( c ) 2022 , Dylan Katz < dykatz @ uw . edu >
2022-12-28 20:26:56 -03:00
* Copyright ( c ) 2022 , Tim Flynn < trflynn89 @ serenityos . org >
2023-04-17 03:01:00 -03:00
* Copyright ( c ) 2023 , Cameron Youell < cameronyouell @ gmail . com >
2022-04-09 00:24:37 -03:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2022-12-28 13:38:46 -03:00
# include <DevTools/SQLStudio/SQLStudioGML.h>
2022-12-29 01:09:23 -03:00
# include <LibCore/DirIterator.h>
# include <LibCore/StandardPaths.h>
2022-04-09 00:24:37 -03:00
# include <LibDesktop/Launcher.h>
2023-03-21 12:35:30 -03:00
# include <LibFileSystem/FileSystem.h>
2022-04-09 00:24:37 -03:00
# include <LibGUI/Action.h>
# include <LibGUI/Application.h>
# include <LibGUI/BoxLayout.h>
2022-12-29 01:09:23 -03:00
# include <LibGUI/ComboBox.h>
2022-04-09 00:24:37 -03:00
# include <LibGUI/FilePicker.h>
2022-06-05 12:52:42 -03:00
# include <LibGUI/GroupBox.h>
2022-12-29 01:09:23 -03:00
# include <LibGUI/ItemListModel.h>
2022-06-05 12:59:54 -03:00
# include <LibGUI/JsonArrayModel.h>
2022-04-09 00:24:37 -03:00
# include <LibGUI/Menu.h>
# include <LibGUI/MessageBox.h>
2022-06-05 12:59:54 -03:00
# include <LibGUI/SortingProxyModel.h>
2022-04-09 00:24:37 -03:00
# include <LibGUI/Statusbar.h>
# include <LibGUI/TabWidget.h>
2022-06-05 12:52:42 -03:00
# include <LibGUI/TableView.h>
2022-04-09 00:24:37 -03:00
# include <LibGUI/TextDocument.h>
# include <LibGUI/TextEditor.h>
# include <LibGUI/Toolbar.h>
# include <LibGUI/ToolbarContainer.h>
2022-06-05 12:58:35 -03:00
# include <LibSQL/AST/Lexer.h>
# include <LibSQL/AST/Token.h>
# include <LibSQL/SQLClient.h>
2022-12-03 13:34:30 -03:00
# include <LibSQL/Value.h>
2022-04-09 00:24:37 -03:00
# include "MainWidget.h"
# include "ScriptEditor.h"
2022-12-28 13:38:46 -03:00
REGISTER_WIDGET ( SQLStudio , MainWidget ) ;
2022-04-09 00:24:37 -03:00
namespace SQLStudio {
2022-12-29 01:09:23 -03:00
static Vector < DeprecatedString > lookup_database_names ( )
{
static constexpr auto database_extension = " .db " sv ;
auto database_path = DeprecatedString : : formatted ( " {}/sql " , Core : : StandardPaths : : data_directory ( ) ) ;
2023-03-21 12:35:30 -03:00
if ( ! FileSystem : : exists ( database_path ) )
2022-12-29 01:09:23 -03:00
return { } ;
Core : : DirIterator iterator ( move ( database_path ) , Core : : DirIterator : : SkipParentAndBaseDir ) ;
Vector < DeprecatedString > database_names ;
while ( iterator . has_next ( ) ) {
if ( auto database = iterator . next_path ( ) ; database . ends_with ( database_extension ) )
database_names . append ( database . substring ( 0 , database . length ( ) - database_extension . length ( ) ) ) ;
}
return database_names ;
}
2023-04-17 03:01:00 -03:00
ErrorOr < NonnullRefPtr < MainWidget > > MainWidget : : create ( )
2022-04-09 00:24:37 -03:00
{
2023-04-17 03:01:00 -03:00
auto widget = TRY ( try_make_ref_counted < MainWidget > ( ) ) ;
2022-04-09 00:24:37 -03:00
2023-04-17 03:01:00 -03:00
TRY ( widget - > load_from_gml ( sql_studio_gml ) ) ;
TRY ( widget - > setup ( ) ) ;
return widget ;
}
ErrorOr < void > MainWidget : : setup ( )
{
m_new_action = GUI : : Action : : create ( " &New " , { Mod_Ctrl , Key_N } , TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/new.png " sv ) ) , [ this ] ( auto & ) {
2022-04-09 00:24:37 -03:00
open_new_script ( ) ;
} ) ;
m_open_action = GUI : : CommonActions : : make_open_action ( [ & ] ( auto & ) {
2022-12-28 20:26:56 -03:00
if ( auto result = GUI : : FilePicker : : get_open_filepath ( window ( ) ) ; result . has_value ( ) )
open_script_from_file ( LexicalPath { result . release_value ( ) } ) ;
2022-04-09 00:24:37 -03:00
} ) ;
m_save_action = GUI : : CommonActions : : make_save_action ( [ & ] ( auto & ) {
2022-12-28 20:26:56 -03:00
auto * editor = active_editor ( ) ;
VERIFY ( editor ) ;
if ( auto result = editor - > save ( ) ; result . is_error ( ) )
GUI : : MessageBox : : show_error ( window ( ) , DeprecatedString : : formatted ( " Failed to save {} \n {} " , editor - > path ( ) , result . error ( ) ) ) ;
2022-04-09 00:24:37 -03:00
} ) ;
m_save_as_action = GUI : : CommonActions : : make_save_as_action ( [ & ] ( auto & ) {
2022-12-28 20:26:56 -03:00
auto * editor = active_editor ( ) ;
VERIFY ( editor ) ;
if ( auto result = editor - > save_as ( ) ; result . is_error ( ) )
GUI : : MessageBox : : show_error ( window ( ) , DeprecatedString : : formatted ( " Failed to save {} \n {} " , editor - > path ( ) , result . error ( ) ) ) ;
2022-04-09 00:24:37 -03:00
} ) ;
m_save_all_action = GUI : : Action : : create ( " Save All " , { Mod_Ctrl | Mod_Alt , Key_S } , [ this ] ( auto & ) {
2022-12-28 20:26:56 -03:00
auto * editor = active_editor ( ) ;
VERIFY ( editor ) ;
2022-04-09 00:24:37 -03:00
m_tab_widget - > for_each_child_widget ( [ & ] ( auto & child ) {
2022-12-28 20:26:56 -03:00
auto & editor = verify_cast < ScriptEditor > ( child ) ;
m_tab_widget - > set_active_widget ( & editor ) ;
if ( auto result = editor . save ( ) ; result . is_error ( ) ) {
GUI : : MessageBox : : show_error ( window ( ) , DeprecatedString : : formatted ( " Failed to save {} \n {} " , editor . path ( ) , result . error ( ) ) ) ;
return IterationDecision : : Break ;
} else if ( ! result . value ( ) ) {
2022-04-09 00:24:37 -03:00
return IterationDecision : : Break ;
}
2022-12-28 20:26:56 -03:00
return IterationDecision : : Continue ;
2022-04-09 00:24:37 -03:00
} ) ;
2022-12-28 20:26:56 -03:00
m_tab_widget - > set_active_widget ( editor ) ;
2022-04-09 00:24:37 -03:00
} ) ;
m_copy_action = GUI : : CommonActions : : make_copy_action ( [ & ] ( auto & ) {
2022-12-28 20:26:56 -03:00
auto * editor = active_editor ( ) ;
VERIFY ( editor ) ;
2022-04-09 00:24:37 -03:00
editor - > copy_action ( ) . activate ( ) ;
update_editor_actions ( editor ) ;
} ) ;
m_cut_action = GUI : : CommonActions : : make_cut_action ( [ & ] ( auto & ) {
2022-12-28 20:26:56 -03:00
auto * editor = active_editor ( ) ;
VERIFY ( editor ) ;
2022-04-09 00:24:37 -03:00
editor - > cut_action ( ) . activate ( ) ;
update_editor_actions ( editor ) ;
} ) ;
m_paste_action = GUI : : CommonActions : : make_paste_action ( [ & ] ( auto & ) {
2022-12-28 20:26:56 -03:00
auto * editor = active_editor ( ) ;
VERIFY ( editor ) ;
2022-04-09 00:24:37 -03:00
editor - > paste_action ( ) . activate ( ) ;
update_editor_actions ( editor ) ;
} ) ;
m_undo_action = GUI : : CommonActions : : make_undo_action ( [ & ] ( auto & ) {
2022-12-28 20:26:56 -03:00
auto * editor = active_editor ( ) ;
VERIFY ( editor ) ;
2022-04-09 00:24:37 -03:00
editor - > document ( ) . undo ( ) ;
update_editor_actions ( editor ) ;
} ) ;
m_redo_action = GUI : : CommonActions : : make_redo_action ( [ & ] ( auto & ) {
2022-12-28 20:26:56 -03:00
auto * editor = active_editor ( ) ;
VERIFY ( editor ) ;
2022-04-09 00:24:37 -03:00
editor - > document ( ) . redo ( ) ;
update_editor_actions ( editor ) ;
} ) ;
2023-04-17 03:01:00 -03:00
m_connect_to_database_action = GUI : : Action : : create ( " Connect to Database " sv , { Mod_Alt , Key_C } , TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/go-forward.png " sv ) ) , [ this ] ( auto & ) {
2022-12-29 01:09:23 -03:00
auto database_name = m_databases_combo_box - > text ( ) . trim_whitespace ( ) ;
if ( database_name . is_empty ( ) )
return ;
2022-12-03 09:07:42 -03:00
2022-12-29 01:09:23 -03:00
m_run_script_action - > set_enabled ( false ) ;
2023-08-07 06:12:38 -03:00
m_statusbar - > set_text ( 1 , " Disconnected " _string ) ;
2022-12-03 09:07:42 -03:00
2022-12-28 20:30:34 -03:00
if ( m_connection_id . has_value ( ) ) {
m_sql_client - > disconnect ( * m_connection_id ) ;
m_connection_id . clear ( ) ;
}
2022-12-03 09:07:42 -03:00
if ( auto connection_id = m_sql_client - > connect ( database_name ) ; connection_id . has_value ( ) ) {
2023-06-04 05:24:38 -03:00
m_statusbar - > set_text ( 1 , String : : formatted ( " Connected to: {} " , database_name ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2022-12-29 01:09:23 -03:00
m_connection_id = * connection_id ;
m_run_script_action - > set_enabled ( true ) ;
2022-12-03 09:07:42 -03:00
} else {
2022-12-29 01:19:02 -03:00
GUI : : MessageBox : : show_error ( window ( ) , DeprecatedString : : formatted ( " Could not connect to {} " , database_name ) ) ;
2022-12-03 09:07:42 -03:00
}
2022-06-05 12:52:42 -03:00
} ) ;
2023-05-22 14:07:09 -03:00
m_run_script_action = GUI : : Action : : create ( " Run Script " , { Mod_Alt , Key_F9 } , TRY ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/play.png " sv ) ) , [ & ] ( auto & ) {
2022-12-29 01:09:23 -03:00
m_results . clear ( ) ;
m_current_line_for_parsing = 0 ;
read_next_sql_statement_of_editor ( ) ;
} ) ;
m_run_script_action - > set_enabled ( false ) ;
static auto database_names = lookup_database_names ( ) ;
m_databases_combo_box = GUI : : ComboBox : : construct ( ) ;
m_databases_combo_box - > set_editor_placeholder ( " Enter new database or select existing database " sv ) ;
m_databases_combo_box - > set_max_width ( font ( ) . width ( m_databases_combo_box - > editor_placeholder ( ) ) + font ( ) . max_glyph_width ( ) + 16 ) ;
m_databases_combo_box - > set_model ( * GUI : : ItemListModel < DeprecatedString > : : create ( database_names ) ) ;
m_databases_combo_box - > on_return_pressed = [ this ] ( ) {
m_connect_to_database_action - > activate ( m_databases_combo_box ) ;
} ;
2022-12-28 13:38:46 -03:00
auto & toolbar = * find_descendant_of_type_named < GUI : : Toolbar > ( " toolbar " sv ) ;
2022-04-09 00:24:37 -03:00
toolbar . add_action ( * m_new_action ) ;
toolbar . add_action ( * m_open_action ) ;
toolbar . add_action ( * m_save_action ) ;
toolbar . add_action ( * m_save_as_action ) ;
toolbar . add_separator ( ) ;
toolbar . add_action ( * m_copy_action ) ;
toolbar . add_action ( * m_cut_action ) ;
toolbar . add_action ( * m_paste_action ) ;
toolbar . add_separator ( ) ;
toolbar . add_action ( * m_undo_action ) ;
toolbar . add_action ( * m_redo_action ) ;
2022-06-05 12:52:42 -03:00
toolbar . add_separator ( ) ;
2022-12-29 01:09:23 -03:00
toolbar . add_child ( * m_databases_combo_box ) ;
toolbar . add_action ( * m_connect_to_database_action ) ;
toolbar . add_separator ( ) ;
2022-06-05 12:52:42 -03:00
toolbar . add_action ( * m_run_script_action ) ;
2022-04-09 00:24:37 -03:00
2022-12-28 13:38:46 -03:00
m_tab_widget = find_descendant_of_type_named < GUI : : TabWidget > ( " script_tab_widget " sv ) ;
2022-04-09 00:24:37 -03:00
m_tab_widget - > on_tab_close_click = [ & ] ( auto & widget ) {
2022-12-28 20:26:56 -03:00
auto & editor = verify_cast < ScriptEditor > ( widget ) ;
if ( auto result = editor . attempt_to_close ( ) ; result . is_error ( ) ) {
GUI : : MessageBox : : show_error ( window ( ) , DeprecatedString : : formatted ( " Failed to save {} \n {} " , editor . path ( ) , result . error ( ) ) ) ;
} else if ( result . value ( ) ) {
m_tab_widget - > remove_tab ( editor ) ;
2022-04-09 00:24:37 -03:00
update_title ( ) ;
2022-08-14 20:35:24 -03:00
on_editor_change ( ) ;
2022-04-09 00:24:37 -03:00
}
} ;
m_tab_widget - > on_change = [ & ] ( auto & ) {
update_title ( ) ;
on_editor_change ( ) ;
} ;
2022-12-28 13:38:46 -03:00
m_action_tab_widget = find_descendant_of_type_named < GUI : : TabWidget > ( " action_tab_widget " sv ) ;
2022-06-05 15:58:40 -03:00
2023-08-07 23:26:17 -03:00
m_query_results_widget = m_action_tab_widget - > add_tab < GUI : : Widget > ( " Results " _string ) ;
2023-02-16 18:07:06 -03:00
m_query_results_widget - > set_layout < GUI : : VerticalBoxLayout > ( 6 ) ;
2022-06-05 15:58:40 -03:00
m_query_results_table_view = m_query_results_widget - > add < GUI : : TableView > ( ) ;
m_action_tab_widget - > on_tab_close_click = [ this ] ( auto & ) {
2022-12-31 13:04:13 -03:00
m_action_tab_widget - > set_visible ( false ) ;
2022-06-05 15:58:40 -03:00
} ;
2022-12-28 13:38:46 -03:00
m_statusbar = find_descendant_of_type_named < GUI : : Statusbar > ( " statusbar " sv ) ;
2022-12-29 00:48:22 -03:00
m_statusbar - > segment ( 1 ) . set_mode ( GUI : : Statusbar : : Segment : : Mode : : Auto ) ;
2023-08-07 06:12:38 -03:00
m_statusbar - > set_text ( 1 , " Disconnected " _string ) ;
2022-04-09 00:24:37 -03:00
m_statusbar - > segment ( 2 ) . set_mode ( GUI : : Statusbar : : Segment : : Mode : : Fixed ) ;
2023-04-11 13:39:26 -03:00
m_statusbar - > segment ( 2 ) . set_fixed_width ( font ( ) . width ( " Ln 0,000 Col 000 " sv ) + font ( ) . max_glyph_width ( ) ) ;
2022-06-05 12:52:42 -03:00
2022-12-29 00:48:22 -03:00
GUI : : Application : : the ( ) - > on_action_enter = [ this ] ( GUI : : Action & action ) {
2023-06-04 07:10:25 -03:00
m_statusbar - > set_override_text ( action . status_tip ( ) ) ;
2022-12-29 00:48:22 -03:00
} ;
GUI : : Application : : the ( ) - > on_action_leave = [ this ] ( GUI : : Action & ) {
m_statusbar - > set_override_text ( { } ) ;
} ;
2023-04-17 03:01:00 -03:00
m_sql_client = TRY ( SQL : : SQLClient : : try_create ( ) ) ;
2023-02-03 12:49:58 -03:00
m_sql_client - > on_execution_success = [ this ] ( auto result ) {
m_result_column_names = move ( result . column_names ) ;
2022-06-05 12:58:35 -03:00
read_next_sql_statement_of_editor ( ) ;
} ;
2023-02-03 12:33:10 -03:00
m_sql_client - > on_execution_error = [ this ] ( auto result ) {
2022-12-29 01:19:02 -03:00
auto * editor = active_editor ( ) ;
VERIFY ( editor ) ;
2023-02-03 12:33:10 -03:00
GUI : : MessageBox : : show_error ( window ( ) , DeprecatedString : : formatted ( " Error executing {} \n {} " , editor - > path ( ) , result . error_message ) ) ;
2022-12-29 01:19:02 -03:00
} ;
2023-02-03 12:33:10 -03:00
m_sql_client - > on_next_result = [ this ] ( auto result ) {
2022-12-03 13:34:30 -03:00
m_results . append ( { } ) ;
2023-02-03 12:33:10 -03:00
m_results . last ( ) . ensure_capacity ( result . values . size ( ) ) ;
2022-12-03 13:34:30 -03:00
2023-02-03 12:33:10 -03:00
for ( auto const & value : result . values )
2022-12-03 13:34:30 -03:00
m_results . last ( ) . unchecked_append ( value . to_deprecated_string ( ) ) ;
2022-06-05 12:59:54 -03:00
} ;
2023-02-03 12:33:10 -03:00
m_sql_client - > on_results_exhausted = [ this ] ( auto ) {
2022-06-05 12:59:54 -03:00
if ( m_results . size ( ) = = 0 )
return ;
if ( m_results [ 0 ] . size ( ) = = 0 )
return ;
2023-02-03 12:49:58 -03:00
2022-06-05 12:59:54 -03:00
Vector < GUI : : JsonArrayModel : : FieldSpec > query_result_fields ;
2023-02-03 12:49:58 -03:00
for ( auto & column_name : m_result_column_names )
2023-05-14 14:38:08 -03:00
query_result_fields . empend ( column_name , String : : from_deprecated_string ( column_name ) . release_value_but_fixme_should_propagate_errors ( ) , Gfx : : TextAlignment : : CenterLeft ) ;
2023-02-03 12:49:58 -03:00
2022-06-05 12:59:54 -03:00
auto query_results_model = GUI : : JsonArrayModel : : create ( " {} " , move ( query_result_fields ) ) ;
m_query_results_table_view - > set_model ( MUST ( GUI : : SortingProxyModel : : create ( * query_results_model ) ) ) ;
for ( auto & result_row : m_results ) {
Vector < JsonValue > individual_result_as_json ;
for ( auto & result_row_column : result_row )
individual_result_as_json . append ( result_row_column ) ;
2023-04-17 03:01:00 -03:00
MUST ( query_results_model - > add ( move ( individual_result_as_json ) ) ) ;
2022-06-05 12:59:54 -03:00
}
2022-12-31 13:04:13 -03:00
m_action_tab_widget - > set_visible ( true ) ;
2022-06-05 12:59:54 -03:00
} ;
2023-04-17 03:01:00 -03:00
return { } ;
2022-04-09 00:24:37 -03:00
}
2023-04-16 09:46:48 -03:00
ErrorOr < void > MainWidget : : initialize_menu ( GUI : : Window * window )
2022-04-09 00:24:37 -03:00
{
2023-08-07 23:26:17 -03:00
auto file_menu = TRY ( window - > try_add_menu ( " &File " _string ) ) ;
2023-04-16 09:46:48 -03:00
TRY ( file_menu - > try_add_action ( * m_new_action ) ) ;
TRY ( file_menu - > try_add_action ( * m_open_action ) ) ;
TRY ( file_menu - > try_add_action ( * m_save_action ) ) ;
TRY ( file_menu - > try_add_action ( * m_save_as_action ) ) ;
TRY ( file_menu - > try_add_action ( * m_save_all_action ) ) ;
TRY ( file_menu - > try_add_separator ( ) ) ;
TRY ( file_menu - > try_add_action ( GUI : : CommonActions : : make_quit_action ( [ ] ( auto & ) {
2022-04-09 00:24:37 -03:00
GUI : : Application : : the ( ) - > quit ( ) ;
2023-04-16 09:46:48 -03:00
} ) ) ) ;
2023-08-07 23:26:17 -03:00
auto edit_menu = TRY ( window - > try_add_menu ( " &Edit " _string ) ) ;
2023-04-16 09:46:48 -03:00
TRY ( edit_menu - > try_add_action ( * m_copy_action ) ) ;
TRY ( edit_menu - > try_add_action ( * m_cut_action ) ) ;
TRY ( edit_menu - > try_add_action ( * m_paste_action ) ) ;
TRY ( edit_menu - > try_add_separator ( ) ) ;
TRY ( edit_menu - > try_add_action ( * m_undo_action ) ) ;
TRY ( edit_menu - > try_add_action ( * m_redo_action ) ) ;
TRY ( edit_menu - > try_add_separator ( ) ) ;
TRY ( edit_menu - > try_add_action ( * m_run_script_action ) ) ;
2023-08-07 23:26:17 -03:00
auto help_menu = TRY ( window - > try_add_menu ( " &Help " _string ) ) ;
2023-04-16 09:46:48 -03:00
TRY ( help_menu - > try_add_action ( GUI : : CommonActions : : make_command_palette_action ( window ) ) ) ;
TRY ( help_menu - > try_add_action ( GUI : : CommonActions : : make_help_action ( [ ] ( auto & ) {
2023-04-08 06:48:22 -03:00
Desktop : : Launcher : : open ( URL : : create_with_file_scheme ( " /usr/share/man/man1/Applications/SQLStudio.md " ) , " /bin/Help " ) ;
2023-04-16 09:46:48 -03:00
} ) ) ) ;
TRY ( help_menu - > try_add_action ( GUI : : CommonActions : : make_about_action ( " SQL Studio " , GUI : : Icon : : default_icon ( " app-sql-studio " sv ) , window ) ) ) ;
return { } ;
2022-04-09 00:24:37 -03:00
}
void MainWidget : : open_new_script ( )
{
2022-12-04 15:02:33 -03:00
auto new_script_name = DeprecatedString : : formatted ( " New Script - {} " , m_new_script_counter ) ;
2022-04-09 00:24:37 -03:00
+ + m_new_script_counter ;
2023-03-10 14:55:52 -03:00
auto & editor = m_tab_widget - > add_tab < ScriptEditor > ( String : : from_deprecated_string ( new_script_name ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2022-04-09 00:24:37 -03:00
editor . new_script_with_temp_name ( new_script_name ) ;
editor . on_cursor_change = [ this ] { on_editor_change ( ) ; } ;
editor . on_selection_change = [ this ] { on_editor_change ( ) ; } ;
editor . on_highlighter_change = [ this ] { on_editor_change ( ) ; } ;
m_tab_widget - > set_active_widget ( & editor ) ;
}
void MainWidget : : open_script_from_file ( LexicalPath const & file_path )
{
2023-06-11 14:45:21 -03:00
auto & editor = m_tab_widget - > add_tab < ScriptEditor > ( String : : from_utf8 ( file_path . title ( ) ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2022-12-28 20:26:56 -03:00
if ( auto result = editor . open_script_from_file ( file_path ) ; result . is_error ( ) ) {
GUI : : MessageBox : : show_error ( window ( ) , DeprecatedString : : formatted ( " Failed to open {} \n {} " , file_path , result . error ( ) ) ) ;
2022-04-09 00:24:37 -03:00
return ;
}
editor . on_cursor_change = [ this ] { on_editor_change ( ) ; } ;
editor . on_selection_change = [ this ] { on_editor_change ( ) ; } ;
editor . on_highlighter_change = [ this ] { on_editor_change ( ) ; } ;
m_tab_widget - > set_active_widget ( & editor ) ;
}
bool MainWidget : : request_close ( )
{
auto any_scripts_modified { false } ;
auto is_script_modified = [ & ] ( auto & child ) {
2022-12-28 20:26:56 -03:00
auto & editor = verify_cast < ScriptEditor > ( child ) ;
if ( editor . document ( ) . is_modified ( ) ) {
2022-04-09 00:24:37 -03:00
any_scripts_modified = true ;
return IterationDecision : : Break ;
}
2022-12-28 20:26:56 -03:00
2022-04-09 00:24:37 -03:00
return IterationDecision : : Continue ;
} ;
2022-12-28 20:26:56 -03:00
2022-04-09 00:24:37 -03:00
m_tab_widget - > for_each_child_widget ( is_script_modified ) ;
if ( ! any_scripts_modified )
return true ;
auto result = GUI : : MessageBox : : ask_about_unsaved_changes ( window ( ) , { } ) ;
switch ( result ) {
2022-05-13 09:10:27 -03:00
case GUI : : Dialog : : ExecResult : : Yes :
2022-04-09 00:24:37 -03:00
break ;
2022-05-13 09:10:27 -03:00
case GUI : : Dialog : : ExecResult : : No :
2022-04-09 00:24:37 -03:00
return true ;
default :
return false ;
}
m_save_all_action - > activate ( ) ;
any_scripts_modified = false ;
2022-12-28 20:26:56 -03:00
2022-04-09 00:24:37 -03:00
m_tab_widget - > for_each_child_widget ( is_script_modified ) ;
return ! any_scripts_modified ;
}
2022-12-28 20:26:56 -03:00
ScriptEditor * MainWidget : : active_editor ( )
{
if ( ! m_tab_widget | | ! m_tab_widget - > active_widget ( ) )
return nullptr ;
return verify_cast < ScriptEditor > ( m_tab_widget - > active_widget ( ) ) ;
}
2022-04-09 00:24:37 -03:00
void MainWidget : : update_title ( )
{
2022-12-28 20:26:56 -03:00
if ( auto * editor = active_editor ( ) )
2022-12-04 15:02:33 -03:00
window ( ) - > set_title ( DeprecatedString : : formatted ( " {} - SQL Studio " , editor - > name ( ) ) ) ;
2022-12-28 20:26:56 -03:00
else
2022-04-09 00:24:37 -03:00
window ( ) - > set_title ( " SQL Studio " ) ;
}
void MainWidget : : on_editor_change ( )
{
2022-12-28 20:26:56 -03:00
auto * editor = active_editor ( ) ;
2022-08-14 20:35:24 -03:00
update_statusbar ( editor ) ;
update_editor_actions ( editor ) ;
2022-04-09 00:24:37 -03:00
}
void MainWidget : : update_statusbar ( ScriptEditor * editor )
{
2022-08-14 20:35:24 -03:00
if ( ! editor ) {
2023-06-04 05:24:38 -03:00
m_statusbar - > set_text ( 0 , { } ) ;
m_statusbar - > set_text ( 2 , { } ) ;
2022-08-14 20:35:24 -03:00
return ;
}
2022-12-29 00:48:22 -03:00
StringBuilder builder ;
2022-04-09 00:24:37 -03:00
if ( editor - > has_selection ( ) ) {
auto character_count = editor - > selected_text ( ) . length ( ) ;
auto word_count = editor - > number_of_selected_words ( ) ;
2023-04-11 13:39:26 -03:00
builder . appendff ( " Selected: {:'d} {} ({:'d} {}) " , character_count , character_count = = 1 ? " character " : " characters " , word_count , word_count ! = 1 ? " words " : " word " ) ;
2022-04-09 00:24:37 -03:00
}
2023-06-04 05:24:38 -03:00
m_statusbar - > set_text ( 0 , builder . to_string ( ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
m_statusbar - > set_text ( 2 , String : : formatted ( " Ln {:'d} Col {:'d} " , editor - > cursor ( ) . line ( ) + 1 , editor - > cursor ( ) . column ( ) ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2022-04-09 00:24:37 -03:00
}
void MainWidget : : update_editor_actions ( ScriptEditor * editor )
{
2022-08-14 20:35:24 -03:00
if ( ! editor ) {
2022-12-28 20:26:56 -03:00
m_save_action - > set_enabled ( false ) ;
m_save_as_action - > set_enabled ( false ) ;
m_save_all_action - > set_enabled ( false ) ;
m_run_script_action - > set_enabled ( false ) ;
2022-08-14 20:35:24 -03:00
m_copy_action - > set_enabled ( false ) ;
m_cut_action - > set_enabled ( false ) ;
m_paste_action - > set_enabled ( false ) ;
m_undo_action - > set_enabled ( false ) ;
m_redo_action - > set_enabled ( false ) ;
2022-12-28 20:26:56 -03:00
2022-08-14 20:35:24 -03:00
return ;
}
2022-12-28 20:26:56 -03:00
m_save_action - > set_enabled ( true ) ;
m_save_as_action - > set_enabled ( true ) ;
m_save_all_action - > set_enabled ( true ) ;
2022-12-29 01:09:23 -03:00
m_run_script_action - > set_enabled ( m_connection_id . has_value ( ) ) ;
2022-12-28 20:26:56 -03:00
2022-04-09 00:24:37 -03:00
m_copy_action - > set_enabled ( editor - > copy_action ( ) . is_enabled ( ) ) ;
m_cut_action - > set_enabled ( editor - > cut_action ( ) . is_enabled ( ) ) ;
m_paste_action - > set_enabled ( editor - > paste_action ( ) . is_enabled ( ) ) ;
m_undo_action - > set_enabled ( editor - > undo_action ( ) . is_enabled ( ) ) ;
m_redo_action - > set_enabled ( editor - > redo_action ( ) . is_enabled ( ) ) ;
}
2022-11-04 18:32:17 -03:00
void MainWidget : : drag_enter_event ( GUI : : DragEvent & event )
{
auto const & mime_types = event . mime_types ( ) ;
if ( mime_types . contains_slow ( " text/uri-list " ) )
event . accept ( ) ;
}
2022-04-09 00:24:37 -03:00
void MainWidget : : drop_event ( GUI : : DropEvent & drop_event )
{
drop_event . accept ( ) ;
window ( ) - > move_to_front ( ) ;
if ( drop_event . mime_data ( ) . has_urls ( ) ) {
auto urls = drop_event . mime_data ( ) . urls ( ) ;
if ( urls . is_empty ( ) )
return ;
for ( auto & url : urls ) {
auto & scheme = url . scheme ( ) ;
2023-08-12 01:52:41 -03:00
if ( ! scheme . bytes_as_string_view ( ) . equals_ignoring_ascii_case ( " file " sv ) )
2022-04-09 00:24:37 -03:00
continue ;
2023-04-14 16:12:03 -03:00
auto lexical_path = LexicalPath ( url . serialize_path ( ) ) ;
2022-12-29 01:22:12 -03:00
open_script_from_file ( lexical_path ) ;
2022-04-09 00:24:37 -03:00
}
}
}
2022-12-28 12:57:30 -03:00
void MainWidget : : read_next_sql_statement_of_editor ( )
2022-06-05 12:58:35 -03:00
{
2022-12-28 20:30:34 -03:00
if ( ! m_connection_id . has_value ( ) )
return ;
2022-06-05 12:58:35 -03:00
StringBuilder piece ;
do {
if ( ! piece . is_empty ( ) )
piece . append ( ' \n ' ) ;
auto line_maybe = read_next_line_of_editor ( ) ;
if ( ! line_maybe . has_value ( ) )
2022-12-28 12:57:30 -03:00
return ;
2022-06-05 12:58:35 -03:00
auto & line = line_maybe . value ( ) ;
auto lexer = SQL : : AST : : Lexer ( line ) ;
piece . append ( line ) ;
bool is_first_token = true ;
bool is_command = false ;
bool last_token_ended_statement = false ;
bool tokens_found = false ;
for ( SQL : : AST : : Token token = lexer . next ( ) ; token . type ( ) ! = SQL : : AST : : TokenType : : Eof ; token = lexer . next ( ) ) {
tokens_found = true ;
switch ( token . type ( ) ) {
case SQL : : AST : : TokenType : : ParenOpen :
+ + m_editor_line_level ;
break ;
case SQL : : AST : : TokenType : : ParenClose :
- - m_editor_line_level ;
break ;
case SQL : : AST : : TokenType : : SemiColon :
last_token_ended_statement = true ;
break ;
case SQL : : AST : : TokenType : : Period :
if ( is_first_token )
is_command = true ;
break ;
default :
last_token_ended_statement = is_command ;
break ;
}
is_first_token = false ;
}
if ( tokens_found )
m_editor_line_level = last_token_ended_statement ? 0 : ( m_editor_line_level > 0 ? m_editor_line_level : 1 ) ;
} while ( ( m_editor_line_level > 0 ) | | piece . is_empty ( ) ) ;
2022-12-28 12:57:30 -03:00
auto sql_statement = piece . to_deprecated_string ( ) ;
2022-12-28 20:30:34 -03:00
if ( auto statement_id = m_sql_client - > prepare_statement ( * m_connection_id , sql_statement ) ; statement_id . has_value ( ) ) {
2022-12-02 18:25:27 -03:00
m_sql_client - > async_execute_statement ( * statement_id , { } ) ;
2022-12-28 12:57:30 -03:00
} else {
2022-12-28 20:26:56 -03:00
auto * editor = active_editor ( ) ;
2022-12-28 12:57:30 -03:00
VERIFY ( editor ) ;
2022-06-05 12:58:35 -03:00
2022-12-28 12:57:30 -03:00
GUI : : MessageBox : : show_error ( window ( ) , DeprecatedString : : formatted ( " Could not parse {} \n {} " , editor - > path ( ) , sql_statement ) ) ;
}
2022-06-05 12:58:35 -03:00
}
2022-12-04 15:02:33 -03:00
Optional < DeprecatedString > MainWidget : : read_next_line_of_editor ( )
2022-06-05 12:58:35 -03:00
{
2022-12-28 20:26:56 -03:00
auto * editor = active_editor ( ) ;
2022-06-05 12:58:35 -03:00
if ( ! editor )
return { } ;
2022-12-28 20:26:56 -03:00
if ( m_current_line_for_parsing > = editor - > document ( ) . line_count ( ) )
2022-06-05 12:58:35 -03:00
return { } ;
2022-12-28 20:26:56 -03:00
auto result = editor - > document ( ) . line ( m_current_line_for_parsing ) . to_utf8 ( ) ;
+ + m_current_line_for_parsing ;
return result ;
2022-06-05 12:58:35 -03:00
}
2022-04-09 00:24:37 -03:00
}