diff --git a/src/entrybox.cc b/src/entrybox.cc index d221393..f207e56 100644 --- a/src/entrybox.cc +++ b/src/entrybox.cc @@ -37,6 +37,7 @@ EntryBox::Label::Label(std::functionset_focus_chain({&lower_box}); } void EntryBox::clear() { @@ -50,16 +51,16 @@ void EntryBox::clear() { void EntryBox::show() { std::vector focus_chain; for(auto& entry: entries) { - upper_box.pack_start(entry, Gtk::PACK_SHRINK); + lower_box.pack_start(entry, Gtk::PACK_SHRINK); focus_chain.emplace_back(&entry); } for(auto& button: buttons) - upper_box.pack_start(button, Gtk::PACK_SHRINK); + lower_box.pack_start(button, Gtk::PACK_SHRINK); for(auto& toggle_button: toggle_buttons) - upper_box.pack_start(toggle_button, Gtk::PACK_SHRINK); + lower_box.pack_start(toggle_button, Gtk::PACK_SHRINK); for(auto& label: labels) - lower_box.pack_start(label, Gtk::PACK_SHRINK); - upper_box.set_focus_chain(focus_chain); + upper_box.pack_start(label, Gtk::PACK_SHRINK); + lower_box.set_focus_chain(focus_chain); show_all(); if(entries.size()>0) { entries.begin()->grab_focus(); diff --git a/src/files.h b/src/files.h index 0aa5e88..6a34e45 100644 --- a/src/files.h +++ b/src/files.h @@ -47,6 +47,8 @@ const std::string configjson = " \"edit_undo\": \"z\",\n" " \"edit_redo\": \"z\",\n" " \"edit_find\": \"f\",\n" +" \"source_goto_line\": \"g\",\n" +" \"source_center_cursor\": \"l\",\n" " \"source_goto_declaration\": \"d\",\n" " \"source_goto_method\": \"m\",\n" " \"source_rename\": \"r\",\n" @@ -112,6 +114,9 @@ const std::string menuxml = " \n" " \n" " \n" +" \n" +" \n" +" \n" " \n" " \n" " \n" diff --git a/src/menu.cc b/src/menu.cc index ca59401..4998bee 100644 --- a/src/menu.cc +++ b/src/menu.cc @@ -33,4 +33,3 @@ void Menu::build() { } ui_manager->insert_action_group(action_group); } - diff --git a/src/notebook.cc b/src/notebook.cc index 24ad15e..4e18c1f 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -3,12 +3,16 @@ #include "sourcefile.h" #include "singletons.h" #include +#include + +#include //TODO: remove +using namespace std; //TODO: remove namespace sigc { SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE } -Notebook::Notebook() : Gtk::Notebook() { +Notebook::Notebook(Directories &directories) : Gtk::Notebook(), directories(directories) { Gsv::init(); } @@ -19,7 +23,7 @@ int Notebook::size() { Source::View* Notebook::get_view(int page) { if(page>=size()) return nullptr; - return source_views.at(page).get(); + return source_views.at(page); } Source::View* Notebook::get_current_view() { @@ -47,15 +51,29 @@ void Notebook::open(std::string path) { } can_read.close(); - auto tmp_project_path=project_path; - if(tmp_project_path=="") { - tmp_project_path=boost::filesystem::path(path).parent_path().string(); - } auto language=Source::guess_language(path); - if(language && (language->get_id()=="chdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc")) - source_views.emplace_back(new Source::ClangView(path, tmp_project_path)); - else - source_views.emplace_back(new Source::GenericView(path, tmp_project_path, language)); + if(language && (language->get_id()=="chdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc")) { + auto view_project_path=project_path; + if(view_project_path=="") { + view_project_path=boost::filesystem::path(path).parent_path().string(); + auto found_project_path=find_project_path(view_project_path); + if(found_project_path!="") { + view_project_path=found_project_path; + Singleton::terminal()->print("Project path for "+path+" set to "+view_project_path+"\n"); + } + else + Singleton::terminal()->print("Error: could not find project path for "+path+"\n"); + } + if(boost::filesystem::exists(view_project_path+"/CMakeLists.txt") && !boost::filesystem::exists(view_project_path+"/compile_commands.json")) + make_compile_commands(view_project_path); + source_views.emplace_back(new Source::ClangView(path, view_project_path)); + } + else { + auto view_project_path=project_path; + if(view_project_path=="") + view_project_path=boost::filesystem::path(path).parent_path().string(); + source_views.emplace_back(new Source::GenericView(path, view_project_path, language)); + } scrolled_windows.emplace_back(new Gtk::ScrolledWindow()); hboxes.emplace_back(new Gtk::HBox()); @@ -94,6 +112,32 @@ void Notebook::open(std::string path) { }; } +std::string Notebook::find_project_path(const std::string &path) { + const auto find_cmake_project=[this](const boost::filesystem::path &path) { + auto cmake_path=path; + cmake_path+="/CMakeLists.txt"; + for(auto &line: juci::filesystem::read_lines(cmake_path)) { + const std::regex cmake_project("^ *project *\\(.*$"); + std::smatch sm; + if(std::regex_match(line, sm, cmake_project)) { + return true; + } + } + return false; + }; + + auto boost_path=boost::filesystem::path(path); + if(find_cmake_project(boost_path)) + return boost_path.string(); + do { + boost_path=boost_path.parent_path(); + if(find_cmake_project(boost_path)) + return boost_path.string(); + } while(boost_path!=boost_path.root_directory()); + + return ""; +} + bool Notebook::save(int page) { if(page>=size()) return false; @@ -102,10 +146,38 @@ bool Notebook::save(int page) { 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"); + + //If CMakeLists.txt have been modified: + if(boost::filesystem::path(view->file_path).filename().string()=="CMakeLists.txt") { + if(project_path!="" && make_compile_commands(project_path)) { + for(auto source_view: source_views) { + if(auto source_clang_view=dynamic_cast(source_view)) { + if(project_path==source_view->project_path) { + if(source_clang_view->restart_parse()) + Singleton::terminal()->print("Reparsing "+source_clang_view->file_path+"\n"); + else + Singleton::terminal()->print("Already reparsing "+source_clang_view->file_path+". Please reopen the file manually.\n"); + } + } + } + } + } + return true; } + Singleton::terminal()->print("Error: could not save file " +view->file_path+"\n"); + } + return false; +} + +bool Notebook::make_compile_commands(const std::string &path) { + Singleton::terminal()->print("Creating "+boost::filesystem::path(path+"/compile_commands.json").string()+"\n"); + //TODO: Windows... + if(Singleton::terminal()->execute(path, "cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 2>&1")) { + if(project_path!="") + directories.open_folder(project_path); + return true; } - Singleton::terminal()->print("Error: could not save file " +view->file_path+"\n"); return false; } @@ -125,9 +197,16 @@ bool Notebook::close_current_page() { } int page = get_current_page(); remove_page(page); + if(get_current_page()==-1) + Singleton::status()->set_text(""); + auto source_view=source_views.at(page); source_views.erase(source_views.begin()+ page); scrolled_windows.erase(scrolled_windows.begin()+page); hboxes.erase(hboxes.begin()+page); + if(auto source_clang_view=dynamic_cast(source_view)) + source_clang_view->async_delete(); + else + delete source_view; } return true; } diff --git a/src/notebook.h b/src/notebook.h index 79fb9f9..ef95143 100644 --- a/src/notebook.h +++ b/src/notebook.h @@ -8,10 +8,11 @@ #include #include #include +#include "directories.h" class Notebook : public Gtk::Notebook { public: - Notebook(); + Notebook(Directories &directories); Source::View* get_view(int page); int size(); Source::View* get_current_view(); @@ -22,8 +23,11 @@ public: std::string project_path; private: + std::string find_project_path(const std::string &path); + bool make_compile_commands(const std::string &path); bool save_modified_dialog(); - std::vector > source_views; + Directories &directories; + std::vector source_views; //Is NOT freed in destructor, this is intended for quick program exit. std::vector > scrolled_windows; std::vector > hboxes; }; diff --git a/src/source.cc b/src/source.cc index fe7f71c..abbd4c9 100644 --- a/src/source.cc +++ b/src/source.cc @@ -278,8 +278,7 @@ Source::GenericView::GenericView(const std::string& file_path, const std::string clang::Index Source::ClangViewParse::clang_index(0, 0); Source::ClangViewParse::ClangViewParse(const std::string& file_path, const std::string& project_path): -Source::View(file_path, project_path), -parse_thread_go(true), parse_thread_mapped(false), parse_thread_stop(false) { +Source::View(file_path, project_path) { override_font(Pango::FontDescription(Singleton::Config::source()->font)); override_background_color(Gdk::RGBA(Singleton::Config::source()->background)); override_background_color(Gdk::RGBA(Singleton::Config::source()->background_selected), Gtk::StateFlags::STATE_FLAG_SELECTED); @@ -308,27 +307,7 @@ parse_thread_go(true), parse_thread_mapped(false), parse_thread_stop(false) { } //TODO: clear tag_class and param_spec? - int start_offset = get_source_buffer()->begin().get_offset(); - 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]; - std::size_t pos=0; - while((pos=str.find("#include", pos))!=std::string::npos) { - auto start_pos=pos; - pos=str.find('\n', pos+8); - if(pos==std::string::npos) - break; - if(start_pos==0 || str[start_pos-1]=='\n') { - str.replace(start_pos, pos-start_pos, pos-start_pos, ' '); - } - pos++; - } - init_syntax_highlighting(buffer_map, - start_offset, - end_offset); - update_syntax(); - + parsing_in_progress=Singleton::terminal()->print_in_progress("Parsing "+file_path); //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]{ @@ -339,8 +318,6 @@ parse_thread_go(true), parse_thread_mapped(false), parse_thread_stop(false) { } parse_thread_go=true; }); - - parsing_in_progress=Singleton::terminal()->print_in_progress("Parsing "+file_path); parse_done.connect([this](){ if(parse_thread_mapped) { if(parsing_mutex.try_lock()) { @@ -359,8 +336,51 @@ parse_thread_go(true), parse_thread_mapped(false), parse_thread_stop(false) { parse_thread_go=true; } }); + init_parse(); + + get_buffer()->signal_changed().connect([this]() { + start_reparse(); + type_tooltips.hide(); + diagnostic_tooltips.hide(); + }); + + get_buffer()->signal_mark_set().connect(sigc::mem_fun(*this, &Source::ClangViewParse::on_mark_set), false); +} + +void Source::ClangViewParse::init_parse() { + type_tooltips.hide(); + diagnostic_tooltips.hide(); + get_buffer()->remove_all_tags(get_buffer()->begin(), get_buffer()->end()); + clang_readable=false; + parse_thread_go=true; + parse_thread_mapped=false; + parse_thread_stop=false; + + int start_offset = get_source_buffer()->begin().get_offset(); + 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]; + std::size_t pos=0; + while((pos=str.find("#include", pos))!=std::string::npos) { + auto start_pos=pos; + pos=str.find('\n', pos+8); + if(pos==std::string::npos) + break; + if(start_pos==0 || str[start_pos-1]=='\n') { + str.replace(start_pos, pos-start_pos, pos-start_pos, ' '); + } + pos++; + } + init_syntax_highlighting(buffer_map, + start_offset, + end_offset); + update_syntax(); + set_status("parsing..."); + if(parse_thread.joinable()) + parse_thread.join(); parse_thread=std::thread([this]() { while(true) { while(!parse_thread_go && !parse_thread_stop) @@ -380,24 +400,6 @@ parse_thread_go(true), parse_thread_mapped(false), parse_thread_stop(false) { } } }); - - get_buffer()->signal_changed().connect([this]() { - start_reparse(); - type_tooltips.hide(); - diagnostic_tooltips.hide(); - }); - - get_buffer()->signal_mark_set().connect(sigc::mem_fun(*this, &Source::ClangViewParse::on_mark_set), false); -} - -Source::ClangViewParse::~ClangViewParse() { - //TODO: Is it possible to stop the clang-process in progress? - parsing_in_progress->cancel("canceled"); - parse_thread_stop=true; - if(parse_thread.joinable()) - parse_thread.join(); - parsing_mutex.lock(); //Be sure not to destroy while still parsing with libclang - parsing_mutex.unlock(); } void Source::ClangViewParse:: @@ -506,14 +508,14 @@ void Source::ClangViewParse::update_diagnostics() { auto spelling=diagnostic.spelling; auto severity_spelling=diagnostic.severity_spelling; - auto get_tooltip_buffer=[this, spelling, severity_spelling, diagnostic_tag_name]() { + auto create_tooltip_buffer=[this, spelling, severity_spelling, diagnostic_tag_name]() { auto tooltip_buffer=Gtk::TextBuffer::create(get_buffer()->get_tag_table()); tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), severity_spelling, diagnostic_tag_name); tooltip_buffer->insert_at_cursor(":\n"+spelling); //TODO: Insert newlines to clang_tu->diagnostics[c].spelling (use 80 chars, then newline?) return tooltip_buffer; }; - diagnostic_tooltips.emplace_back(get_tooltip_buffer, *this, get_buffer()->create_mark(start), get_buffer()->create_mark(end)); + diagnostic_tooltips.emplace_back(create_tooltip_buffer, *this, get_buffer()->create_mark(start), get_buffer()->create_mark(end)); get_buffer()->apply_tag_by_name(diagnostic_tag_name+"_underline", start, end); } @@ -526,7 +528,7 @@ void Source::ClangViewParse::update_types() { if(token.get_kind()==clang::Token_Identifier && token.has_type()) { auto start=get_buffer()->get_iter_at_offset(token.offsets.first); auto end=get_buffer()->get_iter_at_offset(token.offsets.second); - auto get_tooltip_buffer=[this, &token]() { + auto create_tooltip_buffer=[this, &token]() { auto tooltip_buffer=Gtk::TextBuffer::create(get_buffer()->get_tag_table()); tooltip_buffer->insert_at_cursor("Type: "+token.get_type()); auto brief_comment=token.get_brief_comments(); @@ -535,14 +537,14 @@ void Source::ClangViewParse::update_types() { return tooltip_buffer; }; - type_tooltips.emplace_back(get_tooltip_buffer, *this, get_buffer()->create_mark(start), get_buffer()->create_mark(end)); + type_tooltips.emplace_back(create_tooltip_buffer, *this, get_buffer()->create_mark(start), get_buffer()->create_mark(end)); } } } bool Source::ClangViewParse::on_motion_notify_event(GdkEventMotion* event) { delayed_tooltips_connection.disconnect(); - if(clang_readable) { + if(clang_readable && event->state==0) { Gdk::Rectangle rectangle(event->x, event->y, 1, 1); Tooltips::init(); type_tooltips.show(rectangle); @@ -872,14 +874,14 @@ void Source::ClangViewAutocomplete::autocomplete() { } buffer+="\n"; set_status("autocomplete..."); - std::thread autocomplete_thread([this, ac_data, line_nr, column_nr, buffer_map](){ + if(autocomplete_thread.joinable()) + autocomplete_thread.join(); + autocomplete_thread=std::thread([this, ac_data, line_nr, column_nr, buffer_map](){ parsing_mutex.lock(); *ac_data=move(get_autocomplete_suggestions(line_nr, column_nr, *buffer_map)); autocomplete_done(); parsing_mutex.unlock(); }); - - autocomplete_thread.detach(); } } @@ -1051,3 +1053,48 @@ Source::ClangViewAutocomplete(file_path, project_path) { } }; } + +Source::ClangView::ClangView(const std::string& file_path, const std::string& project_path): ClangViewRefactor(file_path, project_path) { + do_delete_object.connect([this](){ + if(delete_thread.joinable()) + delete_thread.join(); + delete this; + }); + do_restart_parse.connect([this](){ + init_parse(); + restart_parse_running=false; + }); +} + +void Source::ClangView::async_delete() { + parsing_in_progress->cancel("canceled, freeing resources in the background"); + parse_thread_stop=true; + delete_thread=std::thread([this](){ + //TODO: Is it possible to stop the clang-process in progress? + if(restart_parse_thread.joinable()) + restart_parse_thread.join(); + if(parse_thread.joinable()) + parse_thread.join(); + if(autocomplete_thread.joinable()) + autocomplete_thread.join(); + do_delete_object(); + }); +} + +bool Source::ClangView::restart_parse() { + if(!restart_parse_running) { + restart_parse_running=true; + parse_thread_stop=true; + if(restart_parse_thread.joinable()) + restart_parse_thread.join(); + restart_parse_thread=std::thread([this](){ + if(parse_thread.joinable()) + parse_thread.join(); + if(autocomplete_thread.joinable()) + autocomplete_thread.join(); + do_restart_parse(); + }); + return true; + } + return false; +} \ No newline at end of file diff --git a/src/source.h b/src/source.h index c874520..7bec853 100644 --- a/src/source.h +++ b/src/source.h @@ -90,17 +90,21 @@ namespace Source { class ClangViewParse : public View { public: ClangViewParse(const std::string& file_path, const std::string& project_path); - ~ClangViewParse(); protected: + void init_parse(); void start_reparse(); bool on_key_press_event(GdkEventKey* key); bool on_focus_out_event(GdkEventFocus* event); std::unique_ptr clang_tu; std::mutex parsing_mutex; std::unique_ptr clang_tokens; - bool clang_readable=false; + bool clang_readable; sigc::connection delayed_reparse_connection; sigc::connection delayed_tooltips_connection; + + std::shared_ptr parsing_in_progress; + std::thread parse_thread; + std::atomic parse_thread_stop; private: std::map get_buffer_map() const; // inits the syntax highligthing on file open @@ -121,16 +125,13 @@ namespace Source { bool on_scroll_event(GdkEventScroll* event); static clang::Index clang_index; std::vector get_compilation_commands(); - - std::shared_ptr parsing_in_progress; + Glib::Dispatcher parse_done; Glib::Dispatcher parse_start; - std::thread parse_thread; std::map parse_thread_buffer_map; std::mutex parse_thread_buffer_map_mutex; std::atomic parse_thread_go; std::atomic parse_thread_mapped; - std::atomic parse_thread_stop; }; class ClangViewAutocomplete : public ClangViewParse { @@ -139,6 +140,7 @@ namespace Source { protected: bool on_key_press_event(GdkEventKey* key); bool on_focus_out_event(GdkEventFocus* event); + std::thread autocomplete_thread; private: void start_autocomplete(); void autocomplete(); @@ -166,8 +168,15 @@ namespace Source { class ClangView : public ClangViewRefactor { public: - ClangView(const std::string& file_path, const std::string& project_path): - ClangViewRefactor(file_path, project_path) {} + ClangView(const std::string& file_path, const std::string& project_path); + void async_delete(); + bool restart_parse(); + private: + Glib::Dispatcher do_delete_object; + Glib::Dispatcher do_restart_parse; + std::thread delete_thread; + std::thread restart_parse_thread; + bool restart_parse_running=false; }; }; // class Source #endif // JUCI_SOURCE_H_ diff --git a/src/terminal.cc b/src/terminal.cc index 5f36da1..de42e7c 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -56,6 +56,39 @@ Terminal::Terminal() { }); } +bool Terminal::execute(const std::string &path, const std::string &command) { + boost::filesystem::path boost_path; + if(path=="") + boost_path=boost::filesystem::current_path(); + else + boost_path=boost::filesystem::path(path); + + //TODO: Windows... + auto cd_path_and_command="cd "+boost_path.string()+" 2>&1 && "+command; + + FILE* p = NULL; + p = popen(cd_path_and_command.c_str(), "r"); + if (p == NULL) { + print("Error: Failed to run command" + command + "\n"); + return false; + } + else { + char buffer[1028]; + while (fgets(buffer, 1028, p) != NULL) { + print(buffer); + } + int exit_code=pclose(p); + if(exit_code==0) + return true; + else + return false; + } +} + +void Terminal::async_execute(const std::string &path, const std::string &command) { + +} + void Terminal::set_change_folder_command(boost::filesystem::path CMake_path) { INFO("Terminal: set_change_folder_command"); path = CMake_path.string(); diff --git a/src/terminal.h b/src/terminal.h index 026884c..d7865c5 100644 --- a/src/terminal.h +++ b/src/terminal.h @@ -31,23 +31,25 @@ public: }; Terminal(); - void set_change_folder_command(boost::filesystem::path CMake_path); - void run(std::string executable); - void compile(); + bool execute(const std::string &path, const std::string &command); + void async_execute(const std::string &path, const std::string &command); + void set_change_folder_command(boost::filesystem::path CMake_path); //TODO: remove + void run(std::string executable); //TODO: remove + void compile(); //TODO: remove int print(std::string message); void print(int line_nr, std::string message); std::shared_ptr print_in_progress(std::string start_msg); private: - void execute_command(std::string command, std::string mode); + void execute_command(std::string command, std::string mode); //TODO: remove Gtk::TextView text_view; Gtk::ScrolledWindow scrolled_window; - std::string change_folder_command; - std::string path; - const std::string cmake_sucsess = "Build files have been written to:"; - const std::string make_built = "Built target"; - const std::string make_executable = "Linking CXX executable"; + std::string change_folder_command; //TODO: remove + std::string path; //TODO: remove + const std::string cmake_sucsess = "Build files have been written to:"; //TODO: remove + const std::string make_built = "Built target"; //TODO: remove + const std::string make_executable = "Linking CXX executable"; //TODO: remove }; #endif // JUCI_TERMINAL_H_ diff --git a/src/tooltips.cc b/src/tooltips.cc index 62b9476..5afc1f3 100644 --- a/src/tooltips.cc +++ b/src/tooltips.cc @@ -1,11 +1,15 @@ #include "tooltips.h" #include "singletons.h" +namespace sigc { + SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE +} + Gdk::Rectangle Tooltips::drawn_tooltips_rectangle=Gdk::Rectangle(); -Tooltip::Tooltip(std::function()> get_buffer, Gtk::TextView& text_view, +Tooltip::Tooltip(std::function()> create_tooltip_buffer, Gtk::TextView& text_view, Glib::RefPtr start_mark, Glib::RefPtr end_mark): -get_buffer(get_buffer), text_view(text_view), +create_tooltip_buffer(create_tooltip_buffer), text_view(text_view), start_mark(start_mark), end_mark(end_mark) {} Tooltip::~Tooltip() { @@ -38,13 +42,17 @@ void Tooltip::adjust(bool disregard_drawn) { window=std::unique_ptr(new Gtk::Window(Gtk::WindowType::WINDOW_POPUP)); window->set_events(Gdk::POINTER_MOTION_MASK); - window->signal_motion_notify_event().connect(sigc::mem_fun(*this, &Tooltip::tooltip_on_motion_notify_event), false); + window->signal_motion_notify_event().connect([this](GdkEventMotion* event){ + window->hide(); + return false; + }); window->property_decorated()=false; window->set_accept_focus(false); window->set_skip_taskbar_hint(true); window->set_default_size(0, 0); - tooltip_widget=std::unique_ptr(new Gtk::TextView(this->get_buffer())); + tooltip_widget=std::unique_ptr(new Gtk::TextView(create_tooltip_buffer())); + wrap_lines(tooltip_widget->get_buffer()); tooltip_widget->set_editable(false); tooltip_widget->override_background_color(Gdk::RGBA(Singleton::Config::source()->background_tooltips)); window->add(*tooltip_widget); @@ -76,9 +84,47 @@ void Tooltip::adjust(bool disregard_drawn) { window->move(rectangle.get_x(), rectangle.get_y()); } -bool Tooltip::tooltip_on_motion_notify_event(GdkEventMotion* event) { - window->hide(); - return false; +void Tooltip::wrap_lines(Glib::RefPtr text_buffer) { + INFO("Tooltip::wrap_lines"); + auto iter=text_buffer->begin(); + + while(iter) { + auto last_space=text_buffer->end(); + bool end=false; + for(unsigned c=0;c<=80;c++) { + if(!iter) { + end=true; + break; + } + if(*iter==' ') + last_space=iter; + if(*iter=='\n') { + end=true; + iter++; + break; + } + iter++; + } + if(!end) { + while(!last_space && iter) { //If no space (word longer than 80) + iter++; + if(iter && *iter==' ') + last_space=iter; + } + if(iter && last_space) { + auto mark=text_buffer->create_mark(last_space); + auto iter_mark=text_buffer->create_mark(iter); + auto last_space_p=last_space++; + text_buffer->erase(last_space, last_space_p); + text_buffer->insert(mark->get_iter(), "\n"); + + iter=iter_mark->get_iter(); + + text_buffer->delete_mark(mark); + text_buffer->delete_mark(iter_mark); + } + } + } } void Tooltips::show(const Gdk::Rectangle& rectangle, bool disregard_drawn) { diff --git a/src/tooltips.h b/src/tooltips.h index e41475f..3d52c27 100644 --- a/src/tooltips.h +++ b/src/tooltips.h @@ -6,7 +6,7 @@ class Tooltip { public: - Tooltip(std::function()> get_buffer, Gtk::TextView& text_view, Glib::RefPtr start_mark, Glib::RefPtr end_mark); + Tooltip(std::function()> create_tooltip_buffer, Gtk::TextView& text_view, Glib::RefPtr start_mark, Glib::RefPtr end_mark); ~Tooltip(); void update(); @@ -15,9 +15,9 @@ public: Gdk::Rectangle activation_rectangle; std::unique_ptr window; private: - bool tooltip_on_motion_notify_event(GdkEventMotion* event); + void wrap_lines(Glib::RefPtr text_buffer); - std::function()> get_buffer; + std::function()> create_tooltip_buffer; std::unique_ptr tooltip_widget; Glib::RefPtr start_mark; Glib::RefPtr end_mark; diff --git a/src/window.cc b/src/window.cc index 70c487c..1c00711 100644 --- a/src/window.cc +++ b/src/window.cc @@ -5,11 +5,14 @@ #include "config.h" #include "api.h" +#include //TODO: remove +using namespace std; //TODO: remove + namespace sigc { SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE } -Window::Window() : box(Gtk::ORIENTATION_VERTICAL) { +Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories) { INFO("Create Window"); set_title("juCi++"); set_default_size(600, 400); @@ -21,10 +24,10 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL) { create_menu(); box.pack_start(menu.get_widget(), Gtk::PACK_SHRINK); - box.pack_start(entry_box, Gtk::PACK_SHRINK); - - directory_and_notebook_panes.pack1(directories, true, true); - directory_and_notebook_panes.pack2(notebook); + directory_and_notebook_panes.pack1(directories, Gtk::SHRINK); + notebook_vbox.pack_start(notebook); + notebook_vbox.pack_end(entry_box, Gtk::PACK_SHRINK); + directory_and_notebook_panes.pack2(notebook_vbox, Gtk::SHRINK); directory_and_notebook_panes.set_position(120); vpaned.set_position(300); vpaned.pack1(directory_and_notebook_panes, true, false); @@ -42,12 +45,16 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL) { }; entry_box.signal_show().connect([this](){ - std::vector focus_chain; - focus_chain.emplace_back(&entry_box); - box.set_focus_chain(focus_chain); + box.set_focus_chain({&vpaned}); + vpaned.set_focus_chain({&directory_and_notebook_panes}); + directory_and_notebook_panes.set_focus_chain({¬ebook_vbox}); + notebook_vbox.set_focus_chain({&entry_box}); }); entry_box.signal_hide().connect([this](){ box.unset_focus_chain(); + vpaned.unset_focus_chain(); + directory_and_notebook_panes.unset_focus_chain(); + notebook_vbox.unset_focus_chain(); }); entry_box.signal_hide().connect([this]() { if(notebook.get_current_page()!=-1) { @@ -78,6 +85,9 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL) { Singleton::status()->set_text(notebook.get_current_view()->status); } }); + notebook.signal_page_removed().connect([this](Gtk::Widget* page, guint page_num) { + entry_box.hide(); + }); INFO("Window created"); } // Window constructor @@ -148,6 +158,16 @@ void Window::create_menu() { INFO("Done Redo"); }); + menu.action_group->add(Gtk::Action::create("SourceGotoLine", "Go to line"), Gtk::AccelKey(menu.key_map["source_goto_line"]), [this]() { + goto_line_entry(); + }); + menu.action_group->add(Gtk::Action::create("SourceCenterCursor", "Center cursor"), Gtk::AccelKey(menu.key_map["source_center_cursor"]), [this]() { + if(notebook.get_current_page()!=-1) { + while(gtk_events_pending()) + gtk_main_iteration(); + notebook.get_current_view()->scroll_to(notebook.get_current_view()->get_buffer()->get_insert(), 0.0, 1.0, 0.5); + } + }); menu.action_group->add(Gtk::Action::create("SourceGotoDeclaration", "Go to declaration"), Gtk::AccelKey(menu.key_map["source_goto_declaration"]), [this]() { if(notebook.get_current_page()!=-1) { if(notebook.get_current_view()->get_declaration_location) { @@ -446,14 +466,6 @@ void Window::search_and_replace_entry() { last_replace=replace_entry_it->get_text(); }); - entry_box.buttons.emplace_back("Find", [this](){ - if(notebook.get_current_page()!=-1) - notebook.get_current_view()->search_forward(); - }); - entry_box.buttons.emplace_back("Replace", [this, replace_entry_it](){ - if(notebook.get_current_page()!=-1) - notebook.get_current_view()->replace_forward(replace_entry_it->get_text()); - }); entry_box.buttons.emplace_back("Replace all", [this, replace_entry_it](){ if(notebook.get_current_page()!=-1) notebook.get_current_view()->replace_all(replace_entry_it->get_text()); @@ -483,6 +495,34 @@ void Window::search_and_replace_entry() { entry_box.show(); } +void Window::goto_line_entry() { + entry_box.clear(); + if(notebook.get_current_page()!=-1) { + entry_box.entries.emplace_back("", [this](const std::string& content){ + if(notebook.get_current_page()!=-1) { + auto buffer=notebook.get_current_view()->get_buffer(); + try { + auto line=stoul(content); + if(line>0 && line<=(unsigned long)buffer->get_line_count()) { + line--; + buffer->place_cursor(buffer->get_iter_at_line(line)); + while(gtk_events_pending()) + gtk_main_iteration(); + notebook.get_current_view()->scroll_to(buffer->get_insert(), 0.0, 1.0, 0.5); + } + } + catch(const std::exception &e) {} + entry_box.hide(); + } + }); + auto entry_it=entry_box.entries.begin(); + entry_box.buttons.emplace_back("Go to line", [this, entry_it](){ + entry_it->activate(); + }); + entry_box.show(); + } +} + void Window::rename_token_entry() { entry_box.clear(); if(notebook.get_current_page()!=-1) { @@ -502,23 +542,23 @@ void Window::rename_token_entry() { }; label_it->update(0, ""); entry_box.entries.emplace_back(*token_name, [this, token_name, token](const std::string& content){ - if(notebook.get_current_page()!=-1 && content!=*token_name) { - for(int c=0;crename_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"); - notebook.save(c); - } + if(notebook.get_current_page()!=-1 && content!=*token_name) { + for(int c=0;crename_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"); + notebook.save(c); } } - entry_box.hide(); } - }); + entry_box.hide(); + } + }); auto entry_it=entry_box.entries.begin(); entry_box.buttons.emplace_back("Rename", [this, entry_it](){ - entry_it->activate(); - }); + entry_it->activate(); + }); entry_box.show(); } } diff --git a/src/window.h b/src/window.h index 992d2be..7100de6 100644 --- a/src/window.h +++ b/src/window.h @@ -10,8 +10,8 @@ class Window : public Gtk::Window { public: Window(); - Notebook notebook; Directories directories; + Notebook notebook; protected: bool on_key_press_event(GdkEventKey *event); bool on_delete_event (GdkEventAny *event); @@ -19,6 +19,7 @@ private: Gtk::Box box; Gtk::VPaned vpaned; Gtk::Paned directory_and_notebook_panes; + Gtk::VBox notebook_vbox; Gtk::VBox terminal_vbox; Gtk::HBox status_hbox; EntryBox entry_box; @@ -33,6 +34,7 @@ private: void save_file_dialog(); void search_and_replace_entry(); + void goto_line_entry(); void rename_token_entry(); std::string last_search; std::string last_replace;