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-05-08 13:32:55 +03:00
# include "Debugger/DebugInfoWidget.h"
# include "Debugger/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"
2020-08-10 22:28:38 +02:00
# include "HackStudio.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>
2020-05-08 13:32:55 +03:00
# include <LibGUI/INISyntaxHighlighter.h>
2020-02-06 20:33:02 +01:00
# 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>
2020-06-28 13:40:10 -04:00
# include <spawn.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-08-10 22:28:38 +02:00
static 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 ,
} ;
2020-08-10 22:28:38 +02:00
static void set_edit_mode ( EditMode mode )
2019-11-08 20:55:58 +01:00
{
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
}
}
2020-08-15 10:58:16 +03:00
static void build ( TerminalWrapper & ) ;
static void run ( TerminalWrapper & ) ;
void open_project ( String ) ;
void open_file ( const String & ) ;
bool make_is_available ( ) ;
EditorWrapper & current_editor_wrapper ( )
2019-10-27 12:55:10 +01:00
{
ASSERT ( g_current_editor_wrapper ) ;
return * g_current_editor_wrapper ;
}
2020-08-10 22:28:38 +02:00
GUI : : TextEditor & 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-08-15 10:58:16 +03:00
static String get_full_path_of_serenity_source ( const String & file )
2020-04-25 00:01:19 +03:00
{
2020-08-15 10:58:16 +03:00
auto path_parts = LexicalPath ( file ) . parts ( ) ;
ASSERT ( path_parts [ 0 ] = = " .. " ) ;
path_parts . remove ( 0 ) ;
StringBuilder relative_path_builder ;
relative_path_builder . join ( " / " , path_parts ) ;
constexpr char SERENITY_LIBS_PREFIX [ ] = " /usr/src/serenity " ;
LexicalPath serenity_sources_base ( SERENITY_LIBS_PREFIX ) ;
return String : : format ( " %s/%s " , serenity_sources_base . string ( ) . characters ( ) , relative_path_builder . to_string ( ) . characters ( ) ) ;
}
NonnullRefPtr < EditorWrapper > get_editor_of_file ( const String & file )
{
String file_path = file ;
// TODO: We can probably do a more specific condition here, something like
// "if (file.starts_with("../Libraries/") || file.starts_with("../AK/"))"
if ( file . starts_with ( " ../ " ) ) {
file_path = get_full_path_of_serenity_source ( file ) ;
2020-04-25 00:01:19 +03:00
}
2020-08-15 10:58:16 +03:00
open_file ( file_path ) ;
return current_editor_wrapper ( ) ;
2020-04-25 00:01:19 +03:00
}
2020-08-10 22:28:38 +02:00
static String get_project_executable_path ( )
2020-04-25 00:01:19 +03:00
{
2020-08-05 16:38:25 +02:00
// e.g /my/project.hackstudio => /my/project
2020-04-25 00:01:19 +03:00
// 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-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-07-04 14:05:19 +02:00
auto app = GUI : : Application : : construct ( 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 ( ) ;
Misc: Use automatic window positioning in more applications
This is a follow up to #2936 / d3e3b4ae56aa79d9bde12ca1f143dcf116f89a4c.
Affected programs:
- Applications: Browser (Download, View source, Inspect DOM tree, JS
console), Terminal (Settings)
- Demos: Cube, Eyes, Fire, HelloWorld, LibGfxDemo, WebView,
WidgetGallery
- DevTools: HackStudio, Inspector, Profiler
- Games: 2048, Minesweeper, Snake, Solitaire
- Userland: test-web
A few have been left out where manual positioning is done on purpose,
e.g. ClipboardManager (to be close to the menu bar) or VisualBuilder (to
preserve alignment of the multiple application windows).
2020-08-15 17:24:05 +02:00
g_window - > resize ( 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-07-15 20:45:11 -06:00
GUI : : MessageBox : : show ( g_window , " 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 ) ;
2019-12-29 09:25:50 +11:00
2020-08-05 16:39:55 +02:00
String argument_absolute_path ;
if ( argc > = 2 )
argument_absolute_path = Core : : File : : real_path_for ( argv [ 1 ] ) ;
if ( ! argument_absolute_path . is_empty ( ) & & argument_absolute_path . ends_with ( " .hackstudio " ) )
open_project ( argument_absolute_path ) ;
else
2020-08-05 17:25:01 +02:00
open_project ( " /home/anon/Source/little/little.hackstudio " ) ;
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-07-16 07:54:42 -06:00
String filename ;
if ( GUI : : InputBox : : show ( filename , g_window , " Enter name of new file: " , " Add new file to project " ) ! = GUI : : InputBox : : ExecOK )
2019-12-28 15:24:30 +11:00
return ;
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-07-15 20:45:11 -06:00
GUI : : MessageBox : : show ( g_window , String : : format ( " Failed to create '%s' " , filename . characters ( ) ) , " Error " , GUI : : MessageBox : : Type : : Error ) ;
2019-12-28 15:24:30 +11:00
return ;
}
if ( ! g_project - > add_file ( filename ) ) {
2020-07-15 20:45:11 -06:00
GUI : : MessageBox : : show ( g_window , String : : format ( " Failed to add '%s' to project " , filename . characters ( ) ) , " Error " , GUI : : MessageBox : : Type : : Error ) ;
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-07-15 19:52:02 -06:00
auto result = GUI : : FilePicker : : get_open_filepath ( g_window , " 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-07-15 20:45:11 -06:00
GUI : : MessageBox : : show ( g_window , String : : format ( " Failed to add '%s' to project " , filename . characters ( ) ) , " Error " , GUI : : MessageBox : : Type : : Error ) ;
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 ) {
2020-05-26 14:52:44 +03:00
message = String : : format ( " Really remove %s from the project? " , LexicalPath ( files [ 0 ] ) . basename ( ) . characters ( ) ) ;
2019-12-28 15:24:30 +11:00
} else {
message = String : : format ( " Really remove %d files from the project? " , files . size ( ) ) ;
}
2020-07-15 20:45:11 -06:00
auto result = GUI : : MessageBox : : show ( g_window ,
2019-12-28 15:24:30 +11:00
message ,
" Confirm deletion " ,
2020-02-02 15:07:41 +01:00
GUI : : MessageBox : : Type : : Warning ,
2020-07-15 20:45:11 -06:00
GUI : : MessageBox : : InputType : : OKCancel ) ;
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-07-15 20:45:11 -06:00
GUI : : MessageBox : : show ( g_window ,
2019-12-28 15:24:30 +11:00
String : : format ( " Removing file %s from the project failed. " , file . characters ( ) ) ,
" Removal failed " ,
2020-07-15 20:45:11 -06:00
GUI : : MessageBox : : Type : : Error ) ;
2019-12-28 15:24:30 +11:00
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 & ) {
2020-07-15 19:52:02 -06:00
auto open_path = GUI : : FilePicker : : get_open_filepath ( g_window , " 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 } ,
2020-07-04 19:41:43 +02:00
Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/app-text-editor.png " ) ,
2020-04-06 01:03:15 +03:00
[ & ] ( 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 ) ;
2020-05-04 12:23:30 +03:00
auto & debug_info_widget = s_action_tab_widget - > add_tab < DebugInfoWidget > ( " Debug " ) ;
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 & ) {
2020-07-04 14:05:19 +02:00
app - > quit ( ) ;
2019-10-21 22:13:20 +02:00
} ) ) ;
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-04-25 14:17:02 +02:00
auto stop_action = GUI : : Action : : create ( " Stop " , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/program-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 ) ;
2020-04-25 14:17:02 +02:00
toolbar . add_separator ( ) ;
2019-10-24 22:25:26 +02:00
2020-04-25 14:17:02 +02:00
auto run_action = GUI : : Action : : create ( " Run " , { Mod_Ctrl , Key_R } , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/program-run.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 ;
2020-04-25 14:17:02 +02:00
auto debug_action = GUI : : Action : : create ( " Debug " , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/debug-run.png " ) , [ & ] ( auto & ) {
2020-04-25 00:01:19 +03:00
if ( g_project - > type ( ) ! = ProjectType : : Cpp ) {
2020-07-15 20:45:11 -06:00
GUI : : MessageBox : : show ( g_window , String : : format ( " Cannot debug current project type " , get_project_executable_path ( ) . characters ( ) ) , " Error " , GUI : : MessageBox : : Type : : Error ) ;
2020-04-25 00:01:19 +03:00
return ;
}
if ( ! GUI : : FilePicker : : file_exists ( get_project_executable_path ( ) ) ) {
2020-07-15 20:45:11 -06:00
GUI : : MessageBox : : show ( g_window , String : : format ( " Could not find file: %s. (did you build the project?) " , get_project_executable_path ( ) . characters ( ) ) , " Error " , GUI : : MessageBox : : Type : : Error ) ;
2020-04-25 00:01:19 +03:00
return ;
}
if ( Debugger : : the ( ) . session ( ) ) {
2020-07-15 20:45:11 -06:00
GUI : : MessageBox : : show ( g_window , " Debugger is already running " , " Error " , GUI : : MessageBox : : Type : : Error ) ;
2020-04-25 00:01:19 +03:00
return ;
}
Debugger : : the ( ) . set_executable_path ( get_project_executable_path ( ) ) ;
debugger_thread = adopt ( * new LibThread : : Thread ( Debugger : : start_static ) ) ;
debugger_thread - > start ( ) ;
} ) ;
2020-04-25 14:17:02 +02:00
auto continue_action = GUI : : Action : : create ( " Continue " , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/debug-continue.png " ) , [ & ] ( auto & ) {
2020-04-25 00:01:19 +03:00
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 ( ) ) ;
} ) ;
2020-04-25 14:17:02 +02:00
auto single_step_action = GUI : : Action : : create ( " Single Step " , Gfx : : Bitmap : : load_from_file ( " /res/icons/16x16/debug-single-step.png " ) , [ & ] ( auto & ) {
2020-04-25 00:01:19 +03:00
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 14:17:02 +02:00
toolbar . add_separator ( ) ;
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 (
2020-05-04 12:23:30 +03:00
[ & ] ( const PtraceRegisters & regs ) {
2020-04-25 00:01:19 +03:00
dbg ( ) < < " Program stopped " ;
2020-05-04 12:23:30 +03:00
2020-05-08 14:55:02 +03:00
ASSERT ( Debugger : : the ( ) . session ( ) ) ;
const auto & debug_session = * Debugger : : the ( ) . session ( ) ;
auto source_position = debug_session . debug_info ( ) . get_source_position ( regs . eip ) ;
2020-05-05 14:14:29 +03:00
if ( ! source_position . has_value ( ) ) {
dbg ( ) < < " Could not find source position for address: " < < ( void * ) regs . eip ;
return Debugger : : HasControlPassedToUser : : No ;
}
2020-08-15 10:58:16 +03:00
Core : : EventLoop : : main ( ) . post_event (
* g_window ,
make < Core : : DeferredInvocationEvent > (
[ & , source_position ] ( auto & ) {
current_editor_in_execution = get_editor_of_file ( source_position . value ( ) . file_path ) ;
current_editor_in_execution - > editor ( ) . set_execution_position ( source_position . value ( ) . line_number - 1 ) ;
debug_info_widget . update_state ( * Debugger : : the ( ) . session ( ) , regs ) ;
continue_action - > set_enabled ( true ) ;
single_step_action - > set_enabled ( true ) ;
reveal_action_tab ( debug_info_widget ) ;
} ) ) ;
Core : : EventLoop : : wake ( ) ;
2020-05-05 14:14:29 +03:00
return Debugger : : HasControlPassedToUser : : Yes ;
2020-04-25 00:01:19 +03:00
} ,
[ & ] ( ) {
dbg ( ) < < " Program continued " ;
2020-08-15 10:58:16 +03:00
Core : : EventLoop : : main ( ) . post_event ( * g_window , make < Core : : DeferredInvocationEvent > ( [ & ] ( auto & ) {
continue_action - > set_enabled ( false ) ;
single_step_action - > set_enabled ( false ) ;
if ( current_editor_in_execution ) {
current_editor_in_execution - > editor ( ) . clear_execution_position ( ) ;
}
} ) ) ;
Core : : EventLoop : : wake ( ) ;
2020-04-25 00:01:19 +03:00
} ,
[ & ] ( ) {
dbg ( ) < < " Program exited " ;
2020-08-15 10:58:16 +03:00
Core : : EventLoop : : main ( ) . post_event ( * g_window , make < Core : : DeferredInvocationEvent > ( [ & ] ( auto & ) {
debug_info_widget . program_stopped ( ) ;
hide_action_tabs ( ) ;
2020-07-15 20:45:11 -06:00
GUI : : MessageBox : : show ( g_window , " Program Exited " , " Debugger " , GUI : : MessageBox : : Type : : Information ) ;
2020-04-25 00:01:19 +03:00
} ) ) ;
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 ) ;
2020-04-25 14:17:02 +02:00
build_menu . add_separator ( ) ;
2020-04-04 12:18:40 +02:00
build_menu . add_action ( run_action ) ;
build_menu . add_action ( stop_action ) ;
2020-04-25 14:17:02 +02:00
build_menu . add_separator ( ) ;
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
} ) ) ;
2020-07-04 14:05:19 +02:00
app - > set_menubar ( move ( menubar ) ) ;
2019-10-21 22:13:20 +02:00
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-08-05 16:39:55 +02:00
if ( ! argument_absolute_path . is_empty ( ) & & ! argument_absolute_path . ends_with ( " .hackstudio " ) )
open_file ( argument_absolute_path ) ;
else
open_file ( g_project - > default_file ( ) ) ;
2019-11-05 21:07:39 +01:00
update_actions ( ) ;
2020-07-04 14:05:19 +02:00
return app - > exec ( ) ;
2019-10-21 18:46:55 +02:00
}
2019-10-22 22:15:43 +02:00
void build ( TerminalWrapper & wrapper )
{
2020-05-07 15:24:18 +01:00
if ( g_project - > type ( ) = = ProjectType : : JavaScript & & g_currently_open_file . ends_with ( " .js " ) )
2020-03-13 15:38:21 +02:00
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-05-07 15:24:18 +01:00
if ( g_project - > type ( ) = = ProjectType : : JavaScript & & g_currently_open_file . ends_with ( " .js " ) )
2020-03-13 15:38:21 +02:00
wrapper . run_command ( String : : format ( " js %s " , g_currently_open_file . characters ( ) ) ) ;
else
wrapper . run_command ( " make run " ) ;
}
void open_project ( String filename )
{
2020-05-26 14:52:44 +03:00
LexicalPath lexical_path ( filename ) ;
if ( chdir ( lexical_path . dirname ( ) . characters ( ) ) < 0 ) {
2020-03-13 15:38:21 +02:00
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 ( ) ) ) ;
2020-07-14 17:02:46 -04:00
current_editor ( ) . set_mode ( GUI : : TextEditor : : Editable ) ;
2020-03-12 00:18:13 +02:00
} else {
auto external_file = ProjectFile : : construct_with_name ( filename ) ;
current_editor ( ) . set_document ( const_cast < GUI : : TextDocument & > ( external_file - > document ( ) ) ) ;
2020-07-14 17:02:46 -04:00
current_editor ( ) . set_mode ( GUI : : TextEditor : : ReadOnly ) ;
2020-03-12 00:18:13 +02:00
}
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 > ( ) ) ;
2020-05-07 15:32:05 +01:00
else if ( filename . ends_with ( " .ini " ) )
current_editor ( ) . set_syntax_highlighter ( make < GUI : : IniSyntaxHighlighter > ( ) ) ;
2020-03-13 00:54:04 +02:00
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 ( )
{
2020-06-28 13:40:10 -04:00
pid_t pid ;
const char * argv [ ] = { " make " , " --version " , nullptr } ;
if ( ( errno = posix_spawnp ( & pid , " make " , nullptr , nullptr , const_cast < char * * > ( argv ) , environ ) ) ) {
perror ( " posix_spawn " ) ;
2019-12-29 09:25:50 +11:00
return false ;
}
int wstatus ;
waitpid ( pid , & wstatus , 0 ) ;
return WEXITSTATUS ( wstatus ) = = 0 ;
}