diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2f16a24..52ed79e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -86,6 +86,8 @@ set(source_files juci.h dialogs.cc project.h project.cc + project_build.h + project_build.cc dispatcher.h dispatcher.cc diff --git a/src/cmake.cc b/src/cmake.cc index cff8f1b..01e372c 100644 --- a/src/cmake.cc +++ b/src/cmake.cc @@ -5,8 +5,6 @@ #include "terminal.h" #include -std::unordered_set CMake::debug_build_needed; - CMake::CMake(const boost::filesystem::path &path) { const auto find_cmake_project=[this](const boost::filesystem::path &cmake_path) { for(auto &line: filesystem::read_lines(cmake_path)) { @@ -18,7 +16,7 @@ CMake::CMake(const boost::filesystem::path &path) { return false; }; - auto search_path=path; + auto search_path=boost::filesystem::is_directory(path)?path:path.parent_path(); while(true) { auto search_cmake_path=search_path/"CMakeLists.txt"; if(boost::filesystem::exists(search_cmake_path)) @@ -35,65 +33,13 @@ CMake::CMake(const boost::filesystem::path &path) { } } -boost::filesystem::path CMake::get_default_build_path(const boost::filesystem::path &project_path) { - boost::filesystem::path default_build_path=Config::get().project.default_build_path; - - const std::string path_variable_project_directory_name=""; - size_t pos=0; - auto default_build_path_string=default_build_path.string(); - auto path_filename_string=project_path.filename().string(); - while((pos=default_build_path_string.find(path_variable_project_directory_name, pos))!=std::string::npos) { - default_build_path_string.replace(pos, path_variable_project_directory_name.size(), path_filename_string); - pos+=path_filename_string.size(); - } - if(pos!=0) - default_build_path=default_build_path_string; - - if(default_build_path.is_relative()) - default_build_path=project_path/default_build_path; - - return default_build_path; -} - -boost::filesystem::path CMake::get_debug_build_path(const boost::filesystem::path &project_path) { - boost::filesystem::path debug_build_path=Config::get().project.debug_build_path; - - const std::string path_variable_project_directory_name=""; - size_t pos=0; - auto debug_build_path_string=debug_build_path.string(); - auto path_filename_string=project_path.filename().string(); - while((pos=debug_build_path_string.find(path_variable_project_directory_name, pos))!=std::string::npos) { - debug_build_path_string.replace(pos, path_variable_project_directory_name.size(), path_filename_string); - pos+=path_filename_string.size(); - } - if(pos!=0) - debug_build_path=debug_build_path_string; - - const std::string path_variable_default_build_path=""; - pos=0; - debug_build_path_string=debug_build_path.string(); - auto default_build_path=Config::get().project.default_build_path; - while((pos=debug_build_path_string.find(path_variable_default_build_path, pos))!=std::string::npos) { - debug_build_path_string.replace(pos, path_variable_default_build_path.size(), default_build_path); - pos+=default_build_path.size(); - } - if(pos!=0) - debug_build_path=debug_build_path_string; - - if(debug_build_path.is_relative()) - debug_build_path=project_path/debug_build_path; - - return debug_build_path; -} - -bool CMake::create_default_build(const boost::filesystem::path &project_path, bool force) { +bool CMake::update_default_build(const boost::filesystem::path &default_build_path, bool force) { if(project_path.empty()) return false; if(!boost::filesystem::exists(project_path/"CMakeLists.txt")) return false; - auto default_build_path=get_default_build_path(project_path); if(default_build_path.empty()) return false; if(!boost::filesystem::exists(default_build_path)) { @@ -108,8 +54,6 @@ bool CMake::create_default_build(const boost::filesystem::path &project_path, bo if(!force && boost::filesystem::exists(default_build_path/"compile_commands.json")) return true; - debug_build_needed.emplace(project_path.string()); - auto compile_commands_path=default_build_path/"compile_commands.json"; Dialog::Message message("Creating/updating default build"); auto exit_status=Terminal::get().process(Config::get().project.cmake_command+" "+ @@ -135,14 +79,13 @@ bool CMake::create_default_build(const boost::filesystem::path &project_path, bo return false; } -bool CMake::create_debug_build(const boost::filesystem::path &project_path) { +bool CMake::update_debug_build(const boost::filesystem::path &debug_build_path, bool force) { if(project_path.empty()) return false; if(!boost::filesystem::exists(project_path/"CMakeLists.txt")) return false; - auto debug_build_path=get_debug_build_path(project_path); if(debug_build_path.empty()) return false; if(!boost::filesystem::exists(debug_build_path)) { @@ -154,11 +97,8 @@ bool CMake::create_debug_build(const boost::filesystem::path &project_path) { } } - if(boost::filesystem::exists(debug_build_path/"CMakeCache.txt")) { - auto it=debug_build_needed.find(project_path.string()); - if(it==debug_build_needed.end()) - return true; - } + if(!force && boost::filesystem::exists(debug_build_path/"CMakeCache.txt")) + return true; std::unique_ptr message; message=std::unique_ptr(new Dialog::Message("Creating/updating debug build")); @@ -166,12 +106,8 @@ bool CMake::create_debug_build(const boost::filesystem::path &project_path) { filesystem::escape_argument(project_path)+" -DCMAKE_BUILD_TYPE=Debug", debug_build_path); if(message) message->hide(); - if(exit_status==EXIT_SUCCESS) { - auto it=debug_build_needed.find(project_path.string()); - if(it!=debug_build_needed.end()) - debug_build_needed.erase(it); + if(exit_status==EXIT_SUCCESS) return true; - } return false; } diff --git a/src/cmake.h b/src/cmake.h index bfa463b..004688d 100644 --- a/src/cmake.h +++ b/src/cmake.h @@ -11,10 +11,8 @@ public: boost::filesystem::path project_path; std::vector paths; - static boost::filesystem::path get_default_build_path(const boost::filesystem::path &project_path); - static boost::filesystem::path get_debug_build_path(const boost::filesystem::path &project_path); - static bool create_default_build(const boost::filesystem::path &project_path, bool force=false); - static bool create_debug_build(const boost::filesystem::path &project_path); + bool update_default_build(const boost::filesystem::path &default_build_path, bool force=false); + bool update_debug_build(const boost::filesystem::path &debug_build_path, bool force=false); boost::filesystem::path get_executable(const boost::filesystem::path &file_path); @@ -31,6 +29,5 @@ private: void parse(); std::vector get_function_parameters(std::string &data); bool parsed=false; - static std::unordered_set debug_build_needed; }; #endif //JUCI_CMAKE_H_ diff --git a/src/dialogs.cc b/src/dialogs.cc index 32d234b..c135a4c 100644 --- a/src/dialogs.cc +++ b/src/dialogs.cc @@ -1,6 +1,4 @@ #include "dialogs.h" -#include "window.h" -#include "notebook.h" #include namespace sigc { @@ -18,7 +16,10 @@ namespace sigc { } Dialog::Message::Message(const std::string &text): Gtk::MessageDialog(text, false, Gtk::MessageType::MESSAGE_INFO, Gtk::ButtonsType::BUTTONS_NONE, true) { - set_transient_for(::Window::get()); + auto g_application=g_application_get_default(); + auto gio_application=Glib::wrap(g_application, true); + auto application=Glib::RefPtr::cast_static(gio_application); + set_transient_for(*application->get_active_window()); set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT); show_now(); @@ -27,25 +28,28 @@ Dialog::Message::Message(const std::string &text): Gtk::MessageDialog(text, fals g_main_context_iteration(NULL, false); } -std::string Dialog::gtk_dialog(const std::string &title, +std::string Dialog::gtk_dialog(const boost::filesystem::path &path, const std::string &title, const std::vector> &buttons, - Gtk::FileChooserAction gtk_options, - const std::string &file_name) { + Gtk::FileChooserAction gtk_options) { Gtk::FileChooserDialog dialog(title, gtk_options); - dialog.set_transient_for(Window::get()); - - auto current_path=Notebook::get().get_current_folder(); - boost::system::error_code ec; - if(current_path.empty()) - current_path=boost::filesystem::current_path(ec); - if(!ec) - gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), current_path.string().c_str()); - - if (!file_name.empty()) - gtk_file_chooser_set_filename((GtkFileChooser*)dialog.gobj(), file_name.c_str()); + auto g_application=g_application_get_default(); + auto gio_application=Glib::wrap(g_application, true); + auto application=Glib::RefPtr::cast_static(gio_application); + dialog.set_transient_for(*application->get_active_window()); dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT); + if(title=="Save File As") + gtk_file_chooser_set_filename(reinterpret_cast(dialog.gobj()), path.string().c_str()); + else if(!path.empty()) + gtk_file_chooser_set_current_folder(reinterpret_cast(dialog.gobj()), path.string().c_str()); + else { + boost::system::error_code ec; + auto current_path=boost::filesystem::current_path(ec); + if(!ec) + gtk_file_chooser_set_current_folder(reinterpret_cast(dialog.gobj()), current_path.string().c_str()); + } + for (auto &button : buttons) dialog.add_button(button.first, button.second); return dialog.run() == Gtk::RESPONSE_OK ? dialog.get_filename() : ""; diff --git a/src/dialogs.h b/src/dialogs.h index 68e5d19..9565dd3 100644 --- a/src/dialogs.h +++ b/src/dialogs.h @@ -7,11 +7,11 @@ class Dialog { public: - static std::string open_folder(); - static std::string open_file(); - static std::string new_file(); - static std::string new_folder(); - static std::string save_file_as(const boost::filesystem::path &file_path); + static std::string open_folder(const boost::filesystem::path &path); + static std::string open_file(const boost::filesystem::path &path); + static std::string new_file(const boost::filesystem::path &path); + static std::string new_folder(const boost::filesystem::path &path); + static std::string save_file_as(const boost::filesystem::path &path); class Message : public Gtk::MessageDialog { public: @@ -19,10 +19,9 @@ public: }; private: - static std::string gtk_dialog(const std::string &title, + static std::string gtk_dialog(const boost::filesystem::path &path, const std::string &title, const std::vector> &buttons, - Gtk::FileChooserAction gtk_options, - const std::string &file_name = ""); + Gtk::FileChooserAction gtk_options); }; #endif //JUCI_DIALOG_H_ diff --git a/src/dialogs_unix.cc b/src/dialogs_unix.cc index 04aff01..2512ce8 100644 --- a/src/dialogs_unix.cc +++ b/src/dialogs_unix.cc @@ -1,32 +1,32 @@ #include "dialogs.h" -std::string Dialog::open_folder() { - return gtk_dialog("Open Folder", +std::string Dialog::open_folder(const boost::filesystem::path &path) { + return gtk_dialog(path, "Open Folder", {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Open", Gtk::RESPONSE_OK)}, Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER); } -std::string Dialog::new_file() { - return gtk_dialog("New File", +std::string Dialog::new_file(const boost::filesystem::path &path) { + return gtk_dialog(path, "New File", {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Save", Gtk::RESPONSE_OK)}, Gtk::FILE_CHOOSER_ACTION_SAVE); } -std::string Dialog::new_folder() { - return gtk_dialog("New Folder", +std::string Dialog::new_folder(const boost::filesystem::path &path) { + return gtk_dialog(path, "New Folder", {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Create", Gtk::RESPONSE_OK)}, Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER); } -std::string Dialog::open_file() { - return gtk_dialog("Open File", +std::string Dialog::open_file(const boost::filesystem::path &path) { + return gtk_dialog(path, "Open File", {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Select", Gtk::RESPONSE_OK)}, Gtk::FILE_CHOOSER_ACTION_OPEN); } -std::string Dialog::save_file_as(const boost::filesystem::path &file_path) { - return gtk_dialog("Save File As", +std::string Dialog::save_file_as(const boost::filesystem::path &path) { + return gtk_dialog(path, "Save File As", {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Save", Gtk::RESPONSE_OK)}, - Gtk::FILE_CHOOSER_ACTION_SAVE, file_path.string()); + Gtk::FILE_CHOOSER_ACTION_SAVE); } diff --git a/src/directories.cc b/src/directories.cc index aa1a3c7..c73c004 100644 --- a/src/directories.cc +++ b/src/directories.cc @@ -3,6 +3,10 @@ #include #include #include "source.h" +#include "terminal.h" +#include "notebook.h" +#include "filesystem.h" +#include "entrybox.h" #include //TODO: remove using namespace std; //TODO: remove @@ -21,10 +25,100 @@ namespace sigc { #endif } +bool Directories::TreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path &path, const Gtk::SelectionData &selection_data) const { + return true; +} + +bool Directories::TreeStore::drag_data_received_vfunc(const TreeModel::Path &path, const Gtk::SelectionData &selection_data) { + auto &directories=Directories::get(); + + auto get_target_folder=[this, &directories](const TreeModel::Path &path) { + if(path.size()==1) + return directories.path; + else { + auto it=get_iter(path); + if(it) { + auto prev_path=path; + prev_path.up(); + it=get_iter(prev_path); + if(it) + return it->get_value(directories.column_record.path); + } + else { + auto prev_path=path; + prev_path.up(); + if(prev_path.size()==1) + return directories.path; + else { + prev_path.up(); + it=get_iter(prev_path); + if(it) + return it->get_value(directories.column_record.path); + } + } + } + return boost::filesystem::path(); + }; + + auto it=directories.get_selection()->get_selected(); + if(it) { + auto source_path=it->get_value(directories.column_record.path); + auto target_path=get_target_folder(path); + + target_path/=source_path.filename(); + + if(source_path==target_path) + return false; + + if(boost::filesystem::exists(target_path)) { + Terminal::get().print("Error: could not move file: "+target_path.string()+" already exists\n", true); + return false; + } + + bool is_directory=boost::filesystem::is_directory(source_path); + + boost::system::error_code ec; + boost::filesystem::rename(source_path, target_path, ec); + if(ec) { + Terminal::get().print("Error: could not move file: "+ec.message()+'\n', true); + return false; + } + + for(int c=0;cfile_path, source_path)) { + auto file_it=view->file_path.begin(); + for(auto source_it=source_path.begin();source_it!=source_path.end();source_it++) + file_it++; + auto new_file_path=target_path; + for(;file_it!=view->file_path.end();file_it++) + new_file_path/=*file_it; + view->file_path=new_file_path; + } + } + if(view->file_path==source_path) { + view->file_path=target_path; + break; + } + } + + Directories::get().update(); + directories.select(target_path); + } + + return false; +} + +bool Directories::TreeStore::drag_data_delete_vfunc (const Gtk::TreeModel::Path &path) { + return false; +} + Directories::Directories() : Gtk::TreeView(), stop_update_thread(false) { this->set_enable_tree_lines(true); - tree_store = Gtk::TreeStore::create(column_record); + tree_store = TreeStore::create(); + tree_store->set_column_types(column_record); set_model(tree_store); append_column("", column_record.name); auto renderer=dynamic_cast(get_column(0)->get_first_cell()); @@ -34,7 +128,7 @@ Directories::Directories() : Gtk::TreeView(), stop_update_thread(false) { set_enable_search(true); //TODO: why does this not work in OS X? set_search_column(column_record.name); - signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column){ + signal_row_activated().connect([this](const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column){ auto iter = tree_store->get_iter(path); if (iter) { auto filesystem_path=iter->get_value(column_record.path); @@ -49,7 +143,7 @@ Directories::Directories() : Gtk::TreeView(), stop_update_thread(false) { } }); - signal_test_expand_row().connect([this](const Gtk::TreeModel::iterator& iter, const Gtk::TreeModel::Path& path){ + signal_test_expand_row().connect([this](const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path){ if(iter->children().begin()->get_value(column_record.path)=="") { update_mutex.lock(); add_path(iter->get_value(column_record.path), *iter); @@ -57,7 +151,7 @@ Directories::Directories() : Gtk::TreeView(), stop_update_thread(false) { } return false; }); - signal_row_collapsed().connect([this](const Gtk::TreeModel::iterator& iter, const Gtk::TreeModel::Path& path){ + signal_row_collapsed().connect([this](const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path){ update_mutex.lock(); auto directory_str=iter->get_value(column_record.path).string(); for(auto it=last_write_times.begin();it!=last_write_times.end();) { @@ -84,34 +178,127 @@ Directories::Directories() : Gtk::TreeView(), stop_update_thread(false) { while(!stop_update_thread) { std::this_thread::sleep_for(std::chrono::milliseconds(1000)); update_mutex.lock(); - if(update_paths.size()==0) { - for(auto it=last_write_times.begin();it!=last_write_times.end();) { - boost::system::error_code ec; - auto last_write_time=boost::filesystem::last_write_time(it->first, ec); - if(!ec) { - if(it->second.secondfirst); - } - it++; + for(auto it=last_write_times.begin();it!=last_write_times.end();) { + boost::system::error_code ec; + auto last_write_time=boost::filesystem::last_write_time(it->first, ec); + auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + if(!ec) { + if(last_write_time!=now && it->second.second(it->first); + dispatcher.post([this, path, last_write_time] { + update_mutex.lock(); + auto it=last_write_times.find(*path); + if(it!=last_write_times.end()) + add_path(*path, it->second.first, last_write_time); + update_mutex.unlock(); + }); } - else - it=last_write_times.erase(it); + it++; } - if(update_paths.size()>0) { - dispatcher.post([this] { - update_mutex.lock(); - for(auto &path: update_paths) { - if(last_write_times.count(path)>0) - add_path(path, last_write_times.at(path).first); + else + it=last_write_times.erase(it); + } + update_mutex.unlock(); + } + }); + + enable_model_drag_source(); + enable_model_drag_dest(); + + menu_item_rename.set_label("Rename"); + menu_item_rename.signal_activate().connect([this] { + if(menu_popup_row_path.empty()) + return; + EntryBox::get().clear(); + auto source_path=std::make_shared(menu_popup_row_path); + EntryBox::get().entries.emplace_back(menu_popup_row_path.filename().string(), [this, source_path](const std::string &content){ + bool is_directory=boost::filesystem::is_directory(*source_path); + + boost::system::error_code ec; + auto target_path=source_path->parent_path()/content; + boost::filesystem::rename(*source_path, target_path, ec); + if(ec) + Terminal::get().print("Error: could not rename "+source_path->string()+": "+ec.message()+'\n'); + else { + update(); + select(target_path); + + for(int c=0;cfile_path, *source_path)) { + auto file_it=view->file_path.begin(); + for(auto source_it=source_path->begin();source_it!=source_path->end();source_it++) + file_it++; + auto new_file_path=target_path; + for(;file_it!=view->file_path.end();file_it++) + new_file_path/=*file_it; + view->file_path=new_file_path; } - update_paths.clear(); - update_mutex.unlock(); - }); + } + else if(view->file_path==*source_path) { + view->file_path=target_path; + g_signal_emit_by_name(view->get_buffer()->gobj(), "modified_changed"); + + std::string old_language_id; + if(view->language) + old_language_id=view->language->get_id(); + view->language=Source::guess_language(target_path); + std::string new_language_id; + if(view->language) + new_language_id=view->language->get_id(); + if(new_language_id!=old_language_id) + Terminal::get().print("Warning: language for "+target_path.string()+" has changed. Please reopen the file\n"); + } + } + + EntryBox::get().hide(); + } + }); + auto entry_it=EntryBox::get().entries.begin(); + entry_it->set_placeholder_text("Filename"); + EntryBox::get().buttons.emplace_back("Rename file", [this, entry_it](){ + entry_it->activate(); + }); + EntryBox::get().show(); + }); + menu.append(menu_item_rename); + + menu_item_delete.set_label("Delete"); + menu_item_delete.signal_activate().connect([this] { + if(menu_popup_row_path.empty()) + return; + Gtk::MessageDialog dialog((Gtk::Window&)(*get_toplevel()), "Delete!", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO); + dialog.set_default_response(Gtk::RESPONSE_NO); + dialog.set_secondary_text("Are you sure you want to delete "+menu_popup_row_path.string()+"?"); + int result = dialog.run(); + if(result==Gtk::RESPONSE_YES) { + bool is_directory=boost::filesystem::is_directory(menu_popup_row_path); + + boost::system::error_code ec; + boost::filesystem::remove_all(menu_popup_row_path, ec); + if(ec) + Terminal::get().print("Error: could not delete "+menu_popup_row_path.string()+": "+ec.message()+"\n", true); + else { + update(); + + for(int c=0;cfile_path, menu_popup_row_path)) + view->get_buffer()->set_modified(); + } + else if(view->file_path==menu_popup_row_path) + view->get_buffer()->set_modified(); } } - update_mutex.unlock(); } }); + menu.append(menu_item_delete); + + menu.show_all(); + menu.accelerate(*this); } Directories::~Directories() { @@ -120,7 +307,7 @@ Directories::~Directories() { dispatcher.disconnect(); } -void Directories::open(const boost::filesystem::path& dir_path) { +void Directories::open(const boost::filesystem::path &dir_path) { JDEBUG("start"); if(dir_path.empty()) return; @@ -128,29 +315,23 @@ void Directories::open(const boost::filesystem::path& dir_path) { tree_store->clear(); update_mutex.lock(); last_write_times.clear(); - update_paths.clear(); update_mutex.unlock(); - cmake=std::unique_ptr(new CMake(dir_path)); - CMake::create_default_build(cmake->project_path); - auto project=cmake->get_functions_parameters("project"); - if(project.size()>0 && project[0].second.size()>0) { - auto title=project[0].second[0]; - //TODO: report that set_title does not handle '_' correctly? - size_t pos=0; - while((pos=title.find('_', pos))!=std::string::npos) { - title.replace(pos, 1, "__"); - pos+=2; - } - get_column(0)->set_title(title); + + //TODO: report that set_title does not handle '_' correctly? + auto title=dir_path.filename().string(); + size_t pos=0; + while((pos=title.find('_', pos))!=std::string::npos) { + title.replace(pos, 1, "__"); + pos+=2; } - else - get_column(0)->set_title(""); + get_column(0)->set_title(title); + update_mutex.lock(); add_path(dir_path, Gtk::TreeModel::Row()); update_mutex.unlock(); - current_path=dir_path; + path=dir_path; JDEBUG("end"); } @@ -165,28 +346,28 @@ void Directories::update() { JDEBUG("end"); } -void Directories::select(const boost::filesystem::path &path) { +void Directories::select(const boost::filesystem::path &select_path) { JDEBUG("start"); - if(current_path=="") + if(path=="") return; - if(path.generic_string().substr(0, current_path.generic_string().size()+1)!=current_path.generic_string()+'/') + if(select_path.generic_string().substr(0, path.generic_string().size()+1)!=path.generic_string()+'/') return; std::list paths; boost::filesystem::path parent_path; - if(boost::filesystem::is_directory(path)) - parent_path=path; + if(boost::filesystem::is_directory(select_path)) + parent_path=select_path; else - parent_path=path.parent_path(); + parent_path=select_path.parent_path(); paths.emplace_front(parent_path); - while(parent_path!=current_path) { + while(parent_path!=path) { parent_path=parent_path.parent_path(); paths.emplace_front(parent_path); } for(auto &a_path: paths) { - tree_store->foreach_iter([this, &a_path](const Gtk::TreeModel::iterator& iter){ + tree_store->foreach_iter([this, &a_path](const Gtk::TreeModel::iterator &iter){ if(iter->get_value(column_record.path)==a_path) { update_mutex.lock(); add_path(a_path, *iter); @@ -197,8 +378,8 @@ void Directories::select(const boost::filesystem::path &path) { }); } - tree_store->foreach_iter([this, &path](const Gtk::TreeModel::iterator& iter){ - if(iter->get_value(column_record.path)==path) { + tree_store->foreach_iter([this, &select_path](const Gtk::TreeModel::iterator &iter){ + if(iter->get_value(column_record.path)==select_path) { auto tree_path=Gtk::TreePath(iter); expand_to_path(tree_path); set_cursor(tree_path); @@ -209,9 +390,23 @@ void Directories::select(const boost::filesystem::path &path) { JDEBUG("end"); } -void Directories::add_path(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &parent) { +bool Directories::on_button_press_event(GdkEventButton* event) { + if(event->type==GDK_BUTTON_PRESS && event->button==GDK_BUTTON_SECONDARY) { + Gtk::TreeModel::Path path; + if(get_path_at_pos(static_cast(event->x), static_cast(event->y), path)) { + menu_popup_row_path=get_model()->get_iter(path)->get_value(column_record.path); + menu.popup(event->button, event->time); + return true; + } + } + + return Gtk::TreeView::on_button_press_event(event); +} + +void Directories::add_path(const boost::filesystem::path &dir_path, const Gtk::TreeModel::Row &parent, time_t last_write_time) { boost::system::error_code ec; - auto last_write_time=boost::filesystem::last_write_time(dir_path, ec); + if(last_write_time==0) + last_write_time=boost::filesystem::last_write_time(dir_path, ec); if(ec) return; last_write_times[dir_path.string()]={parent, last_write_time}; diff --git a/src/directories.h b/src/directories.h index 5e5e374..61daccc 100644 --- a/src/directories.h +++ b/src/directories.h @@ -5,26 +5,38 @@ #include #include #include "boost/filesystem.hpp" -#include "cmake.h" #include #include #include +#include #include "dispatcher.h" class Directories : public Gtk::TreeView { public: - class ColumnRecord : public Gtk::TreeModel::ColumnRecord { + class TreeStore : public Gtk::TreeStore { + protected: + TreeStore() {} + + bool row_drop_possible_vfunc(const Gtk::TreeModel::Path &path, const Gtk::SelectionData &selection_data) const override; + bool drag_data_received_vfunc(const TreeModel::Path &path, const Gtk::SelectionData &selection_data) override; + bool drag_data_delete_vfunc (const Gtk::TreeModel::Path &path) override; + public: - ColumnRecord() { - add(id); - add(name); - add(path); - add(color); - } - Gtk::TreeModelColumn id; - Gtk::TreeModelColumn name; - Gtk::TreeModelColumn path; - Gtk::TreeModelColumn color; + class ColumnRecord : public Gtk::TreeModel::ColumnRecord { + public: + ColumnRecord() { + add(id); + add(name); + add(path); + add(color); + } + Gtk::TreeModelColumn id; + Gtk::TreeModelColumn name; + Gtk::TreeModelColumn path; + Gtk::TreeModelColumn color; + }; + + static Glib::RefPtr create() {return Glib::RefPtr(new TreeStore());} }; private: @@ -35,25 +47,31 @@ public: return singleton; } ~Directories(); - void open(const boost::filesystem::path& dir_path=""); + void open(const boost::filesystem::path &dir_path=""); void update(); void select(const boost::filesystem::path &path); std::function on_row_activated; - std::unique_ptr cmake; - boost::filesystem::path current_path; + boost::filesystem::path path; + +protected: + bool on_button_press_event(GdkEventButton *event) override; private: - void add_path(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &row); + void add_path(const boost::filesystem::path &dir_path, const Gtk::TreeModel::Row &row, time_t last_write_time=0); Glib::RefPtr tree_store; - ColumnRecord column_record; + TreeStore::ColumnRecord column_record; std::unordered_map > last_write_times; std::mutex update_mutex; std::thread update_thread; std::atomic stop_update_thread; Dispatcher dispatcher; - std::vector update_paths; + + Gtk::Menu menu; + Gtk::MenuItem menu_item_rename; + Gtk::MenuItem menu_item_delete; + boost::filesystem::path menu_popup_row_path; }; #endif // JUCI_DIRECTORIES_H_ diff --git a/src/entrybox.h b/src/entrybox.h index 6f8cecf..3393d59 100644 --- a/src/entrybox.h +++ b/src/entrybox.h @@ -32,9 +32,15 @@ public: Label(std::function update=nullptr); std::function update; }; - -public: + +private: EntryBox(); +public: + static EntryBox &get() { + static EntryBox singleton; + return singleton; + } + Gtk::Box upper_box; Gtk::Box lower_box; void clear(); diff --git a/src/files.h b/src/files.h index 03575ce..c69c48d 100644 --- a/src/files.h +++ b/src/files.h @@ -8,8 +8,8 @@ const std::string configjson = "{\n" " \"version\": \""+std::string(JUCI_VERSION)+"\",\n" " \"default_window_size\": {\n" -" \"width\": 600,\n" -" \"height\": 400\n" +" \"width\": 800,\n" +" \"height\": 600\n" " },\n" " \"terminal_history_size\": 1000,\n" " \"gtk_theme\": {\n" @@ -130,7 +130,7 @@ const std::string configjson = " \"cmake_command\": \"cmake\",\n" #endif " \"make_command\": \"cmake --build .\",\n" -" \"save_on_compile_or_run\": false\n" +" \"save_on_compile_or_run\": true\n" " },\n" " \"documentation_searches\": {\n" " \"clang\": {\n" diff --git a/src/filesystem.cc b/src/filesystem.cc index 7c46dea..d83ca13 100644 --- a/src/filesystem.cc +++ b/src/filesystem.cc @@ -1,6 +1,7 @@ #include #include #include +#include #include "filesystem.h" #include "logging.h" @@ -161,3 +162,21 @@ std::string filesystem::unescape(const std::string &argument) { } return escaped; } + +bool filesystem::file_in_path(const boost::filesystem::path &file_path, const boost::filesystem::path &path) { + if(std::distance(file_path.begin(), file_path.end()) #include -#include "cmake.h" +#include "project.h" #include "filesystem.h" #if GTKSOURCEVIEWMM_MAJOR_VERSION > 2 & GTKSOURCEVIEWMM_MINOR_VERSION > 17 @@ -97,24 +97,10 @@ void Notebook::open(const boost::filesystem::path &file_path) { } auto language=Source::guess_language(file_path); - boost::filesystem::path project_path; - auto &directories=Directories::get(); - if(directories.cmake && directories.cmake->project_path!="" && file_path.generic_string().substr(0, directories.cmake->project_path.generic_string().size()+1)==directories.cmake->project_path.generic_string()+'/') - project_path=directories.cmake->project_path; - else { - project_path=file_path.parent_path(); - CMake cmake(project_path); - if(cmake.project_path!="") { - project_path=cmake.project_path; - Terminal::get().print("Project path for "+file_path.string()+" set to "+project_path.string()+"\n"); - } - } - if(language && (language->get_id()=="chdr" || language->get_id()=="cpphdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc")) { - CMake::create_default_build(project_path); - source_views.emplace_back(new Source::ClangView(file_path, project_path, language)); - } + if(language && (language->get_id()=="chdr" || language->get_id()=="cpphdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc")) + source_views.emplace_back(new Source::ClangView(file_path, language)); else - source_views.emplace_back(new Source::GenericView(file_path, project_path, language)); + source_views.emplace_back(new Source::GenericView(file_path, language)); source_views.back()->scroll_to_cursor_delayed=[this](Source::View* view, bool center, bool show_tooltips) { while(g_main_context_pending(NULL)) @@ -257,31 +243,8 @@ bool Notebook::save(int page) { view->get_buffer()->set_modified(false); - //If CMakeLists.txt have been modified: - boost::filesystem::path project_path; - if(view->file_path.filename()=="CMakeLists.txt") { - auto &directories=Directories::get(); - if(directories.cmake && directories.cmake->project_path!="" && view->file_path.generic_string().substr(0, directories.cmake->project_path.generic_string().size()+1)==directories.cmake->project_path.generic_string()+'/') { - if(CMake::create_default_build(directories.cmake->project_path, true)) - project_path=directories.cmake->project_path; - } - else { - CMake cmake(view->file_path.parent_path()); - if(CMake::create_default_build(cmake.project_path, true)) - project_path=cmake.project_path; - } - if(project_path!="") { - auto debug_project_path=CMake::get_debug_build_path(project_path); - if(!debug_project_path.empty() && boost::filesystem::exists(debug_project_path)) - CMake::create_debug_build(project_path); - for(auto source_view: source_views) { - if(auto source_clang_view=dynamic_cast(source_view)) { - if(project_path==source_clang_view->project_path) - source_clang_view->full_reparse_needed=true; - } - } - } - } + Project::on_save(page); + JDEBUG("end true"); return true; } @@ -297,19 +260,6 @@ bool Notebook::save_current() { return save(get_current_page()); } -void Notebook::save_project_files() { - if(get_current_page()==-1) - return; - auto current_view=get_current_view(); - for(int c=0;cget_buffer()->get_modified()) { - if(current_view->project_path==view->project_path) - save(c); - } - } -} - bool Notebook::close(int page) { JDEBUG("start"); if (page!=-1) { @@ -358,14 +308,12 @@ bool Notebook::close_current_page() { } boost::filesystem::path Notebook::get_current_folder() { - boost::filesystem::path current_path; - - if(get_current_page()!=-1) - current_path=get_current_view()->project_path; + if(!Directories::get().path.empty()) + return Directories::get().path; + else if(get_current_page()!=-1) + return get_current_view()->file_path.parent_path(); else - current_path=Directories::get().current_path; - - return current_path; + return boost::filesystem::path(); } bool Notebook::save_modified_dialog(int page) { diff --git a/src/notebook.h b/src/notebook.h index 17dde63..6bb3127 100644 --- a/src/notebook.h +++ b/src/notebook.h @@ -34,7 +34,6 @@ public: void open(const boost::filesystem::path &file_path); bool save(int page); bool save_current(); - void save_project_files(); void configure(int view_nr); boost::filesystem::path get_current_folder(); diff --git a/src/project.cc b/src/project.cc index 297d541..c224d6c 100644 --- a/src/project.cc +++ b/src/project.cc @@ -2,6 +2,7 @@ #include "config.h" #include "terminal.h" #include "filesystem.h" +#include "directories.h" #include #include "menu.h" #include "notebook.h" @@ -9,15 +10,61 @@ #include "debug_clang.h" #endif +boost::filesystem::path Project::debug_last_stop_file_path; std::unordered_map Project::run_arguments; std::unordered_map Project::debug_run_arguments; -std::atomic Project::compiling; -std::atomic Project::debugging; +std::atomic Project::compiling(false); +std::atomic Project::debugging(false); std::pair > Project::debug_stop; -boost::filesystem::path Project::debug_last_stop_file_path; - std::unique_ptr Project::current_language; +Gtk::Label &Project::debug_status_label() { + static Gtk::Label label; + return label; +} + +void Project::save_files(const boost::filesystem::path &path) { + if(Notebook::get().get_current_page()==-1) + return; + for(int c=0;cget_buffer()->get_modified()) { + if(filesystem::file_in_path(view->file_path, path)) + Notebook::get().save(c); + } + } +} + +void Project::on_save(int page) { + if(page>=Notebook::get().size()) + return; + auto view=Notebook::get().get_view(page); + if(view->language && view->language->get_id()=="cmake") { + boost::filesystem::path cmake_path; + if(view->file_path.filename()=="CMakeLists.txt") + cmake_path=view->file_path; + else + cmake_path=filesystem::find_file_in_path_parents("CMakeLists.txt", view->file_path.parent_path()); + + if(!cmake_path.empty()) { + auto build=get_build(cmake_path); + if(dynamic_cast(build.get())) { + build->update_default_build(true); + if(boost::filesystem::exists(build->get_debug_build_path())) + build->update_debug_build(true); + + for(int c=0;c(source_view)) { + if(filesystem::file_in_path(source_clang_view->file_path, build->project_path)) + source_clang_view->full_reparse_needed=true; + } + } + } + } + } +} + void Project::debug_update_status(const std::string &debug_status) { if(debug_status.empty()) debug_status_label().set_text(""); @@ -59,57 +106,48 @@ void Project::debug_update_stop() { } std::unique_ptr Project::get_language() { + std::unique_ptr build; + if(Notebook::get().get_current_page()!=-1) { auto view=Notebook::get().get_current_view(); + build=get_build(view->file_path); if(view->language) { auto language_id=view->language->get_id(); if(language_id=="markdown") - return std::unique_ptr(new Project::Markdown()); + return std::unique_ptr(new Project::Markdown(std::move(build))); if(language_id=="python") - return std::unique_ptr(new Project::Python()); + return std::unique_ptr(new Project::Python(std::move(build))); if(language_id=="js") - return std::unique_ptr(new Project::JavaScript()); + return std::unique_ptr(new Project::JavaScript(std::move(build))); if(language_id=="html") - return std::unique_ptr(new Project::HTML()); + return std::unique_ptr(new Project::HTML(std::move(build))); } } + else + build=get_build(Directories::get().path); - return std::unique_ptr(new Project::Clang()); -} - -std::unique_ptr Project::Clang::get_cmake() { - boost::filesystem::path path; - if(Notebook::get().get_current_page()!=-1) - path=Notebook::get().get_current_view()->file_path.parent_path(); + if(dynamic_cast(build.get())) + return std::unique_ptr(new Project::Clang(std::move(build))); else - path=Directories::get().current_path; - if(path.empty()) - return nullptr; - auto cmake=std::unique_ptr(new CMake(path)); - if(cmake->project_path.empty()) - return nullptr; - if(!CMake::create_default_build(cmake->project_path)) - return nullptr; - return cmake; + return std::unique_ptr(new Project::Language(std::move(build))); } std::pair Project::Clang::get_run_arguments() { - auto cmake=get_cmake(); - if(!cmake) + if(build->get_default_build_path().empty() || !build->update_default_build()) return {"", ""}; - auto project_path=cmake->project_path.string(); + auto project_path=build->project_path.string(); auto run_arguments_it=run_arguments.find(project_path); std::string arguments; if(run_arguments_it!=run_arguments.end()) arguments=run_arguments_it->second; if(arguments.empty()) { - auto executable=cmake->get_executable(Notebook::get().get_current_page()!=-1?Notebook::get().get_current_view()->file_path:"").string(); + auto executable=build->get_executable(Notebook::get().get_current_page()!=-1?Notebook::get().get_current_view()->file_path:"").string(); if(executable!="") { - auto project_path=cmake->project_path; - auto build_path=CMake::get_default_build_path(project_path); + auto project_path=build->project_path; + auto build_path=build->get_default_build_path(); if(!build_path.empty()) { size_t pos=executable.find(project_path.string()); if(pos!=std::string::npos) @@ -118,36 +156,30 @@ std::pair Project::Clang::get_run_arguments() { arguments=filesystem::escape_argument(executable); } else - arguments=filesystem::escape_argument(CMake::get_default_build_path(cmake->project_path)); + arguments=filesystem::escape_argument(build->get_default_build_path()); } return {project_path, arguments}; } -void Project::Clang::compile() { - auto cmake=get_cmake(); - if(!cmake) +void Project::Clang::compile() { + auto default_build_path=build->get_default_build_path(); + if(default_build_path.empty() || !build->update_default_build()) return; - auto default_build_path=CMake::get_default_build_path(cmake->project_path); - if(default_build_path.empty()) - return; compiling=true; - Terminal::get().print("Compiling project "+cmake->project_path.string()+"\n"); + Terminal::get().print("Compiling project "+build->project_path.string()+"\n"); Terminal::get().async_process(Config::get().project.make_command, default_build_path, [this](int exit_status) { compiling=false; }); } void Project::Clang::compile_and_run() { - auto cmake=get_cmake(); - if(!cmake) + auto default_build_path=build->get_default_build_path(); + if(default_build_path.empty() || !build->update_default_build()) return; - auto project_path=cmake->project_path; - auto default_build_path=CMake::get_default_build_path(project_path); - if(default_build_path.empty()) - return; + auto project_path=build->project_path; auto run_arguments_it=run_arguments.find(project_path.string()); std::string arguments; @@ -155,11 +187,9 @@ void Project::Clang::compile_and_run() { arguments=run_arguments_it->second; if(arguments.empty()) { - arguments=cmake->get_executable(Notebook::get().get_current_page()!=-1?Notebook::get().get_current_view()->file_path:"").string(); + arguments=build->get_executable(Notebook::get().get_current_page()!=-1?Notebook::get().get_current_view()->file_path:"").string(); if(arguments.empty()) { - Terminal::get().print("Could not find add_executable in the following paths:\n"); - for(auto &path: cmake->paths) - Terminal::get().print(" "+path.string()+"\n"); + Terminal::get().print("Warning: could not find executable.\n"); Terminal::get().print("Solution: either use Project Set Run Arguments, or open a source file within a directory where add_executable is set.\n", true); return; } @@ -183,22 +213,21 @@ void Project::Clang::compile_and_run() { #ifdef JUCI_ENABLE_DEBUG std::pair Project::Clang::debug_get_run_arguments() { - auto cmake=get_cmake(); - if(!cmake) + if(build->get_default_build_path().empty() || !build->update_default_build()) return {"", ""}; - auto project_path=cmake->project_path.string(); + auto project_path=build->project_path.string(); auto run_arguments_it=debug_run_arguments.find(project_path); std::string arguments; if(run_arguments_it!=debug_run_arguments.end()) arguments=run_arguments_it->second; if(arguments.empty()) { - auto executable=cmake->get_executable(Notebook::get().get_current_page()!=-1?Notebook::get().get_current_view()->file_path:"").string(); + auto executable=build->get_executable(Notebook::get().get_current_page()!=-1?Notebook::get().get_current_view()->file_path:"").string(); if(executable!="") { - auto project_path=cmake->project_path; - auto build_path=CMake::get_debug_build_path(project_path); + auto project_path=build->project_path; + auto build_path=build->get_debug_build_path(); if(!build_path.empty()) { size_t pos=executable.find(project_path.string()); if(pos!=std::string::npos) @@ -207,23 +236,17 @@ std::pair Project::Clang::debug_get_run_arguments() { arguments=filesystem::escape_argument(executable); } else - arguments=filesystem::escape_argument(CMake::get_debug_build_path(cmake->project_path)); + arguments=filesystem::escape_argument(build->get_debug_build_path()); } return {project_path, arguments}; } void Project::Clang::debug_start() { - auto cmake=get_cmake(); - if(!cmake) - return; - auto project_path=cmake->project_path; - - auto debug_build_path=CMake::get_debug_build_path(project_path); - if(debug_build_path.empty()) - return; - if(!CMake::create_debug_build(project_path)) + auto debug_build_path=build->get_debug_build_path(); + if(debug_build_path.empty() || !build->update_debug_build()) return; + auto project_path=build->project_path; auto run_arguments_it=debug_run_arguments.find(project_path.string()); std::string run_arguments; @@ -231,11 +254,9 @@ void Project::Clang::debug_start() { run_arguments=run_arguments_it->second; if(run_arguments.empty()) { - run_arguments=cmake->get_executable(Notebook::get().get_current_page()!=-1?Notebook::get().get_current_view()->file_path:"").string(); + run_arguments=build->get_executable(Notebook::get().get_current_page()!=-1?Notebook::get().get_current_view()->file_path:"").string(); if(run_arguments.empty()) { - Terminal::get().print("Could not find add_executable in the following paths:\n"); - for(auto &path: cmake->paths) - Terminal::get().print(" "+path.string()+"\n"); + Terminal::get().print("Warning: could not find executable.\n"); Terminal::get().print("Solution: either use Debug Set Run Arguments, or open a source file within a directory where add_executable is set.\n", true); return; } @@ -248,7 +269,7 @@ void Project::Clang::debug_start() { auto breakpoints=std::make_shared > >(); for(int c=0;cproject_path) { + if(filesystem::file_in_path(view->file_path, project_path)) { auto iter=view->get_buffer()->begin(); if(view->get_source_buffer()->get_source_marks_at_iter(iter, "debug_breakpoint").size()>0) breakpoints->emplace_back(view->file_path, iter.get_line()+1); diff --git a/src/project.h b/src/project.h index e3f1fc7..51d5bde 100644 --- a/src/project.h +++ b/src/project.h @@ -2,37 +2,36 @@ #define JUCI_PROJECT_H_ #include -#include "cmake.h" #include -#include "directories.h" #include #include +#include #include "tooltips.h" #include "dispatcher.h" #include +#include "project_build.h" -class Project { -private: - static boost::filesystem::path debug_last_stop_file_path; -public: - static std::unordered_map run_arguments; - static std::unordered_map debug_run_arguments; - static std::atomic compiling; - static std::atomic debugging; - static std::pair > debug_stop; - static void debug_update_stop(); - static void debug_update_status(const std::string &debug_status); +namespace Project { + Gtk::Label &debug_status_label(); + void save_files(const boost::filesystem::path &path); + void on_save(int page); - static Gtk::Label &debug_status_label() { - static Gtk::Label label; - return label; - } + extern boost::filesystem::path debug_last_stop_file_path; + extern std::unordered_map run_arguments; + extern std::unordered_map debug_run_arguments; + extern std::atomic compiling; + extern std::atomic debugging; + extern std::pair > debug_stop; + void debug_update_stop(); + void debug_update_status(const std::string &debug_status); class Language { public: - Language() {} + Language(std::unique_ptr &&build): build(std::move(build)) {} virtual ~Language() {} + std::unique_ptr build; + virtual std::pair get_run_arguments() {return {"", ""};} virtual void compile() {} virtual void compile_and_run() {} @@ -60,11 +59,9 @@ public: private: Dispatcher dispatcher; public: - Clang() : Language() {} + Clang(std::unique_ptr &&build) : Language(std::move(build)) {} ~Clang() { dispatcher.disconnect(); } - std::unique_ptr get_cmake(); - std::pair get_run_arguments() override; void compile() override; void compile_and_run() override; @@ -92,7 +89,7 @@ public: class Markdown : public Language { public: - Markdown() : Language() {} + Markdown(std::unique_ptr &&build) : Language(std::move(build)) {} ~Markdown(); boost::filesystem::path last_temp_path; @@ -101,27 +98,27 @@ public: class Python : public Language { public: - Python() : Language() {} + Python(std::unique_ptr &&build) : Language(std::move(build)) {} void compile_and_run() override; }; class JavaScript : public Language { public: - JavaScript() : Language() {} + JavaScript(std::unique_ptr &&build) : Language(std::move(build)) {} void compile_and_run() override; }; class HTML : public Language { public: - HTML() : Language() {} + HTML(std::unique_ptr &&build) : Language(std::move(build)) {} void compile_and_run() override; }; - static std::unique_ptr get_language(); - static std::unique_ptr current_language; + std::unique_ptr get_language(); + extern std::unique_ptr current_language; }; #endif // JUCI_PROJECT_H_ diff --git a/src/project_build.cc b/src/project_build.cc new file mode 100644 index 0000000..430d0fb --- /dev/null +++ b/src/project_build.cc @@ -0,0 +1,77 @@ +#include "project_build.h" +#include "config.h" + +std::unique_ptr Project::get_build(const boost::filesystem::path &path) { + auto cmake=new CMake(path); + if(!cmake->project_path.empty()) + return std::unique_ptr(cmake); + else + return std::unique_ptr(new Project::Build()); +} + +boost::filesystem::path Project::Build::get_default_build_path() { + boost::filesystem::path default_build_path=Config::get().project.default_build_path; + + const std::string path_variable_project_directory_name=""; + size_t pos=0; + auto default_build_path_string=default_build_path.string(); + auto path_filename_string=project_path.filename().string(); + while((pos=default_build_path_string.find(path_variable_project_directory_name, pos))!=std::string::npos) { + default_build_path_string.replace(pos, path_variable_project_directory_name.size(), path_filename_string); + pos+=path_filename_string.size(); + } + if(pos!=0) + default_build_path=default_build_path_string; + + if(default_build_path.is_relative()) + default_build_path=project_path/default_build_path; + + return default_build_path; +} + +boost::filesystem::path Project::Build::get_debug_build_path() { + boost::filesystem::path debug_build_path=Config::get().project.debug_build_path; + + const std::string path_variable_project_directory_name=""; + size_t pos=0; + auto debug_build_path_string=debug_build_path.string(); + auto path_filename_string=project_path.filename().string(); + while((pos=debug_build_path_string.find(path_variable_project_directory_name, pos))!=std::string::npos) { + debug_build_path_string.replace(pos, path_variable_project_directory_name.size(), path_filename_string); + pos+=path_filename_string.size(); + } + if(pos!=0) + debug_build_path=debug_build_path_string; + + const std::string path_variable_default_build_path=""; + pos=0; + debug_build_path_string=debug_build_path.string(); + auto default_build_path=Config::get().project.default_build_path; + while((pos=debug_build_path_string.find(path_variable_default_build_path, pos))!=std::string::npos) { + debug_build_path_string.replace(pos, path_variable_default_build_path.size(), default_build_path); + pos+=default_build_path.size(); + } + if(pos!=0) + debug_build_path=debug_build_path_string; + + if(debug_build_path.is_relative()) + debug_build_path=project_path/debug_build_path; + + return debug_build_path; +} + +Project::CMake::CMake(const boost::filesystem::path &path) : Project::Build(), cmake(path) { + project_path=cmake.project_path; +} + +bool Project::CMake::update_default_build(bool force) { + return cmake.update_default_build(get_default_build_path(), force); +} + +bool Project::CMake::update_debug_build(bool force) { + return cmake.update_debug_build(get_debug_build_path(), force); +} + +boost::filesystem::path Project::CMake::get_executable(const boost::filesystem::path &path) { + return cmake.get_executable(path); +} diff --git a/src/project_build.h b/src/project_build.h new file mode 100644 index 0000000..ea12926 --- /dev/null +++ b/src/project_build.h @@ -0,0 +1,37 @@ +#ifndef JUCI_PROJECT_BUILD_H_ +#define JUCI_PROJECT_BUILD_H_ + +#include +#include "cmake.h" + +namespace Project { + class Build { + public: + Build() {} + virtual ~Build() {} + + boost::filesystem::path project_path; + + boost::filesystem::path get_default_build_path(); + virtual bool update_default_build(bool force=false) {return false;} + boost::filesystem::path get_debug_build_path(); + virtual bool update_debug_build(bool force=false) {return false;} + + virtual boost::filesystem::path get_executable(const boost::filesystem::path &path) {return boost::filesystem::path();} + }; + + class CMake : public Build { + ::CMake cmake; + public: + CMake(const boost::filesystem::path &path); + + bool update_default_build(bool force=false) override; + bool update_debug_build(bool force=false) override; + + boost::filesystem::path get_executable(const boost::filesystem::path &path) override; + }; + + std::unique_ptr get_build(const boost::filesystem::path &path); +} + +#endif // JUCI_PROJECT_BUILD_H_ diff --git a/src/source.cc b/src/source.cc index a74607b..cb90111 100644 --- a/src/source.cc +++ b/src/source.cc @@ -82,7 +82,7 @@ std::string Source::FixIt::string(Glib::RefPtr buffer) { ////////////// AspellConfig* Source::View::spellcheck_config=NULL; -Source::View::View(const boost::filesystem::path &file_path, const boost::filesystem::path &project_path, Glib::RefPtr language): file_path(file_path), project_path(project_path), language(language) { +Source::View::View(const boost::filesystem::path &file_path, Glib::RefPtr language): file_path(file_path), language(language) { get_source_buffer()->begin_not_undoable_action(); if(language) { if(filesystem::read_non_utf8(file_path, get_buffer())==-1) @@ -1058,6 +1058,8 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { return true; } + if(last_keyvalGDK_KEY_Hyper_R) + previous_non_modifier_keyval=last_keyval; last_keyval=key->keyval; if(get_buffer()->get_has_selection()) @@ -1283,9 +1285,30 @@ bool Source::View::on_key_press_event_basic(GdkEventKey* key) { return true; } - bool stop=Gsv::View::on_key_press_event(key); + //Workaround for TextView::on_key_press_event bug sometimes causing segmentation faults + //TODO: figure out the bug and create pull request to gtk + //Have only experienced this on OS X + //Note: valgrind reports issues on TextView::on_key_press_event as well + auto unicode=gdk_keyval_to_unicode(key->keyval); + if((key->state&(GDK_CONTROL_MASK|GDK_META_MASK))==0 && unicode>=32 && unicode!=127 && + (previous_non_modifier_keyvalGDK_KEY_dead_greek)) { + if(get_buffer()->get_has_selection()) { + Gtk::TextIter selection_start, selection_end; + get_buffer()->get_selection_bounds(selection_start, selection_end); + get_buffer()->erase(selection_start, selection_end); + } + get_buffer()->insert_at_cursor(Glib::ustring(1, unicode)); + get_source_buffer()->end_user_action(); + + //Trick to make the cursor visible right after insertion: + set_cursor_visible(false); + set_cursor_visible(); + + return true; + } + get_source_buffer()->end_user_action(); - return stop; + return Gsv::View::on_key_press_event(key); } //Bracket language indentation @@ -1724,7 +1747,7 @@ std::vector Source::View::spellcheck_get_suggestions(const Gtk::Tex ///////////////////// //// GenericView //// ///////////////////// -Source::GenericView::GenericView(const boost::filesystem::path &file_path, const boost::filesystem::path &project_path, Glib::RefPtr language) : View(file_path, project_path, language) { +Source::GenericView::GenericView(const boost::filesystem::path &file_path, Glib::RefPtr language) : View(file_path, language) { configure(); spellcheck_all=true; diff --git a/src/source.h b/src/source.h index feffbbf..f9af6a3 100644 --- a/src/source.h +++ b/src/source.h @@ -58,7 +58,7 @@ namespace Source { class View : public Gsv::View { public: - View(const boost::filesystem::path &file_path, const boost::filesystem::path &project_path, Glib::RefPtr language); + View(const boost::filesystem::path &file_path, Glib::RefPtr language); ~View(); virtual void configure(); @@ -74,7 +74,6 @@ namespace Source { void paste(); boost::filesystem::path file_path; - boost::filesystem::path project_path; Glib::RefPtr language; std::function auto_indent; @@ -117,8 +116,8 @@ namespace Source { Tooltips type_tooltips; virtual void show_diagnostic_tooltips(const Gdk::Rectangle &rectangle) {} virtual void show_type_tooltips(const Gdk::Rectangle &rectangle) {} - gdouble on_motion_last_x; - gdouble on_motion_last_y; + gdouble on_motion_last_x=0.0; + gdouble on_motion_last_y=0.0; void set_tooltip_and_dialog_events(); std::string get_line(const Gtk::TextIter &iter); @@ -155,6 +154,7 @@ namespace Source { bool spellcheck_all=false; std::unique_ptr spellcheck_suggestions_dialog; + guint previous_non_modifier_keyval=0; guint last_keyval=0; private: GtkSourceSearchContext *search_context; @@ -181,7 +181,7 @@ namespace Source { static Glib::RefPtr create() {return Glib::RefPtr(new CompletionBuffer());} }; public: - GenericView(const boost::filesystem::path &file_path, const boost::filesystem::path &project_path, Glib::RefPtr language); + GenericView(const boost::filesystem::path &file_path, Glib::RefPtr language); void parse_language_file(Glib::RefPtr &completion_buffer, bool &has_context_class, const boost::property_tree::ptree &pt); }; diff --git a/src/source_clang.cc b/src/source_clang.cc index 52800d3..98d297d 100644 --- a/src/source_clang.cc +++ b/src/source_clang.cc @@ -1,7 +1,7 @@ #include "source_clang.h" #include "config.h" #include "terminal.h" -#include "cmake.h" +#include "project_build.h" #ifdef JUCI_ENABLE_DEBUG #include "debug_clang.h" #endif @@ -22,8 +22,8 @@ namespace sigc { clang::Index Source::ClangViewParse::clang_index(0, 0); -Source::ClangViewParse::ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language): -Source::View(file_path, project_path, language) { +Source::ClangViewParse::ClangViewParse(const boost::filesystem::path &file_path, Glib::RefPtr language): +Source::View(file_path, language) { JDEBUG("start"); auto tag_table=get_buffer()->get_tag_table(); @@ -174,7 +174,9 @@ void Source::ClangViewParse::soft_reparse() { } std::vector Source::ClangViewParse::get_compilation_commands() { - clang::CompilationDatabase db(CMake::get_default_build_path(project_path).string()); + auto build=Project::get_build(file_path); + build->update_default_build(); + clang::CompilationDatabase db(build->get_default_build_path().string()); clang::CompileCommands commands(file_path.string(), db); std::vector cmds = commands.get_commands(); std::vector arguments; @@ -450,8 +452,8 @@ void Source::ClangViewParse::show_type_tooltips(const Gdk::Rectangle &rectangle) ////////////////////////////// //// ClangViewAutocomplete /// ////////////////////////////// -Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language): -Source::ClangViewParse(file_path, project_path, language), autocomplete_state(AutocompleteState::IDLE) { +Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::path &file_path, Glib::RefPtr language): +Source::ClangViewParse(file_path, language), autocomplete_state(AutocompleteState::IDLE) { get_buffer()->signal_changed().connect([this](){ if(autocomplete_dialog && autocomplete_dialog->shown) delayed_reparse_connection.disconnect(); @@ -637,7 +639,7 @@ void Source::ClangViewAutocomplete::autocomplete() { auto buffer=std::make_shared(get_buffer()->get_text()); auto iter=get_buffer()->get_insert()->get_iter(); auto line_nr=iter.get_line()+1; - auto column_nr=iter.get_line_offset()+1; + auto column_nr=iter.get_line_index()+1; auto pos=iter.get_offset()-1; while(pos>=0 && (((*buffer)[pos]>='a' && (*buffer)[pos]<='z') || ((*buffer)[pos]>='A' && (*buffer)[pos]<='Z') || ((*buffer)[pos]>='0' && (*buffer)[pos]<='9') || (*buffer)[pos]=='_')) { @@ -791,8 +793,8 @@ bool Source::ClangViewAutocomplete::full_reparse() { //////////////////////////// //// ClangViewRefactor ///// //////////////////////////// -Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language): -Source::ClangViewAutocomplete(file_path, project_path, language) { +Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file_path, Glib::RefPtr language): +Source::ClangViewAutocomplete(file_path, language) { similar_tokens_tag=get_buffer()->create_tag(); similar_tokens_tag->property_weight()=1000; //TODO: replace with Pango::WEIGHT_ULTRAHEAVY in 2016 or so (when Ubuntu 14 is history) @@ -1007,13 +1009,6 @@ Source::ClangViewAutocomplete(file_path, project_path, language) { auto referenced=cursor.get_referenced(); if(referenced) { auto usr=referenced.get_usr(); - boost::filesystem::path referenced_path=referenced.get_source_location().get_path(); - - //Terminal::get().print(usr+'\n', true); //TODO: remove - - //Return empty if referenced is within project - if(referenced_path.generic_string().substr(0, this->project_path.generic_string().size()+1)==this->project_path.generic_string()+'/') - return data; data.emplace_back("clang"); @@ -1169,7 +1164,7 @@ void Source::ClangViewRefactor::tag_similar_tokens(const Token &token) { } } -Source::ClangView::ClangView(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language): ClangViewRefactor(file_path, project_path, language) { +Source::ClangView::ClangView(const boost::filesystem::path &file_path, Glib::RefPtr language): ClangViewRefactor(file_path, language) { if(language) { get_source_buffer()->set_highlight_syntax(true); get_source_buffer()->set_language(language); diff --git a/src/source_clang.h b/src/source_clang.h index e392dfd..ba1dfe8 100644 --- a/src/source_clang.h +++ b/src/source_clang.h @@ -24,7 +24,7 @@ namespace Source { int kind; }; - ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language); + ClangViewParse(const boost::filesystem::path &file_path, Glib::RefPtr language); void configure() override; @@ -71,7 +71,7 @@ namespace Source { std::string brief_comments; }; - ClangViewAutocomplete(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language); + ClangViewAutocomplete(const boost::filesystem::path &file_path, Glib::RefPtr language); virtual void async_delete(); bool full_reparse() override; @@ -96,7 +96,7 @@ namespace Source { class ClangViewRefactor : public ClangViewAutocomplete { public: - ClangViewRefactor(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language); + ClangViewRefactor(const boost::filesystem::path &file_path, Glib::RefPtr language); protected: sigc::connection delayed_tag_similar_tokens_connection; private: @@ -109,7 +109,7 @@ namespace Source { class ClangView : public ClangViewRefactor { public: - ClangView(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language); + ClangView(const boost::filesystem::path &file_path, Glib::RefPtr language); void async_delete() override; }; } diff --git a/src/window.cc b/src/window.cc index c50ee81..2adcea4 100644 --- a/src/window.cc +++ b/src/window.cc @@ -6,6 +6,8 @@ //#include "api.h" #include "dialogs.h" #include "filesystem.h" +#include "project.h" +#include "entrybox.h" namespace sigc { #ifndef SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE @@ -38,7 +40,7 @@ Window::Window() : notebook(Notebook::get()) { directories_scrolled_window.add(Directories::get()); directory_and_notebook_panes.pack1(directories_scrolled_window, Gtk::SHRINK); notebook_vbox.pack_start(notebook); - notebook_vbox.pack_end(entry_box, Gtk::PACK_SHRINK); + notebook_vbox.pack_end(EntryBox::get(), Gtk::PACK_SHRINK); directory_and_notebook_panes.pack2(notebook_vbox, Gtk::SHRINK); directory_and_notebook_panes.set_position(static_cast(0.2*Config::get().window.default_size.first)); vpaned.set_position(static_cast(0.75*Config::get().window.default_size.second)); @@ -72,17 +74,17 @@ Window::Window() : notebook(Notebook::get()) { Terminal::get().queue_draw(); }); - entry_box.signal_show().connect([this](){ + EntryBox::get().signal_show().connect([this](){ vpaned.set_focus_chain({&directory_and_notebook_panes}); directory_and_notebook_panes.set_focus_chain({¬ebook_vbox}); - notebook_vbox.set_focus_chain({&entry_box}); + notebook_vbox.set_focus_chain({&EntryBox::get()}); }); - entry_box.signal_hide().connect([this](){ + EntryBox::get().signal_hide().connect([this](){ vpaned.unset_focus_chain(); directory_and_notebook_panes.unset_focus_chain(); notebook_vbox.unset_focus_chain(); }); - entry_box.signal_hide().connect([this]() { + EntryBox::get().signal_hide().connect([this]() { if(notebook.get_current_page()!=-1) { notebook.get_current_view()->grab_focus(); } @@ -91,9 +93,9 @@ Window::Window() : notebook(Notebook::get()) { notebook.signal_switch_page().connect([this](Gtk::Widget* page, guint page_num) { if(notebook.get_current_page()!=-1) { auto view=notebook.get_current_view(); - if(search_entry_shown && entry_box.labels.size()>0) { + if(search_entry_shown && EntryBox::get().labels.size()>0) { view->update_search_occurrences=[this](int number){ - entry_box.labels.begin()->update(0, std::to_string(number)); + EntryBox::get().labels.begin()->update(0, std::to_string(number)); }; view->search_highlight(last_search, case_sensitive_search, regex_search); } @@ -114,7 +116,7 @@ Window::Window() : notebook(Notebook::get()) { } }); notebook.signal_page_removed().connect([this](Gtk::Widget* page, guint page_num) { - entry_box.hide(); + EntryBox::get().hide(); }); about.signal_response().connect([this](int d){ @@ -161,14 +163,14 @@ void Window::set_menu_actions() { }); menu.add_action("new_file", [this]() { - boost::filesystem::path path = Dialog::new_file(); + boost::filesystem::path path = Dialog::new_file(notebook.get_current_folder()); if(path!="") { if(boost::filesystem::exists(path)) { Terminal::get().print("Error: "+path.string()+" already exists.\n", true); } else { if(filesystem::write(path)) { - if(Directories::get().current_path!="") + if(Directories::get().path!="") Directories::get().update(); notebook.open(path); Terminal::get().print("New file "+path.string()+" created.\n"); @@ -180,12 +182,12 @@ void Window::set_menu_actions() { }); menu.add_action("new_folder", [this]() { auto time_now=std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); - boost::filesystem::path path = Dialog::new_folder(); + boost::filesystem::path path = Dialog::new_folder(notebook.get_current_folder()); if(path!="" && boost::filesystem::exists(path)) { boost::system::error_code ec; auto last_write_time=boost::filesystem::last_write_time(path, ec); if(!ec && last_write_time>=time_now) { - if(Directories::get().current_path!="") + if(Directories::get().path!="") Directories::get().update(); Terminal::get().print("New folder "+path.string()+" created.\n"); } @@ -195,7 +197,7 @@ void Window::set_menu_actions() { } }); menu.add_action("new_project_cpp", [this]() { - boost::filesystem::path project_path = Dialog::new_folder(); + boost::filesystem::path project_path = Dialog::new_folder(notebook.get_current_folder()); if(project_path!="") { auto project_name=project_path.filename().string(); for(size_t c=0;cget_buffer()->get_text(); file.close(); - if(Directories::get().current_path!="") + if(Directories::get().path!="") Directories::get().update(); notebook.open(path); Terminal::get().print("File saved to: " + notebook.get_current_view()->file_path.string()+"\n"); @@ -504,54 +506,56 @@ void Window::set_menu_actions() { if(run_arguments->second.empty()) return; - entry_box.clear(); - entry_box.labels.emplace_back(); - auto label_it=entry_box.labels.begin(); + EntryBox::get().clear(); + EntryBox::get().labels.emplace_back(); + auto label_it=EntryBox::get().labels.begin(); label_it->update=[label_it](int state, const std::string& message){ label_it->set_text("Set empty to let juCi++ deduce executable"); }; label_it->update(0, ""); - entry_box.entries.emplace_back(run_arguments->second, [this, run_arguments](const std::string& content){ + EntryBox::get().entries.emplace_back(run_arguments->second, [this, run_arguments](const std::string& content){ Project::run_arguments[run_arguments->first]=content; - entry_box.hide(); + EntryBox::get().hide(); }, 50); - auto entry_it=entry_box.entries.begin(); + auto entry_it=EntryBox::get().entries.begin(); entry_it->set_placeholder_text("Project: Set Run Arguments"); - entry_box.buttons.emplace_back("Project: set run arguments", [this, entry_it](){ + EntryBox::get().buttons.emplace_back("Project: set run arguments", [this, entry_it](){ entry_it->activate(); }); - entry_box.show(); + EntryBox::get().show(); }); menu.add_action("compile_and_run", [this]() { if(Project::compiling || Project::debugging) return; + Project::current_language=Project::get_language(); + if(Config::get().project.save_on_compile_or_run) - notebook.save_project_files(); + Project::save_files(Project::current_language->build->project_path); - Project::current_language=Project::get_language(); Project::current_language->compile_and_run(); }); menu.add_action("compile", [this]() { if(Project::compiling || Project::debugging) return; + + Project::current_language=Project::get_language(); if(Config::get().project.save_on_compile_or_run) - notebook.save_project_files(); - - Project::current_language=Project::get_language(); + Project::save_files(Project::current_language->build->project_path); + Project::current_language->compile(); }); menu.add_action("run_command", [this]() { - entry_box.clear(); - entry_box.labels.emplace_back(); - auto label_it=entry_box.labels.begin(); + EntryBox::get().clear(); + EntryBox::get().labels.emplace_back(); + auto label_it=EntryBox::get().labels.begin(); label_it->update=[label_it](int state, const std::string& message){ - label_it->set_text("Run Command directory order: file project path, opened directory, current directory"); + label_it->set_text("Run Command directory order: opened directory, file path, current directory"); }; label_it->update(0, ""); - entry_box.entries.emplace_back(last_run_command, [this](const std::string& content){ + EntryBox::get().entries.emplace_back(last_run_command, [this](const std::string& content){ if(content!="") { last_run_command=content; auto run_path=notebook.get_current_folder(); @@ -561,14 +565,14 @@ void Window::set_menu_actions() { Terminal::get().async_print(content+" returned: "+std::to_string(exit_status)+'\n'); }); } - entry_box.hide(); + EntryBox::get().hide(); }, 30); - auto entry_it=entry_box.entries.begin(); + auto entry_it=EntryBox::get().entries.begin(); entry_it->set_placeholder_text("Command"); - entry_box.buttons.emplace_back("Run command", [this, entry_it](){ + EntryBox::get().buttons.emplace_back("Run command", [this, entry_it](){ entry_it->activate(); }); - entry_box.show(); + EntryBox::get().show(); }); menu.add_action("kill_last_running", [this]() { @@ -585,23 +589,23 @@ void Window::set_menu_actions() { if(run_arguments->second.empty()) return; - entry_box.clear(); - entry_box.labels.emplace_back(); - auto label_it=entry_box.labels.begin(); + EntryBox::get().clear(); + EntryBox::get().labels.emplace_back(); + auto label_it=EntryBox::get().labels.begin(); label_it->update=[label_it](int state, const std::string& message){ label_it->set_text("Set empty to let juCi++ deduce executable"); }; label_it->update(0, ""); - entry_box.entries.emplace_back(run_arguments->second, [this, run_arguments](const std::string& content){ + EntryBox::get().entries.emplace_back(run_arguments->second, [this, run_arguments](const std::string& content){ Project::debug_run_arguments[run_arguments->first]=content; - entry_box.hide(); + EntryBox::get().hide(); }, 50); - auto entry_it=entry_box.entries.begin(); + auto entry_it=EntryBox::get().entries.begin(); entry_it->set_placeholder_text("Debug: Set Run Arguments"); - entry_box.buttons.emplace_back("Debug: set run arguments", [this, entry_it](){ + EntryBox::get().buttons.emplace_back("Debug: set run arguments", [this, entry_it](){ entry_it->activate(); }); - entry_box.show(); + EntryBox::get().show(); }); menu.add_action("debug_start_continue", [this](){ if(Project::compiling) @@ -610,11 +614,11 @@ void Window::set_menu_actions() { Project::current_language->debug_continue(); return; } + + Project::current_language=Project::get_language(); if(Config::get().project.save_on_compile_or_run) - notebook.save_project_files(); - - Project::current_language=Project::get_language(); + Project::save_files(Project::current_language->build->project_path); Project::current_language->debug_start(); }); @@ -647,21 +651,21 @@ void Window::set_menu_actions() { Project::current_language->debug_show_variables(); }); menu.add_action("debug_run_command", [this]() { - entry_box.clear(); - entry_box.entries.emplace_back(last_run_debug_command, [this](const std::string& content){ + EntryBox::get().clear(); + EntryBox::get().entries.emplace_back(last_run_debug_command, [this](const std::string& content){ if(content!="") { if(Project::current_language) Project::current_language->debug_run_command(content); last_run_debug_command=content; } - entry_box.hide(); + EntryBox::get().hide(); }, 30); - auto entry_it=entry_box.entries.begin(); + auto entry_it=EntryBox::get().entries.begin(); entry_it->set_placeholder_text("Debug Command"); - entry_box.buttons.emplace_back("Run debug command", [this, entry_it](){ + EntryBox::get().buttons.emplace_back("Run debug command", [this, entry_it](){ entry_it->activate(); }); - entry_box.show(); + EntryBox::get().show(); }); menu.add_action("debug_toggle_breakpoint", [this](){ if(notebook.get_current_page()!=-1) { @@ -766,7 +770,7 @@ void Window::activate_menu_items(bool activate) { bool Window::on_key_press_event(GdkEventKey *event) { if(event->keyval==GDK_KEY_Escape) { - entry_box.hide(); + EntryBox::get().hide(); } #ifdef __APPLE__ //For Apple's Command-left, right, up, down keys else if((event->state & GDK_META_MASK)>0 && (event->state & GDK_MOD1_MASK)==0) { @@ -813,9 +817,9 @@ bool Window::on_delete_event(GdkEventAny *event) { } void Window::search_and_replace_entry() { - entry_box.clear(); - entry_box.labels.emplace_back(); - auto label_it=entry_box.labels.begin(); + EntryBox::get().clear(); + EntryBox::get().labels.emplace_back(); + auto label_it=EntryBox::get().labels.begin(); label_it->update=[label_it](int state, const std::string& message){ if(state==0) { try { @@ -830,11 +834,11 @@ void Window::search_and_replace_entry() { catch(const std::exception &e) {} } }; - entry_box.entries.emplace_back(last_search, [this](const std::string& content){ + EntryBox::get().entries.emplace_back(last_search, [this](const std::string& content){ if(notebook.get_current_page()!=-1) notebook.get_current_view()->search_forward(); }); - auto search_entry_it=entry_box.entries.begin(); + auto search_entry_it=EntryBox::get().entries.begin(); search_entry_it->set_placeholder_text("Find"); if(notebook.get_current_page()!=-1) { notebook.get_current_view()->update_search_occurrences=[label_it](int number){ @@ -855,11 +859,11 @@ void Window::search_and_replace_entry() { notebook.get_current_view()->search_highlight(search_entry_it->get_text(), case_sensitive_search, regex_search); }); - entry_box.entries.emplace_back(last_replace, [this](const std::string &content){ + EntryBox::get().entries.emplace_back(last_replace, [this](const std::string &content){ if(notebook.get_current_page()!=-1) notebook.get_current_view()->replace_forward(content); }); - auto replace_entry_it=entry_box.entries.begin(); + auto replace_entry_it=EntryBox::get().entries.begin(); replace_entry_it++; replace_entry_it->set_placeholder_text("Replace"); replace_entry_it->signal_key_press_event().connect([this, replace_entry_it](GdkEventKey* event){ @@ -873,25 +877,25 @@ void Window::search_and_replace_entry() { last_replace=replace_entry_it->get_text(); }); - entry_box.buttons.emplace_back("Replace all", [this, replace_entry_it](){ + EntryBox::get().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()); }); - entry_box.toggle_buttons.emplace_back("Match case"); - entry_box.toggle_buttons.back().set_active(case_sensitive_search); - entry_box.toggle_buttons.back().on_activate=[this, search_entry_it](){ + EntryBox::get().toggle_buttons.emplace_back("Match case"); + EntryBox::get().toggle_buttons.back().set_active(case_sensitive_search); + EntryBox::get().toggle_buttons.back().on_activate=[this, search_entry_it](){ case_sensitive_search=!case_sensitive_search; if(notebook.get_current_page()!=-1) notebook.get_current_view()->search_highlight(search_entry_it->get_text(), case_sensitive_search, regex_search); }; - entry_box.toggle_buttons.emplace_back("Use regex"); - entry_box.toggle_buttons.back().set_active(regex_search); - entry_box.toggle_buttons.back().on_activate=[this, search_entry_it](){ + EntryBox::get().toggle_buttons.emplace_back("Use regex"); + EntryBox::get().toggle_buttons.back().set_active(regex_search); + EntryBox::get().toggle_buttons.back().on_activate=[this, search_entry_it](){ regex_search=!regex_search; if(notebook.get_current_page()!=-1) notebook.get_current_view()->search_highlight(search_entry_it->get_text(), case_sensitive_search, regex_search); }; - entry_box.signal_hide().connect([this]() { + EntryBox::get().signal_hide().connect([this]() { for(int c=0;cupdate_search_occurrences=nullptr; notebook.get_view(c)->search_highlight("", case_sensitive_search, regex_search); @@ -899,19 +903,19 @@ void Window::search_and_replace_entry() { search_entry_shown=false; }); search_entry_shown=true; - entry_box.show(); + EntryBox::get().show(); } void Window::set_tab_entry() { - entry_box.clear(); + EntryBox::get().clear(); if(notebook.get_current_page()!=-1) { auto tab_char_and_size=notebook.get_current_view()->get_tab_char_and_size(); - entry_box.labels.emplace_back(); - auto label_it=entry_box.labels.begin(); + EntryBox::get().labels.emplace_back(); + auto label_it=EntryBox::get().labels.begin(); - entry_box.entries.emplace_back(std::to_string(tab_char_and_size.second)); - auto entry_tab_size_it=entry_box.entries.begin(); + EntryBox::get().entries.emplace_back(std::to_string(tab_char_and_size.second)); + auto entry_tab_size_it=EntryBox::get().entries.begin(); entry_tab_size_it->set_placeholder_text("Tab size"); char tab_char=tab_char_and_size.first; @@ -921,8 +925,8 @@ void Window::set_tab_entry() { else if(tab_char=='\t') tab_char_string="tab"; - entry_box.entries.emplace_back(tab_char_string); - auto entry_tab_char_it=entry_box.entries.rbegin(); + EntryBox::get().entries.emplace_back(tab_char_string); + auto entry_tab_char_it=EntryBox::get().entries.rbegin(); entry_tab_char_it->set_placeholder_text("Tab char"); const auto activate_function=[this, entry_tab_char_it, entry_tab_size_it, label_it](const std::string& content){ @@ -942,7 +946,7 @@ void Window::set_tab_entry() { if(tab_char!=0 && tab_size>0) { notebook.get_current_view()->set_tab_char_and_size(tab_char, tab_size); - entry_box.hide(); + EntryBox::get().hide(); } else { label_it->set_text("Tab size must be >0 and tab char set to either 'space' or 'tab'"); @@ -953,18 +957,18 @@ void Window::set_tab_entry() { entry_tab_char_it->on_activate=activate_function; entry_tab_size_it->on_activate=activate_function; - entry_box.buttons.emplace_back("Set tab in current buffer", [this, entry_tab_char_it](){ + EntryBox::get().buttons.emplace_back("Set tab in current buffer", [this, entry_tab_char_it](){ entry_tab_char_it->activate(); }); - entry_box.show(); + EntryBox::get().show(); } } void Window::goto_line_entry() { - entry_box.clear(); + EntryBox::get().clear(); if(notebook.get_current_page()!=-1) { - entry_box.entries.emplace_back("", [this](const std::string& content){ + EntryBox::get().entries.emplace_back("", [this](const std::string& content){ if(notebook.get_current_page()!=-1) { auto view=notebook.get_current_view(); try { @@ -977,31 +981,31 @@ void Window::goto_line_entry() { } } catch(const std::exception &e) {} - entry_box.hide(); + EntryBox::get().hide(); } }); - auto entry_it=entry_box.entries.begin(); + auto entry_it=EntryBox::get().entries.begin(); entry_it->set_placeholder_text("Line number"); - entry_box.buttons.emplace_back("Go to line", [this, entry_it](){ + EntryBox::get().buttons.emplace_back("Go to line", [this, entry_it](){ entry_it->activate(); }); - entry_box.show(); + EntryBox::get().show(); } } void Window::rename_token_entry() { - entry_box.clear(); + EntryBox::get().clear(); if(notebook.get_current_page()!=-1) { if(notebook.get_current_view()->get_token) { auto token=std::make_shared(notebook.get_current_view()->get_token()); if(*token) { - entry_box.labels.emplace_back(); - auto label_it=entry_box.labels.begin(); + EntryBox::get().labels.emplace_back(); + auto label_it=EntryBox::get().labels.begin(); label_it->update=[label_it](int state, const std::string& message){ label_it->set_text("Warning: only opened and parsed tabs will have its content renamed, and modified files will be saved"); }; label_it->update(0, ""); - entry_box.entries.emplace_back(token->spelling, [this, token](const std::string& content){ + EntryBox::get().entries.emplace_back(token->spelling, [this, token](const std::string& content){ if(notebook.get_current_page()!=-1 && content!=token->spelling) { std::vector modified_pages; for(int c=0;csoft_reparse_needed=false; - entry_box.hide(); + EntryBox::get().hide(); } }); - auto entry_it=entry_box.entries.begin(); + auto entry_it=EntryBox::get().entries.begin(); entry_it->set_placeholder_text("New name"); - entry_box.buttons.emplace_back("Rename", [this, entry_it](){ + EntryBox::get().buttons.emplace_back("Rename", [this, entry_it](){ entry_it->activate(); }); - entry_box.show(); + EntryBox::get().show(); } } } diff --git a/src/window.h b/src/window.h index f061305..c8ed3ee 100644 --- a/src/window.h +++ b/src/window.h @@ -2,9 +2,7 @@ #define JUCI_WINDOW_H_ #include -#include "entrybox.h" #include "notebook.h" -#include "project.h" #include class Window : public Gtk::ApplicationWindow { @@ -30,7 +28,6 @@ private: Gtk::ScrolledWindow terminal_scrolled_window; Gtk::HBox info_and_status_hbox; Gtk::AboutDialog about; - EntryBox entry_box; void configure(); void set_menu_actions();