2020-01-18 09:38:21 +01:00
/*
* Copyright ( c ) 2018 - 2020 , Andreas Kling < kling @ serenityos . org >
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* 1. Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
*
* 2. Redistributions in binary form must reproduce the above copyright notice ,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
* OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
2019-11-10 21:45:32 +01:00
# include "CursorTool.h"
2020-04-25 00:01:19 +03:00
# include "Debugger.h"
2019-10-27 12:55:10 +01:00
# include "Editor.h"
# include "EditorWrapper.h"
2019-10-23 21:13:08 +02:00
# include "FindInFilesWidget.h"
2019-11-08 20:55:58 +01:00
# include "FormEditorWidget.h"
2019-11-10 11:10:04 +01:00
# include "FormWidget.h"
2019-10-28 18:48:53 +01:00
# include "Locator.h"
2019-10-21 18:46:55 +02:00
# include "Project.h"
2019-10-21 20:17:32 +02:00
# include "TerminalWrapper.h"
2019-11-10 22:50:30 +01:00
# include "WidgetTool.h"
2019-11-11 19:13:36 +01:00
# include "WidgetTreeModel.h"
2020-01-15 20:42:54 -06:00
# include <AK/StringBuilder.h>
2020-04-25 00:01:19 +03:00
# include <LibCore/Event.h>
# include <LibCore/EventLoop.h>
2020-02-06 15:04:03 +01:00
# include <LibCore/File.h>
2020-04-25 00:01:19 +03:00
# include <LibDebug/DebugSession.h>
2020-02-06 20:33:02 +01:00
# include <LibGUI/AboutDialog.h>
# include <LibGUI/Action.h>
# include <LibGUI/ActionGroup.h>
# include <LibGUI/Application.h>
# include <LibGUI/BoxLayout.h>
# include <LibGUI/Button.h>
2020-02-07 20:07:15 +01:00
# include <LibGUI/CppSyntaxHighlighter.h>
2020-02-06 20:33:02 +01:00
# include <LibGUI/FilePicker.h>
# include <LibGUI/InputBox.h>
2020-03-13 00:54:04 +02:00
# include <LibGUI/JSSyntaxHighlighter.h>
2020-02-06 20:33:02 +01:00
# include <LibGUI/Label.h>
# include <LibGUI/Menu.h>
# include <LibGUI/MenuBar.h>
# include <LibGUI/MessageBox.h>
# include <LibGUI/Splitter.h>
# include <LibGUI/StackWidget.h>
# include <LibGUI/TabWidget.h>
# include <LibGUI/TableView.h>
# include <LibGUI/TextBox.h>
# include <LibGUI/TextEditor.h>
# include <LibGUI/ToolBar.h>
2020-04-23 17:44:49 +02:00
# include <LibGUI/ToolBarContainer.h>
2020-02-06 20:33:02 +01:00
# include <LibGUI/TreeView.h>
# include <LibGUI/Widget.h>
# include <LibGUI/Window.h>
2020-04-25 00:01:19 +03:00
# include <LibThread/Lock.h>
# include <LibThread/Thread.h>
2020-04-06 01:03:15 +03:00
# include <LibVT/TerminalWidget.h>
2019-10-21 18:46:55 +02:00
# include <stdio.h>
2019-12-29 09:25:50 +11:00
# include <sys/wait.h>
2019-10-21 18:46:55 +02:00
# include <unistd.h>
2019-10-27 13:10:37 +01:00
NonnullRefPtrVector < EditorWrapper > g_all_editor_wrappers ;
2019-10-27 12:55:10 +01:00
RefPtr < EditorWrapper > g_current_editor_wrapper ;
2020-03-12 00:18:13 +02:00
Function < void ( String ) > g_open_file ;
2019-10-27 12:55:10 +01:00
2019-10-22 21:38:58 +02:00
String g_currently_open_file ;
2019-10-23 20:54:41 +02:00
OwnPtr < Project > g_project ;
2020-02-02 15:07:41 +01:00
RefPtr < GUI : : Window > g_window ;
RefPtr < GUI : : TreeView > g_project_tree_view ;
RefPtr < GUI : : StackWidget > g_right_hand_stack ;
RefPtr < GUI : : Splitter > g_text_inner_splitter ;
RefPtr < GUI : : Widget > g_form_inner_container ;
2019-11-08 20:55:58 +01:00
RefPtr < FormEditorWidget > g_form_editor_widget ;
2019-10-22 21:38:58 +02:00
2020-02-02 15:07:41 +01:00
static RefPtr < GUI : : TabWidget > s_action_tab_widget ;
2019-11-05 20:44:39 +01:00
2020-02-02 15:07:41 +01:00
void add_new_editor ( GUI : : Widget & parent )
2019-10-27 12:55:10 +01:00
{
2020-04-25 00:01:19 +03:00
auto wrapper = EditorWrapper : : construct ( Debugger : : on_breakpoint_change ) ;
2019-11-05 20:44:39 +01:00
if ( s_action_tab_widget ) {
parent . insert_child_before ( wrapper , * s_action_tab_widget ) ;
} else {
parent . add_child ( wrapper ) ;
}
2019-10-27 12:55:10 +01:00
g_current_editor_wrapper = wrapper ;
2019-10-27 13:10:37 +01:00
g_all_editor_wrappers . append ( wrapper ) ;
2019-11-05 20:44:39 +01:00
wrapper - > editor ( ) . set_focus ( true ) ;
2019-10-27 12:55:10 +01:00
}
2019-11-08 20:55:58 +01:00
enum class EditMode {
Text ,
Form ,
} ;
void set_edit_mode ( EditMode mode )
{
if ( mode = = EditMode : : Text ) {
2019-11-08 21:19:49 +01:00
g_right_hand_stack - > set_active_widget ( g_text_inner_splitter ) ;
2019-11-08 20:55:58 +01:00
} else if ( mode = = EditMode : : Form ) {
2019-11-08 21:19:49 +01:00
g_right_hand_stack - > set_active_widget ( g_form_inner_container ) ;
2019-11-08 20:55:58 +01:00
}
}
2019-10-27 12:55:10 +01:00
EditorWrapper & current_editor_wrapper ( )
{
ASSERT ( g_current_editor_wrapper ) ;
return * g_current_editor_wrapper ;
}
Editor & current_editor ( )
2019-10-25 10:25:42 +02:00
{
2019-10-27 12:55:10 +01:00
return current_editor_wrapper ( ) . editor ( ) ;
2019-10-25 10:25:42 +02:00
}
2020-04-25 00:01:19 +03:00
NonnullRefPtr < EditorWrapper > get_editor_of_file ( const String & file )
{
for ( auto & wrapper : g_all_editor_wrappers ) {
String wrapper_file = wrapper . filename_label ( ) . text ( ) ;
if ( wrapper_file = = file | | String : : format ( " ./%s " , wrapper_file . characters ( ) ) = = file ) {
return wrapper ;
}
}
ASSERT_NOT_REACHED ( ) ;
}
String get_project_executable_path ( )
{
// e.g /my/project.files => /my/project
// TODO: Perhaps a Makefile rule for getting the value of $(PROGRAM) would be better?
return g_project - > path ( ) . substring ( 0 , g_project - > path ( ) . index_of ( " . " ) . value ( ) ) ;
}
2019-10-22 22:15:43 +02:00
static void build ( TerminalWrapper & ) ;
2019-10-22 22:18:46 +02:00
static void run ( TerminalWrapper & ) ;
2020-03-13 15:38:21 +02:00
void open_project ( String ) ;
2019-10-23 21:13:08 +02:00
void open_file ( const String & ) ;
2019-12-29 09:25:50 +11:00
bool make_is_available ( ) ;
2019-10-22 22:15:43 +02:00
2019-10-21 18:46:55 +02:00
int main ( int argc , char * * argv )
{
2020-04-07 03:08:54 -07:00
if ( pledge ( " stdio tty accept rpath cpath wpath shared_buffer proc exec unix fattr thread " , nullptr ) < 0 ) {
2020-01-11 21:17:56 +01:00
perror ( " pledge " ) ;
return 1 ;
}
2020-02-02 15:07:41 +01:00
GUI : : Application app ( argc , argv ) ;
2019-10-21 18:46:55 +02:00
2020-04-07 03:08:54 -07:00
if ( pledge ( " stdio tty accept rpath cpath wpath shared_buffer proc exec fattr thread " , nullptr ) < 0 ) {
2020-01-12 11:59:11 +01:00
perror ( " pledge " ) ;
return 1 ;
}
2019-11-05 21:07:39 +01:00
Function < void ( ) > update_actions ;
2020-02-02 15:07:41 +01:00
g_window = GUI : : Window : : construct ( ) ;
2019-11-11 19:37:01 +01:00
g_window - > set_rect ( 90 , 90 , 840 , 600 ) ;
2019-10-23 20:54:41 +02:00
g_window - > set_title ( " HackStudio " ) ;
2019-10-21 18:46:55 +02:00
2020-03-04 09:46:23 +01:00
auto & widget = g_window - > set_main_widget < GUI : : Widget > ( ) ;
2019-10-21 18:46:55 +02:00
2020-03-04 09:46:23 +01:00
widget . set_fill_with_background_color ( true ) ;
widget . set_layout < GUI : : VerticalBoxLayout > ( ) ;
2020-04-23 18:21:23 +02:00
widget . layout ( ) - > set_spacing ( 2 ) ;
2019-10-21 18:46:55 +02:00
2020-01-15 20:42:54 -06:00
StringBuilder path ;
path . append ( getenv ( " PATH " ) ) ;
if ( path . length ( ) )
path . append ( " : " ) ;
path . append ( " /bin:/usr/bin:/usr/local/bin " ) ;
setenv ( " PATH " , path . to_string ( ) . characters ( ) , true ) ;
2019-12-29 09:25:50 +11:00
if ( ! make_is_available ( ) )
2020-02-02 15:07:41 +01:00
GUI : : MessageBox : : show ( " The 'make' command is not available. You probably want to install the binutils, gcc, and make ports from the root of the Serenity repository. " , " Error " , GUI : : MessageBox : : Type : : Error , GUI : : MessageBox : : InputType : : OK , g_window ) ;
2019-12-29 09:25:50 +11:00
2020-03-13 15:38:21 +02:00
open_project ( " /home/anon/js/javascript.files " ) ;
2019-10-21 18:46:55 +02:00
2020-04-23 17:44:49 +02:00
auto & toolbar_container = widget . add < GUI : : ToolBarContainer > ( ) ;
auto & toolbar = toolbar_container . add < GUI : : ToolBar > ( ) ;
2019-10-21 18:46:55 +02:00
2019-12-28 15:24:30 +11:00
auto selected_file_names = [ & ] {
Vector < String > files ;
2020-02-02 15:07:41 +01:00
g_project_tree_view - > selection ( ) . for_each_index ( [ & ] ( const GUI : : ModelIndex & index ) {
2019-12-28 15:24:30 +11:00
files . append ( g_project - > model ( ) . data ( index ) . as_string ( ) ) ;
} ) ;
return files ;
} ;
2020-02-06 11:56:38 +01:00
auto new_action = GUI : : Action : : create ( " Add new file to project... " , { Mod_Ctrl , Key_N } , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/new.png " ) , [ & ] ( const GUI : : Action & ) {
2020-03-04 20:53:51 +01:00
auto input_box = GUI : : InputBox : : construct ( " Enter name of new file: " , " Add new file to project " , g_window ) ;
if ( input_box - > exec ( ) = = GUI : : InputBox : : ExecCancel )
2019-12-28 15:24:30 +11:00
return ;
2020-03-04 20:53:51 +01:00
auto filename = input_box - > text_value ( ) ;
2020-02-02 12:34:39 +01:00
auto file = Core : : File : : construct ( filename ) ;
if ( ! file - > open ( ( Core : : IODevice : : OpenMode ) ( Core : : IODevice : : WriteOnly | Core : : IODevice : : MustBeNew ) ) ) {
2020-02-02 15:07:41 +01:00
GUI : : MessageBox : : show ( String : : format ( " Failed to create '%s' " , filename . characters ( ) ) , " Error " , GUI : : MessageBox : : Type : : Error , GUI : : MessageBox : : InputType : : OK , g_window ) ;
2019-12-28 15:24:30 +11:00
return ;
}
if ( ! g_project - > add_file ( filename ) ) {
2020-02-02 15:07:41 +01:00
GUI : : MessageBox : : show ( String : : format ( " Failed to add '%s' to project " , filename . characters ( ) ) , " Error " , GUI : : MessageBox : : Type : : Error , GUI : : MessageBox : : InputType : : OK , g_window ) ;
2019-12-28 15:24:30 +11:00
// FIXME: Should we unlink the file here maybe?
return ;
}
2020-03-18 15:50:34 +02:00
g_project_tree_view - > toggle_index ( g_project_tree_view - > model ( ) - > index ( 0 , 0 ) ) ;
2019-12-28 15:24:30 +11:00
open_file ( filename ) ;
} ) ;
2020-02-06 11:56:38 +01:00
auto add_existing_file_action = GUI : : Action : : create ( " Add existing file to project... " , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/open.png " ) , [ & ] ( auto & ) {
2020-02-02 15:07:41 +01:00
auto result = GUI : : FilePicker : : get_open_filepath ( " Add existing file to project " ) ;
2019-12-28 15:24:30 +11:00
if ( ! result . has_value ( ) )
return ;
auto & filename = result . value ( ) ;
if ( ! g_project - > add_file ( filename ) ) {
2020-02-02 15:07:41 +01:00
GUI : : MessageBox : : show ( String : : format ( " Failed to add '%s' to project " , filename . characters ( ) ) , " Error " , GUI : : MessageBox : : Type : : Error , GUI : : MessageBox : : InputType : : OK , g_window ) ;
2019-12-28 15:24:30 +11:00
return ;
}
2020-03-18 15:50:34 +02:00
g_project_tree_view - > toggle_index ( g_project_tree_view - > model ( ) - > index ( 0 , 0 ) ) ;
2019-12-28 15:24:30 +11:00
open_file ( filename ) ;
} ) ;
2020-02-02 15:07:41 +01:00
auto delete_action = GUI : : CommonActions : : make_delete_action ( [ & ] ( const GUI : : Action & action ) {
2019-12-28 15:24:30 +11:00
( void ) action ;
auto files = selected_file_names ( ) ;
if ( files . is_empty ( ) )
return ;
String message ;
if ( files . size ( ) = = 1 ) {
message = String : : format ( " Really remove %s from the project? " , FileSystemPath ( files [ 0 ] ) . basename ( ) . characters ( ) ) ;
} else {
message = String : : format ( " Really remove %d files from the project? " , files . size ( ) ) ;
}
2020-02-02 15:07:41 +01:00
auto result = GUI : : MessageBox : : show (
2019-12-28 15:24:30 +11:00
message ,
" Confirm deletion " ,
2020-02-02 15:07:41 +01:00
GUI : : MessageBox : : Type : : Warning ,
GUI : : MessageBox : : InputType : : OKCancel ,
2019-12-28 15:24:30 +11:00
g_window ) ;
2020-02-02 15:07:41 +01:00
if ( result = = GUI : : MessageBox : : ExecCancel )
2019-12-28 15:24:30 +11:00
return ;
for ( auto & file : files ) {
if ( ! g_project - > remove_file ( file ) ) {
2020-02-02 15:07:41 +01:00
GUI : : MessageBox : : show (
2019-12-28 15:24:30 +11:00
String : : format ( " Removing file %s from the project failed. " , file . characters ( ) ) ,
" Removal failed " ,
2020-02-02 15:07:41 +01:00
GUI : : MessageBox : : Type : : Error ,
GUI : : MessageBox : : InputType : : OK ,
2019-12-28 15:24:30 +11:00
g_window ) ;
break ;
}
}
} ) ;
delete_action - > set_enabled ( false ) ;
2020-02-02 15:07:41 +01:00
auto project_tree_view_context_menu = GUI : : Menu : : construct ( " Project Files " ) ;
2019-12-28 15:24:30 +11:00
project_tree_view_context_menu - > add_action ( new_action ) ;
project_tree_view_context_menu - > add_action ( add_existing_file_action ) ;
project_tree_view_context_menu - > add_action ( delete_action ) ;
2020-03-04 19:07:55 +01:00
auto & outer_splitter = widget . add < GUI : : HorizontalSplitter > ( ) ;
g_project_tree_view = outer_splitter . add < GUI : : TreeView > ( ) ;
2019-12-23 00:14:24 +01:00
g_project_tree_view - > set_model ( g_project - > model ( ) ) ;
2020-02-02 15:07:41 +01:00
g_project_tree_view - > set_size_policy ( GUI : : SizePolicy : : Fixed , GUI : : SizePolicy : : Fill ) ;
2019-12-23 00:14:24 +01:00
g_project_tree_view - > set_preferred_size ( 140 , 0 ) ;
2020-03-18 15:50:34 +02:00
g_project_tree_view - > toggle_index ( g_project_tree_view - > model ( ) - > index ( 0 , 0 ) ) ;
2019-10-21 18:46:55 +02:00
2020-02-02 15:07:41 +01:00
g_project_tree_view - > on_context_menu_request = [ & ] ( const GUI : : ModelIndex & index , const GUI : : ContextMenuEvent & event ) {
2019-12-28 15:24:30 +11:00
if ( index . is_valid ( ) ) {
project_tree_view_context_menu - > popup ( event . screen_position ( ) ) ;
}
} ;
g_project_tree_view - > on_selection_change = [ & ] {
delete_action - > set_enabled ( ! g_project_tree_view - > selection ( ) . is_empty ( ) ) ;
} ;
2020-03-04 19:07:55 +01:00
g_right_hand_stack = outer_splitter . add < GUI : : StackWidget > ( ) ;
2019-11-08 20:55:58 +01:00
2020-02-23 10:57:42 +01:00
g_form_inner_container = g_right_hand_stack - > add < GUI : : Widget > ( ) ;
2020-03-04 09:43:54 +01:00
g_form_inner_container - > set_layout < GUI : : HorizontalBoxLayout > ( ) ;
2020-03-04 19:07:55 +01:00
auto & form_widgets_toolbar = g_form_inner_container - > add < GUI : : ToolBar > ( Orientation : : Vertical , 26 ) ;
form_widgets_toolbar . set_preferred_size ( 38 , 0 ) ;
2019-11-08 20:55:58 +01:00
2020-02-02 15:07:41 +01:00
GUI : : ActionGroup tool_actions ;
2019-11-10 22:50:30 +01:00
tool_actions . set_exclusive ( true ) ;
2020-04-21 17:19:27 +02:00
auto cursor_tool_action = GUI : : Action : : create_checkable ( " Cursor " , Gfx : : Bitmap : : load_from_file ( " /res/icons/widgets/Cursor.png " ) , [ & ] ( auto & ) {
2019-11-10 21:45:32 +01:00
g_form_editor_widget - > set_tool ( make < CursorTool > ( * g_form_editor_widget ) ) ;
2019-11-10 22:50:30 +01:00
} ) ;
cursor_tool_action - > set_checked ( true ) ;
tool_actions . add_action ( cursor_tool_action ) ;
2020-03-04 19:07:55 +01:00
form_widgets_toolbar . add_action ( cursor_tool_action ) ;
2019-11-10 11:00:55 +01:00
2020-02-02 15:07:41 +01:00
GUI : : WidgetClassRegistration : : for_each ( [ & ] ( const GUI : : WidgetClassRegistration & reg ) {
2020-02-17 21:49:17 +01:00
auto icon_path = String : : format ( " /res/icons/widgets/G%s.png " , reg . class_name ( ) . characters ( ) ) ;
2020-04-21 17:19:27 +02:00
auto action = GUI : : Action : : create_checkable ( reg . class_name ( ) , Gfx : : Bitmap : : load_from_file ( icon_path ) , [ & reg ] ( auto & ) {
2019-11-10 21:45:32 +01:00
g_form_editor_widget - > set_tool ( make < WidgetTool > ( * g_form_editor_widget , reg ) ) ;
2020-02-23 12:07:13 +01:00
auto widget = reg . construct ( ) ;
g_form_editor_widget - > form_widget ( ) . add_child ( widget ) ;
2019-11-10 11:10:04 +01:00
widget - > set_relative_rect ( 30 , 30 , 30 , 30 ) ;
2019-11-11 19:13:36 +01:00
g_form_editor_widget - > model ( ) . update ( ) ;
2019-11-10 11:00:55 +01:00
} ) ;
2019-11-10 22:50:30 +01:00
action - > set_checked ( false ) ;
tool_actions . add_action ( action ) ;
2020-03-04 19:07:55 +01:00
form_widgets_toolbar . add_action ( move ( action ) ) ;
2019-11-10 11:00:55 +01:00
} ) ;
2019-11-08 21:19:49 +01:00
2020-03-04 19:07:55 +01:00
auto & form_editor_inner_splitter = g_form_inner_container - > add < GUI : : HorizontalSplitter > ( ) ;
2019-11-08 21:48:58 +01:00
2020-03-04 19:07:55 +01:00
g_form_editor_widget = form_editor_inner_splitter . add < FormEditorWidget > ( ) ;
2019-11-08 21:48:58 +01:00
2020-03-04 19:07:55 +01:00
auto & form_editing_pane_container = form_editor_inner_splitter . add < GUI : : VerticalSplitter > ( ) ;
form_editing_pane_container . set_size_policy ( GUI : : SizePolicy : : Fixed , GUI : : SizePolicy : : Fill ) ;
form_editing_pane_container . set_preferred_size ( 190 , 0 ) ;
form_editing_pane_container . set_layout < GUI : : VerticalBoxLayout > ( ) ;
2019-11-08 21:48:58 +01:00
auto add_properties_pane = [ & ] ( auto & text , auto pane_widget ) {
2020-03-04 19:07:55 +01:00
auto & wrapper = form_editing_pane_container . add < GUI : : Widget > ( ) ;
wrapper . set_layout < GUI : : VerticalBoxLayout > ( ) ;
auto & label = wrapper . add < GUI : : Label > ( text ) ;
label . set_fill_with_background_color ( true ) ;
label . set_text_alignment ( Gfx : : TextAlignment : : CenterLeft ) ;
label . set_font ( Gfx : : Font : : default_bold_font ( ) ) ;
label . set_size_policy ( GUI : : SizePolicy : : Fill , GUI : : SizePolicy : : Fixed ) ;
label . set_preferred_size ( 0 , 16 ) ;
wrapper . add_child ( pane_widget ) ;
2019-11-08 21:48:58 +01:00
} ;
2020-02-23 10:57:42 +01:00
auto form_widget_tree_view = GUI : : TreeView : : construct ( ) ;
2019-11-11 19:13:36 +01:00
form_widget_tree_view - > set_model ( g_form_editor_widget - > model ( ) ) ;
2019-11-11 19:37:01 +01:00
form_widget_tree_view - > on_selection_change = [ & ] {
g_form_editor_widget - > selection ( ) . disable_hooks ( ) ;
g_form_editor_widget - > selection ( ) . clear ( ) ;
form_widget_tree_view - > selection ( ) . for_each_index ( [ & ] ( auto & index ) {
// NOTE: Make sure we don't add the FormWidget itself to the selection,
// since that would allow you to drag-move the FormWidget.
if ( index . internal_data ( ) ! = & g_form_editor_widget - > form_widget ( ) )
2020-02-02 15:07:41 +01:00
g_form_editor_widget - > selection ( ) . add ( * ( GUI : : Widget * ) index . internal_data ( ) ) ;
2019-11-11 19:37:01 +01:00
} ) ;
g_form_editor_widget - > update ( ) ;
g_form_editor_widget - > selection ( ) . enable_hooks ( ) ;
} ;
g_form_editor_widget - > selection ( ) . on_add = [ & ] ( auto & widget ) {
form_widget_tree_view - > selection ( ) . add ( g_form_editor_widget - > model ( ) . index_for_widget ( widget ) ) ;
} ;
g_form_editor_widget - > selection ( ) . on_remove = [ & ] ( auto & widget ) {
form_widget_tree_view - > selection ( ) . remove ( g_form_editor_widget - > model ( ) . index_for_widget ( widget ) ) ;
} ;
g_form_editor_widget - > selection ( ) . on_clear = [ & ] {
form_widget_tree_view - > selection ( ) . clear ( ) ;
} ;
2019-11-11 19:13:36 +01:00
add_properties_pane ( " Form widget tree: " , form_widget_tree_view ) ;
2020-02-23 10:57:42 +01:00
add_properties_pane ( " Widget properties: " , GUI : : TableView : : construct ( ) ) ;
2019-11-08 21:19:49 +01:00
2020-02-23 10:57:42 +01:00
g_text_inner_splitter = g_right_hand_stack - > add < GUI : : VerticalSplitter > ( ) ;
2019-11-08 21:19:49 +01:00
g_text_inner_splitter - > layout ( ) - > set_margins ( { 0 , 3 , 0 , 0 } ) ;
add_new_editor ( * g_text_inner_splitter ) ;
2019-10-23 20:54:41 +02:00
2020-02-02 15:07:41 +01:00
auto switch_to_next_editor = GUI : : Action : : create ( " Switch to next editor " , { Mod_Ctrl , Key_E } , [ & ] ( auto & ) {
2019-10-27 13:10:37 +01:00
if ( g_all_editor_wrappers . size ( ) < = 1 )
return ;
2019-11-05 20:55:50 +01:00
Vector < EditorWrapper * > wrappers ;
2019-11-08 21:19:49 +01:00
g_text_inner_splitter - > for_each_child_of_type < EditorWrapper > ( [ & ] ( auto & child ) {
2019-11-05 20:55:50 +01:00
wrappers . append ( & child ) ;
return IterationDecision : : Continue ;
} ) ;
2020-02-25 14:49:47 +01:00
for ( size_t i = 0 ; i < wrappers . size ( ) ; + + i ) {
2019-11-05 20:55:50 +01:00
if ( g_current_editor_wrapper . ptr ( ) = = wrappers [ i ] ) {
if ( i = = wrappers . size ( ) - 1 )
wrappers [ 0 ] - > editor ( ) . set_focus ( true ) ;
else
wrappers [ i + 1 ] - > editor ( ) . set_focus ( true ) ;
}
}
} ) ;
2020-02-02 15:07:41 +01:00
auto switch_to_previous_editor = GUI : : Action : : create ( " Switch to previous editor " , { Mod_Ctrl | Mod_Shift , Key_E } , [ & ] ( auto & ) {
2019-11-05 20:55:50 +01:00
if ( g_all_editor_wrappers . size ( ) < = 1 )
2019-10-27 13:10:37 +01:00
return ;
2019-11-05 20:55:50 +01:00
Vector < EditorWrapper * > wrappers ;
2019-11-08 21:19:49 +01:00
g_text_inner_splitter - > for_each_child_of_type < EditorWrapper > ( [ & ] ( auto & child ) {
2019-11-05 20:55:50 +01:00
wrappers . append ( & child ) ;
return IterationDecision : : Continue ;
} ) ;
for ( int i = wrappers . size ( ) - 1 ; i > = 0 ; - - i ) {
if ( g_current_editor_wrapper . ptr ( ) = = wrappers [ i ] ) {
if ( i = = 0 )
wrappers . last ( ) - > editor ( ) . set_focus ( true ) ;
else
wrappers [ i - 1 ] - > editor ( ) . set_focus ( true ) ;
}
2019-10-27 13:10:37 +01:00
}
} ) ;
2020-02-02 15:07:41 +01:00
auto remove_current_editor_action = GUI : : Action : : create ( " Remove current editor " , { Mod_Alt | Mod_Shift , Key_E } , [ & ] ( auto & ) {
2019-11-05 21:02:31 +01:00
if ( g_all_editor_wrappers . size ( ) < = 1 )
return ;
auto wrapper = g_current_editor_wrapper ;
switch_to_next_editor - > activate ( ) ;
2019-11-08 21:19:49 +01:00
g_text_inner_splitter - > remove_child ( * wrapper ) ;
2019-11-05 21:02:31 +01:00
g_all_editor_wrappers . remove_first_matching ( [ & ] ( auto & entry ) { return entry = = wrapper . ptr ( ) ; } ) ;
2019-11-05 21:07:39 +01:00
update_actions ( ) ;
2019-11-05 21:02:31 +01:00
} ) ;
2020-03-30 12:23:12 +02:00
auto open_action = GUI : : Action : : create ( " Open project... " , { Mod_Ctrl | Mod_Shift , Key_O } , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/open.png " ) , [ & ] ( auto & ) {
auto open_path = GUI : : FilePicker : : get_open_filepath ( " Open project " ) ;
2020-03-13 15:38:21 +02:00
if ( ! open_path . has_value ( ) )
return ;
open_project ( open_path . value ( ) ) ;
open_file ( g_project - > default_file ( ) ) ;
update_actions ( ) ;
} ) ;
2020-02-06 11:56:38 +01:00
auto save_action = GUI : : Action : : create ( " Save " , { Mod_Ctrl , Key_S } , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/save.png " ) , [ & ] ( auto & ) {
2019-10-24 22:25:26 +02:00
if ( g_currently_open_file . is_empty ( ) )
return ;
2019-10-27 12:55:10 +01:00
current_editor ( ) . write_to_file ( g_currently_open_file ) ;
2019-10-24 22:25:26 +02:00
} ) ;
2020-03-04 19:07:55 +01:00
toolbar . add_action ( new_action ) ;
toolbar . add_action ( add_existing_file_action ) ;
toolbar . add_action ( save_action ) ;
toolbar . add_action ( delete_action ) ;
toolbar . add_separator ( ) ;
2019-10-27 12:55:10 +01:00
2020-03-04 19:07:55 +01:00
toolbar . add_action ( GUI : : CommonActions : : make_cut_action ( [ & ] ( auto & ) { current_editor ( ) . cut_action ( ) . activate ( ) ; } ) ) ;
toolbar . add_action ( GUI : : CommonActions : : make_copy_action ( [ & ] ( auto & ) { current_editor ( ) . copy_action ( ) . activate ( ) ; } ) ) ;
toolbar . add_action ( GUI : : CommonActions : : make_paste_action ( [ & ] ( auto & ) { current_editor ( ) . paste_action ( ) . activate ( ) ; } ) ) ;
toolbar . add_separator ( ) ;
toolbar . add_action ( GUI : : CommonActions : : make_undo_action ( [ & ] ( auto & ) { current_editor ( ) . undo_action ( ) . activate ( ) ; } ) ) ;
toolbar . add_action ( GUI : : CommonActions : : make_redo_action ( [ & ] ( auto & ) { current_editor ( ) . redo_action ( ) . activate ( ) ; } ) ) ;
toolbar . add_separator ( ) ;
2019-10-24 22:25:26 +02:00
2019-12-23 00:14:24 +01:00
g_project_tree_view - > on_activation = [ & ] ( auto & index ) {
2020-02-02 15:07:41 +01:00
auto filename = g_project_tree_view - > model ( ) - > data ( index , GUI : : Model : : Role : : Custom ) . to_string ( ) ;
2019-10-23 20:54:41 +02:00
open_file ( filename ) ;
2019-10-21 18:46:55 +02:00
} ;
2020-02-23 10:57:42 +01:00
s_action_tab_widget = g_text_inner_splitter - > add < GUI : : TabWidget > ( ) ;
2019-10-23 19:52:34 +02:00
2020-02-02 15:07:41 +01:00
s_action_tab_widget - > set_size_policy ( GUI : : SizePolicy : : Fill , GUI : : SizePolicy : : Fixed ) ;
2019-11-05 20:44:39 +01:00
s_action_tab_widget - > set_preferred_size ( 0 , 24 ) ;
2019-10-26 12:30:49 +02:00
2020-04-06 01:03:15 +03:00
s_action_tab_widget - > on_change = [ & ] ( auto & ) { update_actions ( ) ; } ;
2019-10-26 12:30:49 +02:00
auto reveal_action_tab = [ & ] ( auto & widget ) {
2019-11-05 20:44:39 +01:00
if ( s_action_tab_widget - > preferred_size ( ) . height ( ) < 200 )
s_action_tab_widget - > set_preferred_size ( 0 , 200 ) ;
2020-04-04 11:10:07 +02:00
s_action_tab_widget - > set_active_widget ( & widget ) ;
2019-10-26 12:30:49 +02:00
} ;
auto hide_action_tabs = [ & ] {
2019-11-05 20:44:39 +01:00
s_action_tab_widget - > set_preferred_size ( 0 , 24 ) ;
2019-10-26 12:30:49 +02:00
} ;
2020-02-02 15:07:41 +01:00
auto hide_action_tabs_action = GUI : : Action : : create ( " Hide action tabs " , { Mod_Ctrl | Mod_Shift , Key_X } , [ & ] ( auto & ) {
2019-10-26 12:30:49 +02:00
hide_action_tabs ( ) ;
} ) ;
2020-04-06 01:03:15 +03:00
auto add_editor_action = GUI : : Action : : create ( " Add new editor " , { Mod_Ctrl | Mod_Alt , Key_E } ,
Gfx : : Bitmap : : load_from_file ( " /res/icons/TextEditor16.png " ) ,
[ & ] ( auto & ) {
add_new_editor ( * g_text_inner_splitter ) ;
update_actions ( ) ;
} ) ;
auto add_terminal_action = GUI : : Action : : create ( " Add new Terminal " , { Mod_Ctrl | Mod_Alt , Key_T } ,
Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/app-terminal.png " ) ,
[ & ] ( auto & ) {
auto & terminal = s_action_tab_widget - > add_tab < TerminalWrapper > ( " Terminal " ) ;
reveal_action_tab ( terminal ) ;
update_actions ( ) ;
terminal . terminal ( ) - > set_focus ( true ) ;
} ) ;
auto remove_current_terminal_action = GUI : : Action : : create ( " Remove current Terminal " , { Mod_Alt | Mod_Shift , Key_T } , [ & ] ( auto & ) {
auto widget = s_action_tab_widget - > active_widget ( ) ;
if ( ! widget )
return ;
if ( strcmp ( widget - > class_name ( ) , " TerminalWrapper " ) ! = 0 )
return ;
auto terminal = reinterpret_cast < TerminalWrapper * > ( widget ) ;
if ( ! terminal - > user_spawned ( ) )
return ;
s_action_tab_widget - > remove_tab ( * terminal ) ;
2019-11-05 21:07:39 +01:00
update_actions ( ) ;
2019-11-05 20:44:39 +01:00
} ) ;
2020-04-04 11:10:07 +02:00
auto & find_in_files_widget = s_action_tab_widget - > add_tab < FindInFilesWidget > ( " Find in files " ) ;
2020-04-06 01:03:15 +03:00
auto & terminal_wrapper = s_action_tab_widget - > add_tab < TerminalWrapper > ( " Build " , false ) ;
2019-10-21 20:17:32 +02:00
2020-03-04 19:07:55 +01:00
auto & locator = widget . add < Locator > ( ) ;
2019-10-28 18:48:53 +01:00
2020-02-02 15:07:41 +01:00
auto open_locator_action = GUI : : Action : : create ( " Open Locator... " , { Mod_Ctrl , Key_K } , [ & ] ( auto & ) {
2020-03-04 19:07:55 +01:00
locator . open ( ) ;
2019-10-28 18:48:53 +01:00
} ) ;
2020-04-21 16:01:00 +02:00
auto menubar = GUI : : MenuBar : : construct ( ) ;
2020-04-04 12:18:40 +02:00
auto & app_menu = menubar - > add_menu ( " HackStudio " ) ;
app_menu . add_action ( open_action ) ;
app_menu . add_action ( save_action ) ;
app_menu . add_separator ( ) ;
app_menu . add_action ( GUI : : CommonActions : : make_quit_action ( [ & ] ( auto & ) {
2019-10-21 22:13:20 +02:00
app . quit ( ) ;
} ) ) ;
2020-04-04 12:18:40 +02:00
auto & project_menu = menubar - > add_menu ( " Project " ) ;
project_menu . add_action ( new_action ) ;
project_menu . add_action ( add_existing_file_action ) ;
2019-10-26 21:43:08 +02:00
2020-04-04 12:18:40 +02:00
auto & edit_menu = menubar - > add_menu ( " Edit " ) ;
edit_menu . add_action ( GUI : : Action : : create ( " Find in files... " , { Mod_Ctrl | Mod_Shift , Key_F } , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/find.png " ) , [ & ] ( auto & ) {
2019-10-26 12:30:49 +02:00
reveal_action_tab ( find_in_files_widget ) ;
2020-04-04 11:10:07 +02:00
find_in_files_widget . focus_textbox_and_select_all ( ) ;
2019-10-23 21:02:02 +02:00
} ) ) ;
2020-02-06 11:56:38 +01:00
auto stop_action = GUI : : Action : : create ( " Stop " , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/stop.png " ) , [ & ] ( auto & ) {
2020-04-04 11:10:07 +02:00
terminal_wrapper . kill_running_command ( ) ;
2019-11-14 19:26:25 +03:00
} ) ;
stop_action - > set_enabled ( false ) ;
2020-04-04 11:10:07 +02:00
terminal_wrapper . on_command_exit = [ & ] {
2019-11-14 19:26:25 +03:00
stop_action - > set_enabled ( false ) ;
} ;
2020-02-06 11:56:38 +01:00
auto build_action = GUI : : Action : : create ( " Build " , { Mod_Ctrl , Key_B } , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/build.png " ) , [ & ] ( auto & ) {
2019-10-26 12:30:49 +02:00
reveal_action_tab ( terminal_wrapper ) ;
2019-10-22 22:15:43 +02:00
build ( terminal_wrapper ) ;
2019-11-14 19:26:25 +03:00
stop_action - > set_enabled ( true ) ;
2019-10-24 22:25:26 +02:00
} ) ;
2020-03-04 19:07:55 +01:00
toolbar . add_action ( build_action ) ;
2019-10-24 22:25:26 +02:00
2020-02-06 11:56:38 +01:00
auto run_action = GUI : : Action : : create ( " Run " , { Mod_Ctrl , Key_R } , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/play.png " ) , [ & ] ( auto & ) {
2019-10-26 12:30:49 +02:00
reveal_action_tab ( terminal_wrapper ) ;
2019-10-22 22:18:46 +02:00
run ( terminal_wrapper ) ;
2019-11-14 19:26:25 +03:00
stop_action - > set_enabled ( true ) ;
2019-10-24 22:25:26 +02:00
} ) ;
2020-04-25 00:01:19 +03:00
RefPtr < LibThread : : Thread > debugger_thread ;
auto debug_action = GUI : : Action : : create ( " Debug " , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/play-debug.png " ) , [ & ] ( auto & ) {
if ( g_project - > type ( ) ! = ProjectType : : Cpp ) {
GUI : : MessageBox : : show ( String : : format ( " Cannot debug current project type " , get_project_executable_path ( ) . characters ( ) ) , " Error " , GUI : : MessageBox : : Type : : Error , GUI : : MessageBox : : InputType : : OK , g_window ) ;
return ;
}
if ( ! GUI : : FilePicker : : file_exists ( get_project_executable_path ( ) ) ) {
GUI : : MessageBox : : show ( String : : format ( " Could not find file: %s. (did you build the project?) " , get_project_executable_path ( ) . characters ( ) ) , " Error " , GUI : : MessageBox : : Type : : Error , GUI : : MessageBox : : InputType : : OK , g_window ) ;
return ;
}
if ( Debugger : : the ( ) . session ( ) ) {
GUI : : MessageBox : : show ( " Debugger is already running " , " Error " , GUI : : MessageBox : : Type : : Error , GUI : : MessageBox : : InputType : : OK , g_window ) ;
return ;
}
Debugger : : the ( ) . set_executable_path ( get_project_executable_path ( ) ) ;
debugger_thread = adopt ( * new LibThread : : Thread ( Debugger : : start_static ) ) ;
debugger_thread - > start ( ) ;
} ) ;
auto continue_action = GUI : : Action : : create ( " Continue " , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/go-last.png " ) , [ & ] ( auto & ) {
pthread_mutex_lock ( Debugger : : the ( ) . continue_mutex ( ) ) ;
Debugger : : the ( ) . set_continue_type ( Debugger : : ContinueType : : Continue ) ;
pthread_cond_signal ( Debugger : : the ( ) . continue_cond ( ) ) ;
pthread_mutex_unlock ( Debugger : : the ( ) . continue_mutex ( ) ) ;
} ) ;
auto single_step_action = GUI : : Action : : create ( " Single Step " , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/single-step.png " ) , [ & ] ( auto & ) {
pthread_mutex_lock ( Debugger : : the ( ) . continue_mutex ( ) ) ;
Debugger : : the ( ) . set_continue_type ( Debugger : : ContinueType : : SourceSingleStep ) ;
pthread_cond_signal ( Debugger : : the ( ) . continue_cond ( ) ) ;
pthread_mutex_unlock ( Debugger : : the ( ) . continue_mutex ( ) ) ;
} ) ;
continue_action - > set_enabled ( false ) ;
single_step_action - > set_enabled ( false ) ;
2020-03-04 19:07:55 +01:00
toolbar . add_action ( run_action ) ;
toolbar . add_action ( stop_action ) ;
2020-04-25 00:01:19 +03:00
toolbar . add_action ( debug_action ) ;
toolbar . add_action ( continue_action ) ;
toolbar . add_action ( single_step_action ) ;
RefPtr < EditorWrapper > current_editor_in_execution ;
Debugger : : initialize (
[ & ] ( DebugInfo : : SourcePosition source_position ) {
dbg ( ) < < " Program stopped " ;
current_editor_in_execution = get_editor_of_file ( source_position . file_path ) ;
current_editor_in_execution - > editor ( ) . set_execution_position ( source_position . line_number - 1 ) ;
continue_action - > set_enabled ( true ) ;
single_step_action - > set_enabled ( true ) ;
} ,
[ & ] ( ) {
dbg ( ) < < " Program continued " ;
continue_action - > set_enabled ( false ) ;
single_step_action - > set_enabled ( false ) ;
if ( current_editor_in_execution ) {
current_editor_in_execution - > editor ( ) . clear_execution_position ( ) ;
}
} ,
[ & ] ( ) {
dbg ( ) < < " Program exited " ;
Core : : EventLoop : : main ( ) . post_event ( * g_window , make < Core : : DeferredInvocationEvent > ( [ = ] ( auto & ) {
GUI : : MessageBox : : show ( " Program Exited " , " Debugger " , GUI : : MessageBox : : Type : : Information , GUI : : MessageBox : : InputType : : OK , g_window ) ;
} ) ) ;
Core : : EventLoop : : wake ( ) ;
} ) ;
2019-10-24 22:25:26 +02:00
2020-04-04 12:18:40 +02:00
auto & build_menu = menubar - > add_menu ( " Build " ) ;
build_menu . add_action ( build_action ) ;
build_menu . add_action ( run_action ) ;
build_menu . add_action ( stop_action ) ;
2020-04-25 00:01:19 +03:00
build_menu . add_action ( debug_action ) ;
2020-04-04 12:18:40 +02:00
auto & view_menu = menubar - > add_menu ( " View " ) ;
view_menu . add_action ( hide_action_tabs_action ) ;
view_menu . add_action ( open_locator_action ) ;
view_menu . add_separator ( ) ;
view_menu . add_action ( add_editor_action ) ;
view_menu . add_action ( remove_current_editor_action ) ;
2020-04-06 01:03:15 +03:00
view_menu . add_action ( add_terminal_action ) ;
view_menu . add_action ( remove_current_terminal_action ) ;
2020-04-04 12:18:40 +02:00
auto & help_menu = menubar - > add_menu ( " Help " ) ;
help_menu . add_action ( GUI : : Action : : create ( " About " , [ & ] ( auto & ) {
2020-03-30 12:26:44 +02:00
GUI : : AboutDialog : : show ( " HackStudio " , Gfx : : Bitmap : : load_from_file ( " /res/icons/32x32/app-hack-studio.png " ) , g_window ) ;
2019-10-21 22:13:20 +02:00
} ) ) ;
app . set_menubar ( move ( menubar ) ) ;
2020-03-30 12:26:44 +02:00
g_window - > set_icon ( Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/app-hack-studio.png " ) ) ;
2019-10-21 19:03:09 +02:00
2019-10-23 20:54:41 +02:00
g_window - > show ( ) ;
2019-10-26 10:32:12 +02:00
2019-11-05 21:07:39 +01:00
update_actions = [ & ] ( ) {
2020-04-06 01:03:15 +03:00
auto is_remove_terminal_enabled = [ ] ( ) {
auto widget = s_action_tab_widget - > active_widget ( ) ;
if ( ! widget )
return false ;
if ( strcmp ( widget - > class_name ( ) , " TerminalWrapper " ) ! = 0 )
return false ;
if ( ! reinterpret_cast < TerminalWrapper * > ( widget ) - > user_spawned ( ) )
return false ;
return true ;
} ;
2019-11-05 21:07:39 +01:00
remove_current_editor_action - > set_enabled ( g_all_editor_wrappers . size ( ) > 1 ) ;
2020-04-06 01:03:15 +03:00
remove_current_terminal_action - > set_enabled ( is_remove_terminal_enabled ( ) ) ;
2019-11-05 21:07:39 +01:00
} ;
2020-03-12 00:18:13 +02:00
g_open_file = open_file ;
2020-03-13 15:38:21 +02:00
open_file ( g_project - > default_file ( ) ) ;
2019-11-05 21:07:39 +01:00
update_actions ( ) ;
2019-10-21 18:46:55 +02:00
return app . exec ( ) ;
}
2019-10-22 22:15:43 +02:00
void build ( TerminalWrapper & wrapper )
{
2020-03-13 15:38:21 +02:00
if ( g_project - > type ( ) = = ProjectType : : Javascript & & g_currently_open_file . ends_with ( " .js " ) )
wrapper . run_command ( String : : format ( " js -A %s " , g_currently_open_file . characters ( ) ) ) ;
else
wrapper . run_command ( " make " ) ;
2019-10-22 22:15:43 +02:00
}
2019-10-22 22:18:46 +02:00
void run ( TerminalWrapper & wrapper )
{
2020-03-13 15:38:21 +02:00
if ( g_project - > type ( ) = = ProjectType : : Javascript & & g_currently_open_file . ends_with ( " .js " ) )
wrapper . run_command ( String : : format ( " js %s " , g_currently_open_file . characters ( ) ) ) ;
else
wrapper . run_command ( " make run " ) ;
}
void open_project ( String filename )
{
FileSystemPath path ( filename ) ;
if ( chdir ( path . dirname ( ) . characters ( ) ) < 0 ) {
perror ( " chdir " ) ;
exit ( 1 ) ;
}
g_project = Project : : load_from_file ( filename ) ;
ASSERT ( g_project ) ;
if ( g_project_tree_view ) {
g_project_tree_view - > set_model ( g_project - > model ( ) ) ;
2020-03-18 15:50:34 +02:00
g_project_tree_view - > toggle_index ( g_project_tree_view - > model ( ) - > index ( 0 , 0 ) ) ;
2020-03-13 15:38:21 +02:00
g_project_tree_view - > update ( ) ;
}
2020-04-25 00:01:19 +03:00
if ( Debugger : : is_initialized ( ) ) {
Debugger : : the ( ) . reset_breakpoints ( ) ;
}
2019-10-22 22:18:46 +02:00
}
2019-10-23 20:54:41 +02:00
void open_file ( const String & filename )
{
2020-03-12 00:18:13 +02:00
auto project_file = g_project - > get_file ( filename ) ;
if ( project_file ) {
current_editor ( ) . set_document ( const_cast < GUI : : TextDocument & > ( project_file - > document ( ) ) ) ;
current_editor ( ) . set_readonly ( false ) ;
} else {
auto external_file = ProjectFile : : construct_with_name ( filename ) ;
current_editor ( ) . set_document ( const_cast < GUI : : TextDocument & > ( external_file - > document ( ) ) ) ;
current_editor ( ) . set_readonly ( true ) ;
}
2019-10-25 21:09:16 +02:00
2020-02-07 20:07:15 +01:00
if ( filename . ends_with ( " .cpp " ) | | filename . ends_with ( " .h " ) )
current_editor ( ) . set_syntax_highlighter ( make < GUI : : CppSyntaxHighlighter > ( ) ) ;
2020-03-13 00:54:04 +02:00
else if ( filename . ends_with ( " .js " ) )
current_editor ( ) . set_syntax_highlighter ( make < GUI : : JSSyntaxHighlighter > ( ) ) ;
else
current_editor ( ) . set_syntax_highlighter ( nullptr ) ;
2019-10-26 10:32:12 +02:00
2019-11-08 20:55:58 +01:00
if ( filename . ends_with ( " .frm " ) ) {
set_edit_mode ( EditMode : : Form ) ;
} else {
set_edit_mode ( EditMode : : Text ) ;
}
2019-10-26 10:32:12 +02:00
g_currently_open_file = filename ;
g_window - > set_title ( String : : format ( " %s - HackStudio " , g_currently_open_file . characters ( ) ) ) ;
2019-12-23 00:14:24 +01:00
g_project_tree_view - > update ( ) ;
2019-10-26 21:45:29 +02:00
2019-10-27 12:55:10 +01:00
current_editor_wrapper ( ) . filename_label ( ) . set_text ( filename ) ;
current_editor ( ) . set_focus ( true ) ;
2019-10-23 20:54:41 +02:00
}
2019-12-29 09:25:50 +11:00
bool make_is_available ( )
{
auto pid = fork ( ) ;
if ( pid < 0 )
return false ;
if ( ! pid ) {
int rc = execlp ( " make " , " make " , " --version " , nullptr ) ;
ASSERT ( rc < 0 ) ;
perror ( " execl " ) ;
exit ( 127 ) ;
}
int wstatus ;
waitpid ( pid , & wstatus , 0 ) ;
return WEXITSTATUS ( wstatus ) = = 0 ;
}