From 50d684b5767916ee02bd1390c045129bac7201d6 Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 19 Aug 2015 07:56:11 +0200 Subject: [PATCH] Directories now gets updated automatically when files are deleted, added or renamed (max 1 sec delay). --- src/directories.cc | 165 ++++++++++++++++++++++++++++++--------------- src/directories.h | 12 +++- src/juci.cc | 2 +- src/notebook.cc | 1 - src/window.cc | 19 ++---- src/window.h | 1 - 6 files changed, 127 insertions(+), 73 deletions(-) diff --git a/src/directories.cc b/src/directories.cc index 80db07c..72ad344 100644 --- a/src/directories.cc +++ b/src/directories.cc @@ -26,80 +26,121 @@ Directories::Directories() { auto iter = tree_store->get_iter(path); if (iter) { auto path_str=iter->get_value(column_record.path); - if (boost::filesystem::is_directory(boost::filesystem::path(path_str))) { - tree_view.row_expanded(path) ? tree_view.collapse_row(path) : tree_view.expand_row(path, false); - } else { - if(on_row_activated) - on_row_activated(path_str); + if(path_str!="") { + if (boost::filesystem::is_directory(boost::filesystem::path(path_str))) { + tree_view.row_expanded(path) ? tree_view.collapse_row(path) : tree_view.expand_row(path, false); + } else { + if(on_row_activated) + on_row_activated(path_str); + } } } }); tree_view.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); + update_mutex.unlock(); } return false; }); tree_view.signal_row_collapsed().connect([this](const Gtk::TreeModel::iterator& iter, const Gtk::TreeModel::Path& path){ + update_mutex.lock(); + last_write_times.erase(iter->get_value(column_record.path)); + update_mutex.unlock(); auto children=iter->children(); if(children) { while(children) { tree_store->erase(children.begin()); } - tree_store->append(iter->children()); + auto child=tree_store->append(iter->children()); + child->set_value(column_record.name, std::string("(empty)")); } }); + + update_dispatcher.connect([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); + } + update_paths.clear(); + update_mutex.unlock(); + }); + + std::thread update_thread([this](){ + while(true) { + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + update_mutex.lock(); + if(update_paths.size()==0) { + for(auto &last_write_time: last_write_times) { + try { + if(last_write_time.second.second0) + update_dispatcher(); + } + update_mutex.unlock(); + } + }); + update_thread.detach(); } -void Directories::open_folder(const boost::filesystem::path& dir_path) { - if(dir_path!="") - tree_store->clear(); +void Directories::open(const boost::filesystem::path& dir_path) { + if(dir_path=="") + return; - auto new_path=dir_path; INFO("Open folder"); - if(dir_path=="") { - if(current_path=="") - return; - new_path=current_path; - } - std::vector expanded_paths; - if(current_path==new_path) { - tree_view.map_expanded_rows([&expanded_paths](Gtk::TreeView* tree_view, const Gtk::TreeModel::Path& path){ - expanded_paths.emplace_back(path); - }); - } - - if(dir_path!="") - cmake=std::unique_ptr(new CMake(new_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)); auto project=cmake->get_functions_parameters("project"); if(project.size()>0 && project[0].second.size()>0) tree_view.get_column(0)->set_title(project[0].second[0]); else tree_view.get_column(0)->set_title(""); - add_path(new_path, Gtk::TreeModel::Row()); - - for(auto &path: expanded_paths) - tree_view.expand_row(path, false); + update_mutex.lock(); + add_path(dir_path, Gtk::TreeModel::Row()); + update_mutex.unlock(); - current_path=new_path; + current_path=dir_path; DEBUG("Folder opened"); } -void Directories::select_path(const boost::filesystem::path &path) { +void Directories::update() { + update_mutex.lock(); + for(auto &last_write_time: last_write_times) { + add_path(last_write_time.first, last_write_time.second.first); + } + update_mutex.unlock(); +} + +void Directories::select(const boost::filesystem::path &path) { if(current_path=="") return; if(path.string().substr(0, current_path.string().size())!=current_path.string()) return; - if(boost::filesystem::is_directory(path)) - return; - std::list paths; - auto parent_path=path.parent_path(); + boost::filesystem::path parent_path; + if(boost::filesystem::is_directory(path)) + parent_path=path; + else + parent_path=path.parent_path(); paths.emplace_front(parent_path); while(parent_path!=current_path) { parent_path=parent_path.parent_path(); @@ -109,7 +150,9 @@ void Directories::select_path(const boost::filesystem::path &path) { for(auto &a_path: paths) { tree_store->foreach_iter([this, &a_path](const Gtk::TreeModel::iterator& iter){ if(iter->get_value(column_record.path)==a_path.string()) { + update_mutex.lock(); add_path(a_path, *iter); + update_mutex.unlock(); return true; } return false; @@ -121,7 +164,6 @@ void Directories::select_path(const boost::filesystem::path &path) { auto tree_path=Gtk::TreePath(iter); tree_view.expand_to_path(tree_path); tree_view.set_cursor(tree_path); - selected_path=path; return true; } return false; @@ -145,13 +187,15 @@ bool Directories::ignored(std::string path) { } void Directories::add_path(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &parent) { - auto children=tree_store->children(); + last_write_times[dir_path.string()]={parent, boost::filesystem::last_write_time(dir_path)}; + std::unique_ptr children; //Gtk::TreeNodeChildren is missing default constructor... if(parent) - children=parent.children(); - if(children) { - if(children.begin()->get_value(column_record.path)=="") { - tree_store->erase(parent->children().begin()); - } + children=std::unique_ptr(new Gtk::TreeNodeChildren(parent.children())); + else + children=std::unique_ptr(new Gtk::TreeNodeChildren(tree_store->children())); + if(*children) { + if(children->begin()->get_value(column_record.path)=="") + tree_store->erase(children->begin()); } std::unordered_set not_deleted; boost::filesystem::directory_iterator end_it; @@ -159,8 +203,8 @@ void Directories::add_path(const boost::filesystem::path& dir_path, const Gtk::T auto filename=it->path().filename().string(); if (!ignored(filename)) { bool already_added=false; - if(children) { - for(auto &child: children) { + if(*children) { + for(auto &child: *children) { if(child.get_value(column_record.name)==filename) { not_deleted.emplace(filename); already_added=true; @@ -169,29 +213,44 @@ void Directories::add_path(const boost::filesystem::path& dir_path, const Gtk::T } } if(!already_added) { - auto child = tree_store->append(children); + auto child = tree_store->append(*children); not_deleted.emplace(filename); child->set_value(column_record.name, filename); child->set_value(column_record.path, it->path().string()); if (boost::filesystem::is_directory(*it)) { child->set_value(column_record.id, "a"+filename); - tree_store->append(child->children()); + auto grandchild=tree_store->append(child->children()); + grandchild->set_value(column_record.name, std::string("(empty)")); } else child->set_value(column_record.id, "b"+filename); } } } - if(children) { - auto last_it=children.begin(); - for(auto it=children.begin();it!=children.end();it++) { + if(*children) { + auto last_it=children->begin(); + auto it=last_it; + while(it!=children->end()) { if(not_deleted.count(it->get_value(column_record.name))==0) { - tree_store->erase(it); - it=last_it; + if(it==children->begin()) { + tree_store->erase(it); + it=children->begin(); + last_it=it; + } + else { + tree_store->erase(it); + it=last_it; + it++; + } + } + else { + last_it=it; + it++; } - last_it=it; } } - else - tree_store->append(children); + if(!*children) { + auto child=tree_store->append(*children); + child->set_value(column_record.name, std::string("(empty)")); + } } diff --git a/src/directories.h b/src/directories.h index 4b7b557..1b0d6f0 100644 --- a/src/directories.h +++ b/src/directories.h @@ -6,6 +6,8 @@ #include #include "boost/filesystem.hpp" #include "cmake.h" +#include +#include class Directories : public Gtk::ScrolledWindow { public: @@ -28,8 +30,9 @@ public: }; Directories(); - void open_folder(const boost::filesystem::path& dir_path=""); - void select_path(const boost::filesystem::path &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; @@ -41,7 +44,10 @@ private: Gtk::TreeView tree_view; Glib::RefPtr tree_store; ColumnRecord column_record; - boost::filesystem::path selected_path; + std::unordered_map > last_write_times; + std::mutex update_mutex; + Glib::Dispatcher update_dispatcher; + std::vector update_paths; }; #endif // JUCI_DIRECTORIES_H_ diff --git a/src/juci.cc b/src/juci.cc index 68623ea..6390d3d 100644 --- a/src/juci.cc +++ b/src/juci.cc @@ -45,7 +45,7 @@ void app::on_activate() { bool first_directory=true; for(auto &directory: directories) { if(first_directory) { - window->directories.open_folder(directory); + window->directories.open(directory); first_directory=false; } else { diff --git a/src/notebook.cc b/src/notebook.cc index 3bc8f0e..7bfd4f9 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -120,7 +120,6 @@ bool Notebook::save(int page) { //TODO: recreate cmake even without directories open? if(view->file_path.filename()=="CMakeLists.txt") { if(directories.cmake && directories.cmake->project_path!="" && view->file_path.string().substr(0, directories.cmake->project_path.string().size())==directories.cmake->project_path.string() && CMake::create_compile_commands(directories.cmake->project_path)) { - directories.open_folder(); for(auto source_view: source_views) { if(auto source_clang_view=dynamic_cast(source_view)) { if(directories.cmake->project_path.string()==source_clang_view->project_path) { diff --git a/src/window.cc b/src/window.cc index 11e4722..be27f3c 100644 --- a/src/window.cc +++ b/src/window.cc @@ -91,7 +91,7 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil if(auto menu_item=dynamic_cast(menu.ui_manager->get_widget("/MenuBar/SourceMenu/SourceRename"))) menu_item->set_sensitive((bool)notebook.get_current_view()->rename_similar_tokens); - directories.select_path(notebook.get_current_view()->file_path); + directories.select(notebook.get_current_view()->file_path); Singleton::status()->set_text(notebook.get_current_view()->status); } @@ -99,10 +99,6 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil notebook.signal_page_removed().connect([this](Gtk::Widget* page, guint page_num) { entry_box.hide(); }); - - compile_success.connect([this](){ - directories.open_folder(); - }); INFO("Window created"); } // Window constructor @@ -217,7 +213,6 @@ void Window::create_menu() { if(notebook.get_current_page()==-1 || compiling) return; CMake cmake(notebook.get_current_view()->file_path); - directories.open_folder(); auto executables = cmake.get_functions_parameters("add_executable"); boost::filesystem::path executable_path; if(executables.size()>0 && executables[0].second.size()>0) { @@ -233,7 +228,6 @@ void Window::create_menu() { Singleton::terminal()->async_execute(Singleton::Config::terminal()->make_command, cmake.project_path, [this, executable_path, project_path](int exit_code){ compiling=false; if(exit_code==EXIT_SUCCESS) { - compile_success(); //TODO: Windows... auto executable_path_spaces_fixed=executable_path.string(); char last_char=0; @@ -261,15 +255,12 @@ void Window::create_menu() { if(notebook.get_current_page()==-1 || compiling) return; CMake cmake(notebook.get_current_view()->file_path); - directories.open_folder(); if(cmake.project_path!="") { compiling=true; Singleton::terminal()->print("Compiling project "+cmake.project_path.string()+"\n"); //TODO: Windows... Singleton::terminal()->async_execute(Singleton::Config::terminal()->make_command, cmake.project_path, [this](int exit_code){ compiling=false; - if(exit_code==EXIT_SUCCESS) - compile_success(); }); } }); @@ -369,7 +360,7 @@ void Window::new_file_entry() { else { if(juci::filesystem::write(p)) { if(directories.current_path!="") - directories.open_folder(); + directories.update(); notebook.open(boost::filesystem::canonical(p).string()); Singleton::terminal()->print("New file "+p.string()+" created.\n"); } @@ -420,7 +411,7 @@ void Window::new_cpp_project_dialog() { std::string cmakelists="cmake_minimum_required(VERSION 2.8)\n\nproject("+project_name+")\n\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -std=c++1y -Wall\")\n\nadd_executable("+project_name+" main.cpp)\n"; std::string cpp_main="#include \n\nusing namespace std;\n\nint main() {\n cout << \"Hello World!\" << endl;\n\n return 0;\n}\n"; if(juci::filesystem::write(cmakelists_path, cmakelists) && juci::filesystem::write(cpp_main_path, cpp_main)) { - directories.open_folder(project_path); + directories.open(project_path); notebook.open(cpp_main_path); Singleton::terminal()->print("C++ project "+project_name+" created.\n"); } @@ -444,7 +435,7 @@ void Window::open_folder_dialog() { if(result==Gtk::RESPONSE_OK) { std::string project_path=dialog.get_filename(); - directories.open_folder(project_path); + directories.open(project_path); } } @@ -506,7 +497,7 @@ void Window::save_file_dialog() { file << notebook.get_current_view()->get_buffer()->get_text(); file.close(); if(directories.current_path!="") - directories.open_folder(); + directories.update(); notebook.open(path); Singleton::terminal()->print("File saved to: " + notebook.get_current_view()->file_path.string()+"\n"); } diff --git a/src/window.h b/src/window.h index dbb08eb..3d2cf65 100644 --- a/src/window.h +++ b/src/window.h @@ -36,7 +36,6 @@ private: EntryBox entry_box; Menu menu; std::atomic compiling; - Glib::Dispatcher compile_success; void create_menu(); void hide();