diff --git a/src/api.cc b/src/api.cc index 0cf4d8c..be1381d 100644 --- a/src/api.cc +++ b/src/api.cc @@ -13,7 +13,7 @@ PluginApi::PluginApi(Notebook* notebook, Menu* menu) { this->menu = menu; DEBUG("Initiating plugins(from plugins.py).."); #ifndef __APPLE__ - InitPlugins(); //TODO: fix this + //InitPlugins(); //TODO: fix this #endif DEBUG("Plugins initiated.."); } diff --git a/src/cmake.cc b/src/cmake.cc index 0d9fefc..d2ecfe6 100644 --- a/src/cmake.cc +++ b/src/cmake.cc @@ -40,14 +40,14 @@ CMake::CMake(const boost::filesystem::path &path) { } if(project_path!="") { if(boost::filesystem::exists(project_path.string()+"/CMakeLists.txt") && !boost::filesystem::exists(project_path.string()+"/compile_commands.json")) - create_compile_commands(project_path.string()); + create_compile_commands(project_path); } } -bool CMake::create_compile_commands(const std::string &path) { - Singleton::terminal()->print("Creating "+boost::filesystem::path(path+"/compile_commands.json").string()+"\n"); +bool CMake::create_compile_commands(const boost::filesystem::path &path) { + Singleton::terminal()->print("Creating "+path.string()+"/compile_commands.json\n"); //TODO: Windows... - if(Singleton::terminal()->execute("cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 2>&1", path)==EXIT_SUCCESS) + if(Singleton::terminal()->execute("cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON", path)==EXIT_SUCCESS) return true; return false; } diff --git a/src/cmake.h b/src/cmake.h index f300f15..8be9d13 100644 --- a/src/cmake.h +++ b/src/cmake.h @@ -8,7 +8,7 @@ class CMake { public: CMake(const boost::filesystem::path &path); std::vector > > get_functions_parameters(const std::string &name); - static bool create_compile_commands(const std::string &path); + static bool create_compile_commands(const boost::filesystem::path &path); std::vector paths; std::vector files; diff --git a/src/config.cc b/src/config.cc index cf2a7c1..7aac125 100644 --- a/src/config.cc +++ b/src/config.cc @@ -12,6 +12,7 @@ MainConfig::MainConfig() { GenerateSource(); GenerateTheme(); GenerateDirectoryFilter(); + Singleton::Config::terminal()->make_command=cfg.get("project.make_command"); } void MainConfig::GenerateTheme() { diff --git a/src/directories.cc b/src/directories.cc index 088a0f7..82d9dba 100644 --- a/src/directories.cc +++ b/src/directories.cc @@ -72,14 +72,14 @@ void Directories::open_folder(const boost::filesystem::path& dir_path) { current_path=new_path; - if(selected_path.size()>0) + if(selected_path!="") select_path(selected_path); DEBUG("Folder opened"); } -void Directories::select_path(const std::string &path) { +void Directories::select_path(const boost::filesystem::path &path) { tree_store->foreach_iter([this, &path](const Gtk::TreeModel::iterator& iter){ - if(iter->get_value(column_record.path)==path) { + if(iter->get_value(column_record.path)==path.string()) { auto tree_path=Gtk::TreePath(iter); tree_view.expand_to_path(tree_path); tree_view.set_cursor(tree_path); diff --git a/src/directories.h b/src/directories.h index 57d36da..d8dcb77 100644 --- a/src/directories.h +++ b/src/directories.h @@ -29,7 +29,7 @@ public: Directories(); void open_folder(const boost::filesystem::path& dir_path=""); - void select_path(const std::string &path); + void select_path(const boost::filesystem::path &path); std::function on_row_activated; std::unique_ptr cmake; @@ -41,7 +41,7 @@ private: Gtk::TreeView tree_view; Glib::RefPtr tree_store; ColumnRecord column_record; - std::string selected_path; + boost::filesystem::path selected_path; }; #endif // JUCI_DIRECTORIES_H_ diff --git a/src/files.h b/src/files.h index 14a3610..4ac51f0 100644 --- a/src/files.h +++ b/src/files.h @@ -53,7 +53,13 @@ const std::string configjson = " \"source_goto_method\": \"m\",\n" " \"source_rename\": \"r\",\n" " \"compile_and_run\": \"Return\",\n" -" \"compile\": \"Return\"\n" +" \"compile\": \"Return\",\n" +" \"run_command\": \"Return\",\n" +" \"kill_last_running\": \"Escape\",\n" +" \"force_kill_last_running\": \"Escape\"\n" +" },\n" +" \"project\": {\n" +" \"make_command\": \"make\"\n" " },\n" " \"directoryfilter\": {\n" " \"ignore\": [\n" @@ -76,6 +82,9 @@ const std::string menuxml = " \n" " \n" " \n" +" \n" +" \n" +" \n" " \n" " \n" " \n" @@ -103,6 +112,9 @@ const std::string menuxml = " \n" " \n" " \n" +" \n" +" \n" +" \n" " \n" " \n" " \n" diff --git a/src/juci.cc b/src/juci.cc index e6a377b..9f0ce9a 100644 --- a/src/juci.cc +++ b/src/juci.cc @@ -1,6 +1,7 @@ #include "juci.h" #include "singletons.h" #include "config.h" +#include void init_logging() { add_common_attributes(); @@ -19,13 +20,16 @@ int app::on_command_line(const Glib::RefPtr &cmd) { ctx.parse(argc, argv); if(argc>=2) { for(int c=1;c &cmd); void on_activate(); diff --git a/src/notebook.cc b/src/notebook.cc index e77e000..146cc8e 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -34,51 +34,49 @@ Source::View* Notebook::get_current_view() { return get_view(get_current_page()); } -void Notebook::open(std::string path) { +void Notebook::open(const boost::filesystem::path &file_path) { INFO("Notebook open file"); INFO("Notebook create page"); for(int c=0;cfile_path) { + if(file_path==get_view(c)->file_path) { set_current_page(c); get_current_view()->grab_focus(); return; } } - std::ifstream can_read(path); + std::ifstream can_read(file_path.string()); if(!can_read) { - Singleton::terminal()->print("Error: could not open "+path+"\n"); + Singleton::terminal()->print("Error: could not open "+file_path.string()+"\n"); return; } can_read.close(); - auto language=Source::guess_language(path); + auto language=Source::guess_language(file_path); if(language && (language->get_id()=="chdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc")) { - std::string project_path; + boost::filesystem::path project_path; if(directories.cmake && directories.cmake->project_path!="") - project_path=directories.cmake->project_path.string(); + project_path=directories.cmake->project_path; else { - auto parent_path=boost::filesystem::path(path).parent_path(); - project_path=parent_path.string(); - CMake cmake(parent_path); + project_path=file_path.parent_path(); + CMake cmake(project_path); if(cmake.project_path!="") { - project_path=cmake.project_path.string(); - Singleton::terminal()->print("Project path for "+path+" set to "+project_path+"\n"); + project_path=cmake.project_path; + Singleton::terminal()->print("Project path for "+file_path.string()+" set to "+project_path.string()+"\n"); } else - Singleton::terminal()->print("Error: could not find project path for "+path+"\n"); + Singleton::terminal()->print("Error: could not find project path for "+file_path.string()+"\n"); } - source_views.emplace_back(new Source::ClangView(path, project_path)); + source_views.emplace_back(new Source::ClangView(file_path, project_path)); } else - source_views.emplace_back(new Source::GenericView(path, language)); + source_views.emplace_back(new Source::GenericView(file_path, language)); scrolled_windows.emplace_back(new Gtk::ScrolledWindow()); hboxes.emplace_back(new Gtk::HBox()); scrolled_windows.back()->add(*source_views.back()); hboxes.back()->pack_start(*scrolled_windows.back(), true, true); - boost::filesystem::path file_path(source_views.back()->file_path); std::string title=file_path.filename().string(); append_page(*hboxes.back(), title); show_all_children(); @@ -89,8 +87,7 @@ void Notebook::open(std::string path) { //Add star on tab label when the page is not saved: auto source_view=get_current_view(); get_current_view()->get_buffer()->signal_modified_changed().connect([this, source_view]() { - boost::filesystem::path file_path(source_view->file_path); - std::string title=file_path.filename().string(); + std::string title=source_view->file_path.filename().string(); if(source_view->get_buffer()->get_modified()) title+="*"; int page=-1; @@ -117,20 +114,20 @@ bool Notebook::save(int page) { if (view->file_path != "" && view->get_buffer()->get_modified()) { if(juci::filesystem::write(view->file_path, view->get_buffer())) { view->get_buffer()->set_modified(false); - Singleton::terminal()->print("File saved to: " +view->file_path+"\n"); + Singleton::terminal()->print("File saved to: " +view->file_path.string()+"\n"); //If CMakeLists.txt have been modified: //TODO: recreate cmake even without directories open? - if(boost::filesystem::path(view->file_path).filename().string()=="CMakeLists.txt") { - if(directories.cmake && directories.cmake->project_path!="" && boost::filesystem::path(view->file_path)>=directories.cmake->project_path && CMake::create_compile_commands(directories.cmake->project_path.string())) { + if(view->file_path.filename()=="CMakeLists.txt") { + if(directories.cmake && directories.cmake->project_path!="" && view->file_path>=directories.cmake->project_path && CMake::create_compile_commands(directories.cmake->project_path)) { directories.open_folder(); for(auto source_view: source_views) { if(auto source_clang_view=dynamic_cast(source_view)) { if(directories.cmake->project_path.string()==source_clang_view->project_path) { if(source_clang_view->restart_parse()) - Singleton::terminal()->print("Reparsing "+source_clang_view->file_path+"\n"); + Singleton::terminal()->async_print("Reparsing "+source_clang_view->file_path.string()+"\n"); else - Singleton::terminal()->print("Already reparsing "+source_clang_view->file_path+". Please reopen the file manually.\n"); + Singleton::terminal()->async_print("Already reparsing "+source_clang_view->file_path.string()+". Please reopen the file manually.\n"); } } } @@ -139,7 +136,7 @@ bool Notebook::save(int page) { return true; } - Singleton::terminal()->print("Error: could not save file " +view->file_path+"\n"); + Singleton::terminal()->print("Error: could not save file " +view->file_path.string()+"\n"); } return false; } @@ -177,7 +174,7 @@ bool Notebook::close_current_page() { bool Notebook::save_modified_dialog() { INFO("Notebook::save_modified_dialog"); Gtk::MessageDialog dialog((Gtk::Window&)(*get_toplevel()), "Save file!", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO); - dialog.set_secondary_text("Do you want to save: " + get_current_view()->file_path+" ?"); + dialog.set_secondary_text("Do you want to save: " + get_current_view()->file_path.string()+" ?"); int result = dialog.run(); if(result==Gtk::RESPONSE_YES) { save_current(); diff --git a/src/notebook.h b/src/notebook.h index 8ad8480..a77040f 100644 --- a/src/notebook.h +++ b/src/notebook.h @@ -17,12 +17,11 @@ public: int size(); Source::View* get_current_view(); bool close_current_page(); - void open(std::string filename); + void open(const boost::filesystem::path &file_path); bool save(int page); bool save_current(); private: - bool make_compile_commands(const std::string &path); bool save_modified_dialog(); Directories &directories; std::vector source_views; //Is NOT freed in destructor, this is intended for quick program exit. diff --git a/src/singletons.cc b/src/singletons.cc index 07b2a39..7039985 100644 --- a/src/singletons.cc +++ b/src/singletons.cc @@ -1,11 +1,10 @@ #include "singletons.h" std::unique_ptr Singleton::Config::source_=std::unique_ptr(new Source::Config()); -std::unique_ptr Singleton::Config::directories_=std::unique_ptr(new Directories::Config()); -std::unique_ptr Singleton::terminal_=std::unique_ptr(); +std::unique_ptr Singleton::Config::directories_=std::unique_ptr(new Directories::Config());std::unique_ptr Singleton::Config::terminal_=std::unique_ptr(new Terminal::Config()); std::unique_ptr Singleton::Config::theme_ = std::unique_ptr(new Theme::Config()); std::unique_ptr Singleton::Config::window_ = std::unique_ptr(new Window::Config()); - +std::unique_ptr Singleton::terminal_=std::unique_ptr(); Terminal *Singleton::terminal() { if(!terminal_) diff --git a/src/singletons.h b/src/singletons.h index dd1e6ca..f65a3d9 100644 --- a/src/singletons.h +++ b/src/singletons.h @@ -1,3 +1,4 @@ + #ifndef JUCI_SINGLETONS_H_ #define JUCI_SINGLETONS_H_ @@ -19,11 +20,14 @@ public: static Directories::Config *directories() {return directories_.get();} static Theme::Config *theme() { return theme_.get(); } static Window::Config *window() { return window_.get(); } + static Terminal::Config *terminal() {return terminal_.get();} + private: static std::unique_ptr source_; static std::unique_ptr theme_; static std::unique_ptr window_; static std::unique_ptr directories_; + static std::unique_ptr terminal_; }; static std::string config_dir() { return std::string(getenv("HOME")) + "/.juci/config/"; } static std::string theme_dir() { return std::string(getenv("HOME")) + "/.juci/gtk-themes/"; } diff --git a/src/source.cc b/src/source.cc index aa6cef7..a26e1d8 100644 --- a/src/source.cc +++ b/src/source.cc @@ -15,18 +15,17 @@ namespace sigc { SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE } -Glib::RefPtr Source::guess_language(const std::string &file_path) { +Glib::RefPtr Source::guess_language(const boost::filesystem::path &file_path) { auto language_manager=Gsv::LanguageManager::get_default(); bool result_uncertain = false; - auto content_type = Gio::content_type_guess(file_path, NULL, 0, result_uncertain); + auto content_type = Gio::content_type_guess(file_path.string(), NULL, 0, result_uncertain); if(result_uncertain) { content_type.clear(); } - auto language=language_manager->guess_language(file_path, content_type); + auto language=language_manager->guess_language(file_path.string(), content_type); if(!language) { - auto path=boost::filesystem::path(file_path); - auto filename=path.filename().string(); - auto extension=path.extension(); + auto filename=file_path.filename().string(); + auto extension=file_path.extension(); if(filename=="CMakeLists.txt") language=language_manager->get_language("cmake"); } @@ -36,7 +35,7 @@ Glib::RefPtr Source::guess_language(const std::string &file_path) ////////////// //// View //// ////////////// -Source::View::View(const std::string& file_path): file_path(file_path) { +Source::View::View(const boost::filesystem::path &file_path): file_path(file_path) { set_smart_home_end(Gsv::SMART_HOME_END_BEFORE); get_source_buffer()->begin_not_undoable_action(); juci::filesystem::read(file_path, get_buffer()); @@ -142,6 +141,92 @@ void Source::View::replace_all(const std::string &replacement) { gtk_source_search_context_replace_all(search_context, replacement.c_str(), replacement.size(), NULL); } +void Source::View::paste() { + Gtk::Clipboard::get()->request_text([this](const Glib::ustring& text){ + const std::regex spaces_regex(std::string("^(")+Singleton::Config::source()->tab_char+"*)(.*)$"); + auto line=get_line_before_insert(); + std::smatch sm; + std::string prefix_tabs; + if(std::regex_match(line, sm, spaces_regex) && sm[2].str().size()==0) { + prefix_tabs=sm[1].str(); + + Glib::ustring::size_type start_line=0; + Glib::ustring::size_type end_line=0; + bool paste_line=false; + bool first_paste_line=true; + size_t paste_line_tabs=-1; + bool first_paste_line_has_tabs=false; + for(Glib::ustring::size_type c=0;ctab_char) + tabs++; + else + break; + } + if(first_paste_line) { + if(tabs!=0) { + first_paste_line_has_tabs=true; + paste_line_tabs=tabs; + } + first_paste_line=false; + } + else + paste_line_tabs=std::min(paste_line_tabs, tabs); + + start_line=end_line+1; + paste_line=false; + } + } + if(paste_line_tabs==(size_t)-1) + paste_line_tabs=0; + start_line=0; + end_line=0; + paste_line=false; + first_paste_line=true; + get_source_buffer()->begin_user_action(); + for(Glib::ustring::size_type c=0;cinsert_at_cursor(text.substr(start_line+paste_line_tabs, end_line-start_line-paste_line_tabs)); + else + get_buffer()->insert_at_cursor(text.substr(start_line, end_line-start_line)); + first_paste_line=false; + } + else + get_buffer()->insert_at_cursor('\n'+prefix_tabs+text.substr(start_line+paste_line_tabs, end_line-start_line-paste_line_tabs)); + start_line=end_line+1; + paste_line=false; + } + } + get_buffer()->place_cursor(get_buffer()->get_insert()->get_iter()); + scroll_to(get_buffer()->get_insert()); + get_source_buffer()->end_user_action(); + } + else + get_buffer()->paste_clipboard(Gtk::Clipboard::get()); + }); +} + void Source::View::set_status(const std::string &status) { this->status=status; if(on_update_status) @@ -171,14 +256,17 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { const std::regex spaces_regex(std::string("^(")+config->tab_char+"*).*$"); //Indent as in next or previous line if(key->keyval==GDK_KEY_Return && key->state==0 && !get_buffer()->get_has_selection()) { - int line_nr=get_source_buffer()->get_insert()->get_iter().get_line(); - string line(get_line_before_insert()); + auto insert_it=get_buffer()->get_insert()->get_iter(); + int line_nr=insert_it.get_line(); + auto line=get_line_before_insert(); std::smatch sm; if(std::regex_match(line, sm, spaces_regex)) { - if((line_nr+1)get_line_count()) { + if((line_nr+1)get_line_count()) { string next_line=get_line(line_nr+1); + auto line_end_iter=get_buffer()->get_iter_at_line(line_nr+1); + line_end_iter--; std::smatch sm2; - if(std::regex_match(next_line, sm2, spaces_regex)) { + if(insert_it==line_end_iter && std::regex_match(next_line, sm2, spaces_regex)) { if(sm2[1].str().size()>sm[1].str().size()) { get_source_buffer()->insert_at_cursor("\n"+sm2[1].str()); scroll_to(get_source_buffer()->get_insert()); @@ -213,9 +301,14 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { int line_start=selection_start.get_line(); int line_end=selection_end.get_line(); + unsigned indent_left_steps=config->tab_size; for(int line_nr=line_start;line_nr<=line_end;line_nr++) { string line=get_line(line_nr); - if(!(line.size()>=config->tab_size && line.substr(0, config->tab_size)==config->tab)) { + std::smatch sm; + if(std::regex_match(line, sm, spaces_regex) && sm[1].str().size()>0) { + indent_left_steps=std::min(indent_left_steps, (unsigned)sm[1].str().size()); + } + else { get_source_buffer()->end_user_action(); return true; } @@ -225,7 +318,7 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(line_nr); Gtk::TextIter line_plus_it=line_it; - for(unsigned c=0;ctab_size;c++) + for(unsigned c=0;cerase(line_it, line_plus_it); } @@ -234,20 +327,37 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { } //"Smart" backspace key else if(key->keyval==GDK_KEY_BackSpace && !get_buffer()->get_has_selection()) { - Gtk::TextIter insert_it=get_source_buffer()->get_insert()->get_iter(); + auto insert_it=get_buffer()->get_insert()->get_iter(); int line_nr=insert_it.get_line(); - if(line_nr>0) { - string line=get_line(line_nr); - string previous_line=get_line(line_nr-1); - smatch sm; - if(std::regex_match(previous_line, sm, spaces_regex)) { - if(line==sm[1] || line==(std::string(sm[1])+config->tab) || (line+config->tab==sm[1])) { - Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(line_nr); - get_source_buffer()->erase(line_it, insert_it); + auto line=get_line_before_insert(); + std::smatch sm; + if(std::regex_match(line, sm, spaces_regex) && sm[1].str().size()==line.size()) { + if((line_nr-1)>=0) { + string previous_line=get_line(line_nr-1); + std::smatch sm2; + if(std::regex_match(previous_line, sm2, spaces_regex)) { + if(line.size()==sm2[1].str().size() || line.size()==sm2[1].str().size()+config->tab_size || line.size()==sm2[1].str().size()-config->tab_size) { + auto previous_line_end_it=insert_it; + for(unsigned c=0;cerase(previous_line_end_it, insert_it); + get_source_buffer()->end_user_action(); + return true; + } } } + if(line.size()>=config->tab_size) { + auto insert_minus_tab_it=insert_it; + for(unsigned c=0;ctab_size;c++) + insert_minus_tab_it--; + get_source_buffer()->erase(insert_minus_tab_it, insert_it); + get_source_buffer()->end_user_action(); + return true; + } } } + bool stop=Gsv::View::on_key_press_event(key); get_source_buffer()->end_user_action(); return stop; @@ -256,10 +366,10 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { ///////////////////// //// GenericView //// ///////////////////// -Source::GenericView::GenericView(const std::string& file_path, Glib::RefPtr language) : View(file_path) { +Source::GenericView::GenericView(const boost::filesystem::path &file_path, Glib::RefPtr language) : View(file_path) { if(language) { get_source_buffer()->set_language(language); - Singleton::terminal()->print("Language for file "+file_path+" set to "+language->get_name()+".\n"); + Singleton::terminal()->print("Language for file "+file_path.string()+" set to "+language->get_name()+".\n"); } auto completion=get_completion(); auto completion_words=Gsv::CompletionWords::create("", Glib::RefPtr()); @@ -275,9 +385,8 @@ Source::GenericView::GenericView(const std::string& file_path, Glib::RefPtrtags) { auto scheme = get_source_buffer()->get_style_scheme(); auto style = scheme->get_style(item.second); @@ -322,7 +431,7 @@ Source::ClangViewParse::ClangViewParse(const std::string& file_path, const std:: } //TODO: clear tag_class and param_spec? - parsing_in_progress=Singleton::terminal()->print_in_progress("Parsing "+file_path); + parsing_in_progress=Singleton::terminal()->print_in_progress("Parsing "+file_path.string()); //GTK-calls must happen in main thread, so the parse_thread //sends signals to the main thread that it is to call the following functions: parse_start.connect([this]{ @@ -376,7 +485,7 @@ void Source::ClangViewParse::init_parse() { int end_offset = get_source_buffer()->end().get_offset(); auto buffer_map=get_buffer_map(); //Remove includes for first parse for initial syntax highlighting - auto& str=buffer_map[file_path]; + auto& str=buffer_map[file_path.string()]; std::size_t pos=0; while((pos=str.find("#include", pos))!=std::string::npos) { auto start_pos=pos; @@ -424,15 +533,15 @@ init_syntax_highlighting(const std::map int end_offset) { std::vector arguments = get_compilation_commands(); clang_tu = std::unique_ptr(new clang::TranslationUnit(clang_index, - file_path, + file_path.string(), arguments, buffers)); - clang_tokens=clang_tu->get_tokens(0, buffers.find(file_path)->second.size()-1); + clang_tokens=clang_tu->get_tokens(0, buffers.find(file_path.string())->second.size()-1); } std::map Source::ClangViewParse::get_buffer_map() const { std::map buffer_map; - buffer_map[file_path]=get_source_buffer()->get_text().raw(); + buffer_map[file_path.string()]=get_source_buffer()->get_text().raw(); return buffer_map; } @@ -450,13 +559,13 @@ void Source::ClangViewParse::start_reparse() { int Source::ClangViewParse::reparse(const std::map &buffer) { int status = clang_tu->ReparseTranslationUnit(buffer); - clang_tokens=clang_tu->get_tokens(0, parse_thread_buffer_map.find(file_path)->second.size()-1); + clang_tokens=clang_tu->get_tokens(0, parse_thread_buffer_map.find(file_path.string())->second.size()-1); return status; } std::vector Source::ClangViewParse::get_compilation_commands() { - clang::CompilationDatabase db(project_path); - clang::CompileCommands commands(file_path, db); + clang::CompilationDatabase db(project_path.string()); + clang::CompileCommands commands(file_path.string(), db); std::vector cmds = commands.get_commands(); std::vector arguments; for (auto &i : cmds) { @@ -465,7 +574,7 @@ std::vector Source::ClangViewParse::get_compilation_commands() { arguments.emplace_back(lol[a]); } } - if(boost::filesystem::path(file_path).extension()==".h") //TODO: temporary fix for .h-files (parse as c++) + if(file_path.extension()==".h") //TODO: temporary fix for .h-files (parse as c++) arguments.emplace_back("-xc++"); return arguments; } @@ -513,7 +622,7 @@ void Source::ClangViewParse::update_diagnostics() { get_buffer()->remove_tag_by_name("diagnostic_error_underline", get_buffer()->begin(), get_buffer()->end()); auto diagnostics=clang_tu->get_diagnostics(); for(auto &diagnostic: diagnostics) { - if(diagnostic.path==file_path) { + if(diagnostic.path==file_path.string()) { auto start=get_buffer()->get_iter_at_offset(diagnostic.offsets.first); auto end=get_buffer()->get_iter_at_offset(diagnostic.offsets.second); std::string diagnostic_tag_name; @@ -644,16 +753,23 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) { return true; } } + if(next_line!=sm[1].str()+"}") { + get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+config->tab+"\n"+sm[1].str()+"}"); + auto insert_it = get_source_buffer()->get_insert()->get_iter(); + for(size_t c=0;ctab_size+sm[1].str().size();c++) + insert_it--; + scroll_to(get_source_buffer()->get_insert()); + get_source_buffer()->place_cursor(insert_it); + get_source_buffer()->end_user_action(); + return true; + } + else { + get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+config->tab); + scroll_to(get_source_buffer()->get_insert()); + get_source_buffer()->end_user_action(); + return true; + } } - //TODO: insert without moving mark backwards. - get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+config->tab+"\n"+sm[1].str()+"}"); - auto insert_it = get_source_buffer()->get_insert()->get_iter(); - for(size_t c=0;ctab_size+sm[1].str().size();c++) - insert_it--; - scroll_to(get_source_buffer()->get_insert()); - get_source_buffer()->place_cursor(insert_it); - get_source_buffer()->end_user_action(); - return true; } else if(std::regex_match(line, sm, no_bracket_statement_regex)) { get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+config->tab); @@ -718,7 +834,7 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) { ////////////////////////////// //// ClangViewAutocomplete /// ////////////////////////////// -Source::ClangViewAutocomplete::ClangViewAutocomplete(const std::string& file_path, const std::string& project_path): +Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path): Source::ClangViewParse(file_path, project_path), autocomplete_cancel_starting(false) { get_buffer()->signal_changed().connect([this](){ if(completion_dialog_shown) @@ -879,7 +995,7 @@ void Source::ClangViewAutocomplete::autocomplete() { }); std::shared_ptr > buffer_map=std::make_shared >(); - auto& buffer=(*buffer_map)[this->file_path]; + auto& buffer=(*buffer_map)[this->file_path.string()]; buffer=get_buffer()->get_text(get_buffer()->begin(), get_buffer()->get_insert()->get_iter()); auto iter = get_source_buffer()->get_insert()->get_iter(); auto line_nr=iter.get_line()+1; @@ -938,7 +1054,7 @@ get_autocomplete_suggestions(int line_number, int column, std::mapcreate_tag(); similar_tokens_tag->property_weight()=Pango::WEIGHT_BOLD; @@ -1070,7 +1186,7 @@ Source::ClangViewAutocomplete(file_path, project_path) { }; } -Source::ClangView::ClangView(const std::string& file_path, const std::string& project_path): ClangViewRefactor(file_path, project_path) { +Source::ClangView::ClangView(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path): ClangViewRefactor(file_path, project_path) { do_delete_object.connect([this](){ if(delete_thread.joinable()) delete_thread.join(); diff --git a/src/source.h b/src/source.h index b24fb2b..a888fae 100644 --- a/src/source.h +++ b/src/source.h @@ -16,7 +16,7 @@ #include namespace Source { - Glib::RefPtr guess_language(const std::string &file_path); + Glib::RefPtr guess_language(const boost::filesystem::path &file_path); class Config { public: @@ -45,7 +45,7 @@ namespace Source { class View : public Gsv::View { public: - View(const std::string& file_path); + View(const boost::filesystem::path &file_path); ~View(); void search_highlight(const std::string &text, bool case_sensitive, bool regex); @@ -55,8 +55,10 @@ namespace Source { void replace_forward(const std::string &replacement); void replace_backward(const std::string &replacement); void replace_all(const std::string &replacement); + + void paste(); - std::string file_path; + boost::filesystem::path file_path; std::function()> get_declaration_location; std::function goto_method; @@ -82,13 +84,13 @@ namespace Source { class GenericView : public View { public: - GenericView(const std::string& file_path, Glib::RefPtr language); + GenericView(const boost::filesystem::path &file_path, Glib::RefPtr language); }; class ClangViewParse : public View { public: - ClangViewParse(const std::string& file_path, const std::string& project_path); - std::string project_path; + ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path); + boost::filesystem::path project_path; protected: void init_parse(); void start_reparse(); @@ -135,7 +137,7 @@ namespace Source { class ClangViewAutocomplete : public ClangViewParse { public: - ClangViewAutocomplete(const std::string& file_path, const std::string& project_path); + ClangViewAutocomplete(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path); protected: bool on_key_press_event(GdkEventKey* key); bool on_focus_out_event(GdkEventFocus* event); @@ -157,7 +159,7 @@ namespace Source { class ClangViewRefactor : public ClangViewAutocomplete { public: - ClangViewRefactor(const std::string& file_path, const std::string& project_path); + ClangViewRefactor(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path); private: Glib::RefPtr similar_tokens_tag; std::string last_similar_tokens_tagged; @@ -167,7 +169,7 @@ namespace Source { class ClangView : public ClangViewRefactor { public: - ClangView(const std::string& file_path, const std::string& project_path); + ClangView(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path); void async_delete(); bool restart_parse(); private: diff --git a/src/sourcefile.cc b/src/sourcefile.cc index 7fedbd1..b6f0786 100644 --- a/src/sourcefile.cc +++ b/src/sourcefile.cc @@ -65,7 +65,7 @@ bool juci::filesystem::write(const std::string &path, Glib::RefPtrget_text(start_iter, end_iter); + output << buffer->get_text(start_iter, end_iter).c_str(); start_iter=end_iter; } output.close(); diff --git a/src/sourcefile.h b/src/sourcefile.h index aabe617..d1ee608 100644 --- a/src/sourcefile.h +++ b/src/sourcefile.h @@ -11,6 +11,7 @@ namespace juci { static std::string read(const std::string &path); static std::string read(const boost::filesystem::path &path) { return read(path.string()); } static bool read(const std::string &path, Glib::RefPtr text_buffer); + static bool read(const boost::filesystem::path &path, Glib::RefPtr text_buffer) { return read(path.string(), text_buffer); } static std::vector read_lines(const std::string &path); static std::vector read_lines(const boost::filesystem::path &path) { return read_lines(path.string()); }; @@ -20,6 +21,7 @@ namespace juci { static bool write(const std::string &path) { return write(path, ""); }; static bool write(const boost::filesystem::path &path) { return write(path, ""); }; static bool write(const std::string &path, Glib::RefPtr text_buffer); + static bool write(const boost::filesystem::path &path, Glib::RefPtr text_buffer) { return write(path.string(), text_buffer); } }; } // namepace juci #endif // JUCI_SOURCEFILE_H_ diff --git a/src/terminal.cc b/src/terminal.cc index 50d6990..bb0663c 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -45,6 +45,8 @@ pid_t popen3(const char *command, int &stdin, int &stdout, int &stderr) { dup2(p_stderr[1], 2); setpgid(0, 0); + //TODO: See here on how to emulate tty for colors: http://stackoverflow.com/questions/1401002/trick-an-application-into-thinking-its-stdin-is-interactive-not-a-pipe + //TODO: One solution is: echo "command;exit"|script -q /dev/null execl("/bin/sh", "sh", "-c", command, NULL); perror("execl"); exit(EXIT_FAILURE); @@ -63,7 +65,7 @@ pid_t popen3(const char *command, int &stdin, int &stdout, int &stderr) { Terminal::InProgress::InProgress(const std::string& start_msg): stop(false) { waiting_print.connect([this](){ - Singleton::terminal()->print(line_nr-1, "."); + Singleton::terminal()->async_print(line_nr-1, "."); }); start(start_msg); } @@ -90,30 +92,30 @@ void Terminal::InProgress::start(const std::string& msg) { void Terminal::InProgress::done(const std::string& msg) { if(!stop) { stop=true; - Singleton::terminal()->print(line_nr-1, msg); + Singleton::terminal()->async_print(line_nr-1, msg); } } void Terminal::InProgress::cancel(const std::string& msg) { if(!stop) { stop=true; - Singleton::terminal()->print(line_nr-1, msg); + Singleton::terminal()->async_print(line_nr-1, msg); } } Terminal::Terminal() { - text_view.set_editable(false); - scrolled_window.add(text_view); - add(scrolled_window); + bold_tag=get_buffer()->create_tag(); + bold_tag->property_weight()=PANGO_WEIGHT_BOLD; - text_view.signal_size_allocate().connect([this](Gtk::Allocation& allocation){ - auto end=text_view.get_buffer()->create_mark(text_view.get_buffer()->end()); - text_view.scroll_to(end); - text_view.get_buffer()->delete_mark(end); + signal_size_allocate().connect([this](Gtk::Allocation& allocation){ + auto iter=get_buffer()->end(); + if(iter.backward_char()) { + auto mark=get_buffer()->create_mark(iter); + scroll_to(mark, 0.0, 1.0, 1.0); + get_buffer()->delete_mark(mark); + } }); - bold_tag=text_view.get_buffer()->create_tag(); - bold_tag->property_weight()=PANGO_WEIGHT_BOLD; async_print_dispatcher.connect([this](){ async_print_strings_mutex.lock(); if(async_print_strings.size()>0) { @@ -123,16 +125,22 @@ Terminal::Terminal() { } async_print_strings_mutex.unlock(); }); + async_print_on_line_dispatcher.connect([this](){ + async_print_on_line_strings_mutex.lock(); + if(async_print_on_line_strings.size()>0) { + for(auto &line_string: async_print_on_line_strings) + print(line_string.first, line_string.second); + async_print_on_line_strings.clear(); + } + async_print_on_line_strings_mutex.unlock(); + }); } -int Terminal::execute(const std::string &command, const std::string &path) { - boost::filesystem::path boost_path; +int Terminal::execute(const std::string &command, const boost::filesystem::path &path) { std::string cd_path_and_command; if(path!="") { - boost_path=boost::filesystem::path(path); - //TODO: Windows... - cd_path_and_command="cd "+boost_path.string()+" && "+command; + cd_path_and_command="cd "+path.string()+" && "+command; } else cd_path_and_command=command; @@ -179,24 +187,23 @@ int Terminal::execute(const std::string &command, const std::string &path) { } } -void Terminal::async_execute(const std::string &command, const std::string &path, std::function callback) { +void Terminal::async_execute(const std::string &command, const boost::filesystem::path &path, std::function callback) { std::thread async_execute_thread([this, command, path, callback](){ - boost::filesystem::path boost_path; std::string cd_path_and_command; if(path!="") { - boost_path=boost::filesystem::path(path); //TODO: Windows... - cd_path_and_command="cd "+boost_path.string()+" && "+command; + cd_path_and_command="cd "+path.string()+" && "+command; } else cd_path_and_command=command; int stdin, stdout, stderr; - async_execute_pids_mutex.lock(); + async_executes_mutex.lock(); + stdin_buffer.clear(); auto pid=popen3(cd_path_and_command.c_str(), stdin, stdout, stderr); - async_execute_pids.emplace(pid); - async_execute_pids_mutex.unlock(); + async_executes.emplace_back(pid, stdin); + async_executes_mutex.unlock(); if (pid<=0) { async_print("Error: Failed to run command: " + command + "\n"); @@ -230,12 +237,18 @@ void Terminal::async_execute(const std::string &command, const std::string &path int exit_code; waitpid(pid, &exit_code, 0); - async_execute_pids_mutex.lock(); - async_execute_pids.erase(pid); - async_execute_pids_mutex.unlock(); + async_executes_mutex.lock(); + for(auto it=async_executes.begin();it!=async_executes.end();it++) { + if(it->first==pid) { + async_executes.erase(it); + break; + } + } + stdin_buffer.clear(); close(stdin); close(stdout); close(stderr); + async_executes_mutex.unlock(); if(callback) callback(exit_code); @@ -244,32 +257,51 @@ void Terminal::async_execute(const std::string &command, const std::string &path async_execute_thread.detach(); } -void Terminal::kill_executing() { - async_execute_pids_mutex.lock(); - for(auto &pid: async_execute_pids) { - kill(-pid, SIGINT); +void Terminal::kill_last_async_execute(bool force) { + async_executes_mutex.lock(); + if(async_executes.size()>0) { + if(force) + kill(-async_executes.back().first, SIGTERM); + else + kill(-async_executes.back().first, SIGINT); } - async_execute_pids_mutex.unlock(); + async_executes_mutex.unlock(); +} + +void Terminal::kill_async_executes(bool force) { + async_executes_mutex.lock(); + for(auto &async_execute: async_executes) { + if(force) + kill(-async_execute.first, SIGTERM); + else + kill(-async_execute.first, SIGINT); + } + async_executes_mutex.unlock(); } int Terminal::print(const std::string &message, bool bold){ INFO("Terminal: PrintMessage"); if(bold) - text_view.get_buffer()->insert_with_tag(text_view.get_buffer()->end(), message, bold_tag); + get_buffer()->insert_with_tag(get_buffer()->end(), message, bold_tag); else - text_view.get_buffer()->insert(text_view.get_buffer()->end(), message); - return text_view.get_buffer()->end().get_line(); + get_buffer()->insert(get_buffer()->end(), message); + + auto iter=get_buffer()->end(); + if(iter.backward_char()) { + auto mark=get_buffer()->create_mark(iter); + scroll_to(mark, 0.0, 1.0, 1.0); + get_buffer()->delete_mark(mark); + } + + return get_buffer()->end().get_line(); } -void Terminal::print(int line_nr, const std::string &message, bool bold){ +void Terminal::print(int line_nr, const std::string &message){ INFO("Terminal: PrintMessage at line " << line_nr); - auto iter=text_view.get_buffer()->get_iter_at_line(line_nr); + auto iter=get_buffer()->get_iter_at_line(line_nr); while(!iter.ends_line()) iter++; - if(bold) - text_view.get_buffer()->insert_with_tag(iter, message, bold_tag); - else - text_view.get_buffer()->insert(iter, message); + get_buffer()->insert(iter, message); } std::shared_ptr Terminal::print_in_progress(std::string start_msg) { @@ -287,3 +319,46 @@ void Terminal::async_print(const std::string &message, bool bold) { if(dispatch) async_print_dispatcher(); } + +void Terminal::async_print(int line_nr, const std::string &message) { + async_print_on_line_strings_mutex.lock(); + bool dispatch=true; + if(async_print_on_line_strings.size()>0) + dispatch=false; + async_print_on_line_strings.emplace_back(line_nr, message); + async_print_on_line_strings_mutex.unlock(); + if(dispatch) + async_print_on_line_dispatcher(); +} + +bool Terminal::on_key_press_event(GdkEventKey *event) { + async_executes_mutex.lock(); + if(async_executes.size()>0) { + get_buffer()->place_cursor(get_buffer()->end()); + auto unicode=gdk_keyval_to_unicode(event->keyval); + char chr=(char)unicode; + if(unicode>=32 && unicode<=126) { + stdin_buffer+=chr; + get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size()-1)); + scroll_to(get_buffer()->get_insert()); + } + else if(event->keyval==GDK_KEY_BackSpace) { + if(stdin_buffer.size()>0 && get_buffer()->get_char_count()>0) { + auto iter=get_buffer()->end(); + iter--; + stdin_buffer.pop_back(); + get_buffer()->erase(iter, get_buffer()->end()); + scroll_to(get_buffer()->get_insert()); + } + } + else if(event->keyval==GDK_KEY_Return) { + stdin_buffer+='\n'; + write(async_executes.back().second, stdin_buffer.c_str(), stdin_buffer.size()); + get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size()-1)); + scroll_to(get_buffer()->get_insert()); + stdin_buffer.clear(); + } + } + async_executes_mutex.unlock(); + return true; +} diff --git a/src/terminal.h b/src/terminal.h index 4559473..5bdbe1a 100644 --- a/src/terminal.h +++ b/src/terminal.h @@ -7,10 +7,15 @@ #include #include #include -#include +#include -class Terminal : public Gtk::HBox { +class Terminal : public Gtk::TextView { public: + class Config { + public: + std::string make_command; + }; + class InProgress { public: InProgress(const std::string& start_msg); @@ -26,25 +31,31 @@ public: }; Terminal(); - int execute(const std::string &command, const std::string &path=""); - void async_execute(const std::string &command, const std::string &path="", std::function callback=nullptr); - void kill_executing(); + int execute(const std::string &command, const boost::filesystem::path &path=""); + void async_execute(const std::string &command, const boost::filesystem::path &path="", std::function callback=nullptr); + void kill_last_async_execute(bool force=false); + void kill_async_executes(bool force=false); int print(const std::string &message, bool bold=false); - void print(int line_nr, const std::string &message, bool bold=false); + void print(int line_nr, const std::string &message); std::shared_ptr print_in_progress(std::string start_msg); void async_print(const std::string &message, bool bold=false); + void async_print(int line_nr, const std::string &message); +protected: + bool on_key_press_event(GdkEventKey *event); private: - Gtk::TextView text_view; - Gtk::ScrolledWindow scrolled_window; - Glib::Dispatcher async_print_dispatcher; + Glib::Dispatcher async_print_on_line_dispatcher; std::vector > async_print_strings; + std::vector > async_print_on_line_strings; std::mutex async_print_strings_mutex; + std::mutex async_print_on_line_strings_mutex; Glib::RefPtr bold_tag; - std::mutex async_execute_pids_mutex; - std::unordered_set async_execute_pids; + std::mutex async_executes_mutex; + std::list > async_executes; + + std::string stdin_buffer; }; #endif // JUCI_TERMINAL_H_ diff --git a/src/window.cc b/src/window.cc index 8044d8d..954eacd 100644 --- a/src/window.cc +++ b/src/window.cc @@ -41,7 +41,8 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil vpaned.set_position(300); vpaned.pack1(directory_and_notebook_panes, true, false); - terminal_vbox.pack_start(*Singleton::terminal()); + terminal_scrolled_window.add(*Singleton::terminal()); + terminal_vbox.pack_start(terminal_scrolled_window); status_hbox.pack_end(*Singleton::status(), Gtk::PACK_SHRINK); terminal_vbox.pack_end(status_hbox, Gtk::PACK_SHRINK); vpaned.pack2(terminal_vbox, true, true); @@ -100,7 +101,7 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil compile_success.connect([this](){ directories.open_folder(); - }); + }); INFO("Window created"); } // Window constructor @@ -113,6 +114,10 @@ void Window::create_menu() { menu.action_group->add(Gtk::Action::create("FileNewFile", "New file"), Gtk::AccelKey(menu.key_map["new_file"]), [this]() { new_file_entry(); }); + menu.action_group->add(Gtk::Action::create("FileNewProject", "New Project")); + menu.action_group->add(Gtk::Action::create("FileNewProjectCpp", "C++"), [this]() { + new_cpp_project_dialog(); + }); menu.action_group->add(Gtk::Action::create("FileOpenFile", "Open file"), Gtk::AccelKey(menu.key_map["open_file"]), [this]() { open_file_dialog(); }); @@ -146,7 +151,7 @@ void Window::create_menu() { if(auto entry=dynamic_cast(widget)) entry->paste_clipboard(); else if(notebook.get_current_page()!=-1) - notebook.get_current_view()->get_buffer()->paste_clipboard(Gtk::Clipboard::get()); + notebook.get_current_view()->paste(); }); menu.action_group->add(Gtk::Action::create("EditFind", "Find"), Gtk::AccelKey(menu.key_map["edit_find"]), [this]() { search_and_replace_entry(); @@ -213,31 +218,33 @@ void Window::create_menu() { CMake cmake(notebook.get_current_view()->file_path); directories.open_folder(); auto executables = cmake.get_functions_parameters("add_executable"); - std::string executable; - boost::filesystem::path path; + boost::filesystem::path executable_path; if(executables.size()>0 && executables[0].second.size()>0) { - executable=executables[0].second[0]; - path=executables[0].first.parent_path(); - path+="/"+executables[0].second[0]; + executable_path=executables[0].first.parent_path(); + executable_path+="/"+executables[0].second[0]; } if(cmake.project_path!="") { - if(path!="") { + if(executable_path!="") { compiling=true; - Singleton::terminal()->print("Compiling and executing "+path.string()+"\n"); + Singleton::terminal()->print("Compiling and running "+executable_path.string()+"\n"); //TODO: Windows... - Singleton::terminal()->async_execute("make", cmake.project_path.string(), [this, path](int exit_code){ + auto project_path=cmake.project_path; + Singleton::terminal()->async_execute(Singleton::Config::terminal()->make_command, cmake.project_path, [this, executable_path, project_path](int exit_code){ compiling=false; if(exit_code==EXIT_SUCCESS) { compile_success(); //TODO: Windows... - Singleton::terminal()->async_execute(path.string(), path.parent_path().string(), [this, path](int exit_code){ - Singleton::terminal()->async_print(path.string()+" returned: "+std::to_string(exit_code)+'\n'); + Singleton::terminal()->async_execute(executable_path.string(), project_path, [this, executable_path](int exit_code){ + Singleton::terminal()->async_print(executable_path.string()+" returned: "+std::to_string(exit_code)+'\n'); }); } }); } - else - Singleton::terminal()->print("Could not find an executable, please use add_executable in CMakeLists.txt\n"); + else { + Singleton::terminal()->print("Could not find add_executable in the following paths:\n"); + for(auto &path: cmake.paths) + Singleton::terminal()->print(" "+path.string()+"\n"); + } } }); menu.action_group->add(Gtk::Action::create("ProjectCompile", "Compile"), Gtk::AccelKey(menu.key_map["compile"]), [this]() { @@ -249,13 +256,37 @@ void Window::create_menu() { compiling=true; Singleton::terminal()->print("Compiling project "+cmake.project_path.string()+"\n"); //TODO: Windows... - Singleton::terminal()->async_execute("make 2>&1", cmake.project_path.string(), [this](int exit_code){ + Singleton::terminal()->async_execute(Singleton::Config::terminal()->make_command, cmake.project_path, [this](int exit_code){ compiling=false; if(exit_code==EXIT_SUCCESS) compile_success(); }); } }); + menu.action_group->add(Gtk::Action::create("ProjectRunCommand", "Run Command"), Gtk::AccelKey(menu.key_map["run_command"]), [this]() { + entry_box.clear(); + entry_box.entries.emplace_back(last_run_command, [this](const std::string& content){ + if(content!="") { + last_run_command=content; + Singleton::terminal()->async_print("Running: "+content+'\n'); + Singleton::terminal()->async_execute(content, directories.current_path, [this, content](int exit_code){ + Singleton::terminal()->async_print(content+" returned: "+std::to_string(exit_code)+'\n'); + }); + } + entry_box.hide(); + }); + auto entry_it=entry_box.entries.begin(); + entry_box.buttons.emplace_back("Run command", [this, entry_it](){ + entry_it->activate(); + }); + entry_box.show(); + }); + menu.action_group->add(Gtk::Action::create("ProjectKillLastRunning", "Kill Last Process"), Gtk::AccelKey(menu.key_map["kill_last_running"]), [this]() { + Singleton::terminal()->kill_last_async_execute(); + }); + menu.action_group->add(Gtk::Action::create("ProjectForceKillLastRunning", "Force Kill Last Process"), Gtk::AccelKey(menu.key_map["force_kill_last_running"]), [this]() { + Singleton::terminal()->kill_last_async_execute(true); + }); menu.action_group->add(Gtk::Action::create("WindowCloseTab", "Close tab"), Gtk::AccelKey(menu.key_map["close_tab"]), [this]() { notebook.close_current_page(); @@ -267,8 +298,6 @@ void Window::create_menu() { bool Window::on_key_press_event(GdkEventKey *event) { if(event->keyval==GDK_KEY_Escape) { - if(entry_box.entries.size()==0) - Singleton::terminal()->kill_executing(); entry_box.hide(); } #ifdef __APPLE__ //For Apple's Command-left, right, up, down keys @@ -312,7 +341,7 @@ void Window::hide() { if(!notebook.close_current_page()) return; } - Singleton::terminal()->kill_executing(); + Singleton::terminal()->kill_async_executes(); Gtk::Window::hide(); } @@ -347,6 +376,46 @@ void Window::new_file_entry() { entry_box.show(); } +void Window::new_cpp_project_dialog() { + Gtk::FileChooserDialog dialog("Please create and/or choose a folder", Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER); + if(directories.current_path!="") + gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), directories.current_path.string().c_str()); + else + gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str()); + dialog.set_transient_for(*this); + + dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL); + dialog.add_button("Select", Gtk::RESPONSE_OK); + + int result = dialog.run(); + + if(result==Gtk::RESPONSE_OK) { + boost::filesystem::path project_path=dialog.get_filename(); + auto project_name=project_path.filename().string(); + auto cmakelists_path=project_path; + cmakelists_path+="/CMakeLists.txt"; + auto cpp_main_path=project_path; + cpp_main_path+="/main.cpp"; + if(boost::filesystem::exists(cmakelists_path)) { + Singleton::terminal()->print("Error: "+cmakelists_path.string()+" already exists.\n"); + return; + } + if(boost::filesystem::exists(cpp_main_path)) { + Singleton::terminal()->print("Error: "+cpp_main_path.string()+" already exists.\n"); + return; + } + std::string cmakelists="cmake_minimum_required(VERSION 2.8)\n\nproject("+project_name+")\n\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -std=c++1y\")\n\nadd_executable("+project_name+" main.cpp)\n"; + std::string cpp_main="#include \n\nusing namespace std;\n\nint main() {\n cout << \"Hello World!\" << endl;\n\n return 0;\n}\n"; + if(juci::filesystem::write(cmakelists_path, cmakelists) && juci::filesystem::write(cpp_main_path, cpp_main)) { + directories.open_folder(project_path); + notebook.open(cpp_main_path); + Singleton::terminal()->print("C++ project "+project_name+" created.\n"); + } + else + Singleton::terminal()->print("Error: Could not create project "+project_path.string()+"\n"); + } +} + void Window::open_folder_dialog() { Gtk::FileChooserDialog dialog("Please choose a folder", Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER); if(directories.current_path!="") @@ -426,7 +495,7 @@ void Window::save_file_dialog() { if(directories.current_path!="") directories.open_folder(); notebook.open(path); - Singleton::terminal()->print("File saved to: " + notebook.get_current_view()->file_path+"\n"); + Singleton::terminal()->print("File saved to: " + notebook.get_current_view()->file_path.string()+"\n"); } else Singleton::terminal()->print("Error saving file\n"); @@ -573,7 +642,7 @@ void Window::rename_token_entry() { if(notebook.get_view(c)->rename_similar_tokens) { auto number=notebook.get_view(c)->rename_similar_tokens(*token, content); if(number>0) { - Singleton::terminal()->print("Replaced "+std::to_string(number)+" occurrences in file "+notebook.get_view(c)->file_path+"\n"); + Singleton::terminal()->print("Replaced "+std::to_string(number)+" occurrences in file "+notebook.get_view(c)->file_path.string()+"\n"); notebook.save(c); } } diff --git a/src/window.h b/src/window.h index ef392a4..f2b57f1 100644 --- a/src/window.h +++ b/src/window.h @@ -29,6 +29,7 @@ private: Gtk::Paned directory_and_notebook_panes; Gtk::VBox notebook_vbox; Gtk::VBox terminal_vbox; + Gtk::ScrolledWindow terminal_scrolled_window; Gtk::HBox status_hbox; EntryBox entry_box; Menu menu; @@ -38,6 +39,7 @@ private: void create_menu(); void hide(); void new_file_entry(); + void new_cpp_project_dialog(); void open_folder_dialog(); void open_file_dialog(); void save_file_dialog(); @@ -47,6 +49,7 @@ private: void generate_keybindings(); std::string last_search; std::string last_replace; + std::string last_run_command; bool case_sensitive_search=true; bool regex_search=false; bool search_entry_shown=false;