From 4460819a6c0525d91820e9eac2627f7a607bdf56 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Apr 2017 01:07:10 +0200 Subject: [PATCH] Macro editor now acts on more file changes - Adding/removing folders to packages now will make them appear/disappear automatically in the macro editor. - Adding and removing files from folders will make them appear/disappear in the macro tree. --- src/lay/layMacro.cc | 111 +++++++++++++++++++++-- src/lay/layMacro.h | 12 ++- src/lay/layMacroController.cc | 131 +++++++++++++++++---------- src/lay/layMacroController.h | 26 +++--- src/lay/layMacroEditorDialog.cc | 156 ++++++++++---------------------- src/lay/layMacroEditorDialog.h | 4 +- src/lay/laySaltController.cc | 45 ++++++++- src/lay/laySaltController.h | 14 +++ 8 files changed, 321 insertions(+), 178 deletions(-) diff --git a/src/lay/layMacro.cc b/src/lay/layMacro.cc index 2a062b540..7c180cdaf 100644 --- a/src/lay/layMacro.cc +++ b/src/lay/layMacro.cc @@ -1173,7 +1173,7 @@ MacroCollection::make_readonly (bool f) } MacroCollection * -MacroCollection::add_folder (const std::string &description, const std::string &path, const std::string &cat, bool readonly) +MacroCollection::add_folder (const std::string &description, const std::string &path, const std::string &cat, bool readonly, bool force_create) { if (! path.empty () && path[0] == ':') { readonly = true; @@ -1183,15 +1183,25 @@ MacroCollection::add_folder (const std::string &description, const std::string & if (! file_info.exists ()) { - // Try to create the folder since it does not exist yet - if (tl::verbosity () >= 20) { - tl::log << "Folder does not exist yet - trying to create it: " << path; - } - if (! QDir::root ().mkpath (file_info.absoluteFilePath ())) { - if (tl::verbosity () >= 10) { - tl::error << "Unable to create folder path: " << path; + // Try to create the folder since it does not exist yet or skip that one + if (! force_create) { + + if (tl::verbosity () >= 20) { + tl::log << "Folder does not exist - skipping: " << path; } return 0; + + } else { + + if (tl::verbosity () >= 20) { + tl::log << "Folder does not exist yet - trying to create it: " << path; + } + if (! QDir::root ().mkpath (file_info.absoluteFilePath ())) { + if (tl::verbosity () >= 10) { + tl::error << "Unable to create folder path: " << path; + } + return 0; + } } file_info.refresh (); @@ -1666,6 +1676,91 @@ MacroCollection &MacroCollection::root () return ms_root; } +static bool sync_macros (lay::MacroCollection *current, lay::MacroCollection *actual) +{ + bool ret = false; + + if (actual) { + current->make_readonly (actual->is_readonly ()); + } + + std::vector folders_to_delete; + + for (lay::MacroCollection::child_iterator m = current->begin_children (); m != current->end_children (); ++m) { + lay::MacroCollection *cm = actual ? actual->folder_by_name (m->first) : 0; + if (! cm) { + folders_to_delete.push_back (m->second); + } + } + + if (actual) { + for (lay::MacroCollection::child_iterator m = actual->begin_children (); m != actual->end_children (); ++m) { + lay::MacroCollection *cm = current->folder_by_name (m->first); + if (! cm) { + cm = current->create_folder (m->first.c_str (), false); + ret = true; + } + if (sync_macros(cm, m->second)) { + ret = true; + } + } + } + + // delete folders which do no longer exist + for (std::vector::iterator m = folders_to_delete.begin (); m != folders_to_delete.end (); ++m) { + ret = true; + sync_macros (*m, 0); + current->erase (*m); + } + + std::vector macros_to_delete; + + for (lay::MacroCollection::iterator m = current->begin (); m != current->end (); ++m) { + lay::Macro *cm = actual ? actual->macro_by_name (m->first, m->second->format ()) : 0; + if (! cm) { + macros_to_delete.push_back (m->second); + } + } + + if (actual) { + for (lay::MacroCollection::iterator m = actual->begin (); m != actual->end (); ++m) { + lay::Macro *cm = current->macro_by_name (m->first, m->second->format ()); + if (cm) { + if (*cm != *m->second) { + cm->assign (*m->second); + } + cm->set_readonly (m->second->is_readonly ()); + } else { + cm = current->create (m->first.c_str (), m->second->format ()); + cm->assign (*m->second); + cm->set_readonly (m->second->is_readonly ()); + ret = true; + } + } + } + + // erase macros from collection which are no longer used + for (std::vector::const_iterator m = macros_to_delete.begin (); m != macros_to_delete.end (); ++m) { + current->erase (*m); + ret = true; + } + + return ret; +} + +void MacroCollection::reload () +{ + // create a new collection and synchronize + + lay::MacroCollection new_collection; + for (lay::MacroCollection::child_iterator c = begin_children (); c != end_children (); ++c) { + new_collection.add_folder (c->second->description (), c->second->path (), c->second->category (), c->second->is_readonly (), false /* don't force to create */); + } + + // and synchronize current with the actual one + sync_macros (this, &new_collection); +} + static bool has_autorun_for (const lay::MacroCollection &collection, bool early) { for (lay::MacroCollection::const_child_iterator c = collection.begin_children (); c != collection.end_children (); ++c) { diff --git a/src/lay/layMacro.h b/src/lay/layMacro.h index 0b6d0b9a7..46b7c6982 100644 --- a/src/lay/layMacro.h +++ b/src/lay/layMacro.h @@ -666,8 +666,11 @@ public: * @brief Add a folder (will also scan the folder) * * @return A pointer to the new collection if successful + * + * If force_create is true (the default), the folder will be created if it does not + * exist yet. On error, 0 is returned. */ - MacroCollection *add_folder (const std::string &description, const std::string &path, const std::string &category, bool readonly); + MacroCollection *add_folder (const std::string &description, const std::string &path, const std::string &category, bool readonly, bool force_create = true); /** * @brief Gets the category tag of the collection @@ -998,6 +1001,13 @@ public: */ void rescan (); + /** + * @brief Reloads the macro collection + * + * This method is similar to rescan, but it will also remove folders and macros. + */ + void reload (); + /** * @brief Gets the root of the macro hierarchy corresponding to the configuration space */ diff --git a/src/lay/layMacroController.cc b/src/lay/layMacroController.cc index 564591c53..12c44901f 100644 --- a/src/lay/layMacroController.cc +++ b/src/lay/layMacroController.cc @@ -38,11 +38,13 @@ namespace lay { MacroController::MacroController () - : mp_macro_editor (0), mp_mw (0), m_no_implicit_macros (false), - dm_do_update_menu_with_macros (this, &MacroController::do_update_menu_with_macros) + : mp_macro_editor (0), mp_mw (0), m_no_implicit_macros (false), m_file_watcher (0), + dm_do_update_menu_with_macros (this, &MacroController::do_update_menu_with_macros), + dm_sync_file_watcher (this, &MacroController::sync_file_watcher), + dm_sync_files (this, &MacroController::sync_files) { - connect (&m_temp_macros, SIGNAL (menu_needs_update ()), this, SLOT (update_menu_with_macros ())); - connect (&m_temp_macros, SIGNAL (macro_collection_changed (MacroCollection *)), this, SLOT (update_menu_with_macros ())); + connect (&m_temp_macros, SIGNAL (menu_needs_update ()), this, SLOT (macro_collection_changed ())); + connect (&m_temp_macros, SIGNAL (macro_collection_changed (MacroCollection *)), this, SLOT (macro_collection_changed ())); } void @@ -70,8 +72,6 @@ MacroController::load () } } - - sync_implicit_macros (false); } void @@ -83,32 +83,48 @@ MacroController::initialized (lay::PluginRoot *root) mp_macro_editor->setModal (false); } - connect (&lay::MacroCollection::root (), SIGNAL (menu_needs_update ()), this, SLOT (update_menu_with_macros ())); - connect (&lay::MacroCollection::root (), SIGNAL (macro_collection_changed (MacroCollection *)), this, SLOT (update_menu_with_macros ())); + if (! m_file_watcher) { + m_file_watcher = new tl::FileSystemWatcher (this); + connect (m_file_watcher, SIGNAL (fileChanged (const QString &)), this, SLOT (file_watcher_triggered ())); + connect (m_file_watcher, SIGNAL (fileRemoved (const QString &)), this, SLOT (file_watcher_triggered ())); + } + + connect (&lay::MacroCollection::root (), SIGNAL (menu_needs_update ()), this, SLOT (macro_collection_changed ())); + connect (&lay::MacroCollection::root (), SIGNAL (macro_collection_changed (MacroCollection *)), this, SLOT (macro_collection_changed ())); if (lay::TechnologyController::instance ()) { - connect (lay::TechnologyController::instance (), SIGNAL (active_technology_changed ()), this, SLOT (update_menu_with_macros ())); - connect (lay::TechnologyController::instance (), SIGNAL (technologies_edited ()), this, SLOT (technologies_edited ())); + connect (lay::TechnologyController::instance (), SIGNAL (active_technology_changed ()), this, SLOT (macro_collection_changed ())); + connect (lay::TechnologyController::instance (), SIGNAL (technologies_edited ()), this, SLOT (sync_with_external_sources ())); } if (lay::SaltController::instance ()) { - connect (lay::SaltController::instance (), SIGNAL (salt_changed ()), this, SLOT (salt_changed ())); + connect (lay::SaltController::instance (), SIGNAL (salt_changed ()), this, SLOT (sync_with_external_sources ())); } + // synchronize the macro collection with all external sources + sync_implicit_macros (false); + // update the menus with the macro menu bindings as late as possible (now we // can be sure that the menus are created propertly) - do_update_menu_with_macros (); + macro_collection_changed (); } void MacroController::uninitialize (lay::PluginRoot * /*root*/) { - disconnect (&lay::MacroCollection::root (), SIGNAL (menu_needs_update ()), this, SLOT (update_menu_with_macros ())); - disconnect (&lay::MacroCollection::root (), SIGNAL (macro_collection_changed (MacroCollection *)), this, SLOT (update_menu_with_macros ())); + disconnect (&lay::MacroCollection::root (), SIGNAL (menu_needs_update ()), this, SLOT (macro_collection_changed ())); + disconnect (&lay::MacroCollection::root (), SIGNAL (macro_collection_changed (MacroCollection *)), this, SLOT (macro_collection_changed ())); if (lay::TechnologyController::instance ()) { - disconnect (lay::TechnologyController::instance (), SIGNAL (active_technology_changed ()), this, SLOT (update_menu_with_macros ())); - disconnect (lay::TechnologyController::instance (), SIGNAL (technologies_edited ()), this, SLOT (technologies_edited ())); + disconnect (lay::TechnologyController::instance (), SIGNAL (active_technology_changed ()), this, SLOT (macro_collection_changed ())); + disconnect (lay::TechnologyController::instance (), SIGNAL (technologies_edited ()), this, SLOT (sync_with_external_sources ())); } if (lay::SaltController::instance ()) { - disconnect (lay::SaltController::instance (), SIGNAL (salt_changed ()), this, SLOT (salt_changed ())); + disconnect (lay::SaltController::instance (), SIGNAL (salt_changed ()), this, SLOT (sync_with_external_sources ())); + } + + if (m_file_watcher) { + disconnect (m_file_watcher, SIGNAL (fileChanged (const QString &)), this, SLOT (file_watcher_triggered ())); + disconnect (m_file_watcher, SIGNAL (fileRemoved (const QString &)), this, SLOT (file_watcher_triggered ())); + delete m_file_watcher; + m_file_watcher = 0; } delete mp_macro_editor; @@ -238,9 +254,6 @@ MacroController::drop_url (const std::string &path_or_url) macro->save (); - // refresh macro editor to show new macro plus to install the menus - refresh (); - } } else { @@ -350,10 +363,16 @@ MacroController::sync_implicit_macros (bool check_autorun) std::vector folders_to_delete; + // determine the paths that will be in use + std::map new_folders_by_path; + for (std::vector::const_iterator p = external_paths.begin (); p != external_paths.end (); ++p) { + new_folders_by_path.insert (std::make_pair (p->path, p.operator-> ())); + } + // determine the paths currently in use - std::map used_folders_by_path; + std::map prev_folders_by_path; for (std::vector::const_iterator p = m_external_paths.begin (); p != m_external_paths.end (); ++p) { - used_folders_by_path.insert (std::make_pair (p->path, p.operator-> ())); + prev_folders_by_path.insert (std::make_pair (p->path, p.operator-> ())); } lay::MacroCollection *root = &lay::MacroCollection::root (); @@ -361,8 +380,8 @@ MacroController::sync_implicit_macros (bool check_autorun) for (lay::MacroCollection::child_iterator m = root->begin_children (); m != root->end_children (); ++m) { if (m->second->virtual_mode () == lay::MacroCollection::TechFolder || m->second->virtual_mode () == lay::MacroCollection::SaltFolder) { - std::map::const_iterator u = used_folders_by_path.find (m->second->path ()); - if (u == used_folders_by_path.end ()) { + std::map::const_iterator u = new_folders_by_path.find (m->second->path ()); + if (u == new_folders_by_path.end ()) { // no longer used folders_to_delete.push_back (m->second); } else { @@ -387,7 +406,7 @@ MacroController::sync_implicit_macros (bool check_autorun) for (std::vector::const_iterator p = m_external_paths.begin (); p != m_external_paths.end (); ++p) { - if (used_folders_by_path.find (p->path) != used_folders_by_path.end ()) { + if (prev_folders_by_path.find (p->path) != prev_folders_by_path.end ()) { continue; } @@ -424,14 +443,6 @@ MacroController::sync_implicit_macros (bool check_autorun) } } -void -MacroController::refresh () -{ - if (mp_macro_editor) { - mp_macro_editor->refresh (); - } -} - void MacroController::add_path (const std::string &path, const std::string &description, const std::string &category, bool readonly) { @@ -530,27 +541,22 @@ MacroController::add_macro_items_to_menu (lay::MacroCollection &collection, int } void -MacroController::salt_changed () +MacroController::sync_with_external_sources () { - sync_implicit_macros (true); - refresh (); - update_menu_with_macros (); + try { + sync_implicit_macros (true); + } catch (tl::Exception &ex) { + tl::error << ex.msg (); + } } void -MacroController::technologies_edited () -{ - sync_implicit_macros (true); - refresh (); - update_menu_with_macros (); -} - -void -MacroController::update_menu_with_macros () +MacroController::macro_collection_changed () { // empty action to macro table now we know it's invalid m_action_to_macro.clear (); dm_do_update_menu_with_macros (); + dm_sync_file_watcher (); } void @@ -597,6 +603,39 @@ MacroController::do_update_menu_with_macros () } } +void +MacroController::file_watcher_triggered () +{ + dm_sync_files (); +} + +static void +add_collections_to_file_watcher (const lay::MacroCollection &collection, tl::FileSystemWatcher *watcher) +{ + for (lay::MacroCollection::const_child_iterator c = collection.begin_children (); c != collection.end_children (); ++c) { + if (! c->second->path ().empty () && c->second->path ()[0] != ':') { + watcher->add_file (c->second->path ()); + add_collections_to_file_watcher (*c->second, watcher); + } + } +} + +void +MacroController::sync_file_watcher () +{ + m_file_watcher->clear (); + m_file_watcher->enable (false); + add_collections_to_file_watcher (lay::MacroCollection::root (), m_file_watcher); + m_file_watcher->enable (true); +} + +void +MacroController::sync_files () +{ + tl::log << tl::to_string (tr ("Detected file system change in macro folders - updating")); + lay::MacroCollection::root ().reload (); +} + MacroController * MacroController::instance () { diff --git a/src/lay/layMacroController.h b/src/lay/layMacroController.h index 8f0bf1dde..f888f983e 100644 --- a/src/lay/layMacroController.h +++ b/src/lay/layMacroController.h @@ -29,6 +29,7 @@ #include "layMacro.h" #include "tlObject.h" #include "tlDeferredExecution.h" +#include "tlFileSystemWatcher.h" #include #include @@ -120,11 +121,6 @@ public: */ void show_editor (const std::string &cat = std::string (), bool force_add = false); - /** - * @brief Reloads all macros from the paths registered - */ - void refresh (); - /** * @brief Adds a search path to the macros * After adding the paths, "load" needs to be called to actually load the macros. @@ -163,19 +159,20 @@ public: public slots: /** - * @brief Update the menu with macros bound to a menu + * @brief Updates the menu with macros bound to a menu */ - void update_menu_with_macros (); + void macro_collection_changed (); /** - * @brief Called when the technologies got changed + * @brief Called when the technologies or the salt got changed */ - void technologies_edited (); + void sync_with_external_sources (); +private slots: /** - * @brief Called when the salt (packages) got changed + * @brief Called when the file watcher detects a change in the file system */ - void salt_changed (); + void file_watcher_triggered (); private: /** @@ -215,17 +212,22 @@ private: lay::MacroEditorDialog *mp_macro_editor; lay::MainWindow *mp_mw; bool m_no_implicit_macros; - tl::DeferredMethod dm_do_update_menu_with_macros; std::vector m_macro_actions; std::map m_action_to_macro; lay::MacroCollection m_temp_macros; std::vector< std::pair > m_macro_categories; std::vector m_internal_paths; std::vector m_external_paths; + tl::FileSystemWatcher *m_file_watcher; + tl::DeferredMethod dm_do_update_menu_with_macros; + tl::DeferredMethod dm_sync_file_watcher; + tl::DeferredMethod dm_sync_files; void sync_implicit_macros (bool check_autorun); void add_macro_items_to_menu (lay::MacroCollection &collection, int &n, std::set &groups, const lay::Technology *tech, std::vector > *key_bindings); void do_update_menu_with_macros (); + void sync_file_watcher (); + void sync_files (); }; } diff --git a/src/lay/layMacroEditorDialog.cc b/src/lay/layMacroEditorDialog.cc index 58edc972f..3702b2592 100644 --- a/src/lay/layMacroEditorDialog.cc +++ b/src/lay/layMacroEditorDialog.cc @@ -253,7 +253,8 @@ MacroEditorDialog::MacroEditorDialog (QWidget * /*parent*/, lay::MacroCollection m_font_size (0), m_edit_trace_index (-1), m_add_edit_trace_enabled (true), - dm_refresh_file_watcher (this, &MacroEditorDialog::do_refresh_file_watcher) + dm_refresh_file_watcher (this, &MacroEditorDialog::do_refresh_file_watcher), + dm_update_ui_to_run_mode (this, &MacroEditorDialog::do_update_ui_to_run_mode) { // Makes this dialog receive events while progress bars are on - this way we can set breakpoints // during execution of a macro even if anything lengthy is running. @@ -264,6 +265,7 @@ MacroEditorDialog::MacroEditorDialog (QWidget * /*parent*/, lay::MacroCollection connect (mp_root, SIGNAL (macro_changed (Macro *)), this, SLOT (macro_changed (Macro *))); connect (mp_root, SIGNAL (macro_deleted (Macro *)), this, SLOT (macro_deleted (Macro *))); connect (mp_root, SIGNAL (macro_collection_deleted (MacroCollection *)), this, SLOT (macro_collection_deleted (MacroCollection *))); + connect (mp_root, SIGNAL (macro_collection_changed (MacroCollection *)), this, SLOT (macro_collection_changed (MacroCollection *))); m_categories = lay::MacroController::instance ()->macro_categories (); @@ -391,7 +393,6 @@ MacroEditorDialog::MacroEditorDialog (QWidget * /*parent*/, lay::MacroCollection watchList->addAction (actionDeleteWatches); watchList->addAction (actionClearWatches); - connect (actionAddWatch, SIGNAL (triggered ()), this, SLOT (add_watch ())); connect (actionAddWatch, SIGNAL (triggered ()), this, SLOT (add_watch ())); connect (actionEditWatch, SIGNAL (triggered ()), this, SLOT (edit_watch ())); connect (actionDeleteWatches, SIGNAL (triggered ()), this, SLOT (del_watches ())); @@ -1025,7 +1026,7 @@ MacroEditorDialog::add_edit_trace (bool compress) } MacroEditorPage *page = dynamic_cast (tabWidget->currentWidget ()); - if (! page) { + if (! page || ! page->macro ()) { return; } @@ -1551,17 +1552,16 @@ MacroEditorDialog::macro_collection_deleted (lay::MacroCollection *collection) std::map ::iterator p = m_tab_widgets.find (*mc); if (p != m_tab_widgets.end ()) { + // disable the macro on the page - we'll ask for updates when the file + // watcher becomes active. So long, the macro is "zombie". p->second->connect_macro (0); - tabWidget->blockSignals (true); // blockSignals prevents a reentrant call into set_current of the tree - tabWidget->removeTab (tabWidget->indexOf (p->second)); - tabWidget->blockSignals (false); - delete p->second; m_tab_widgets.erase (p); } } refresh_file_watcher (); + update_ui_to_run_mode (); } void @@ -1573,15 +1573,20 @@ MacroEditorDialog::macro_deleted (lay::Macro *macro) std::map ::iterator page = m_tab_widgets.find (macro); if (page != m_tab_widgets.end ()) { + // disable the macro on the page - we'll ask for updates when the file + // watcher becomes active. So long, the macro is "zombie". page->second->connect_macro (0); - tabWidget->blockSignals (true); // blockSignals prevents a reentrant call into set_current of the tree - tabWidget->removeTab (tabWidget->indexOf (page->second)); - tabWidget->blockSignals (false); - delete page->second; m_tab_widgets.erase (page); } refresh_file_watcher (); + update_ui_to_run_mode (); +} + +void +MacroEditorDialog::macro_collection_changed (lay::MacroCollection * /*collection*/) +{ + refresh_file_watcher (); } void @@ -1624,7 +1629,7 @@ MacroEditorDialog::current_tab_changed (int index) update_ui_to_run_mode (); } -lay::Macro *MacroEditorDialog::create_macro_here(const char *prefix) +lay::Macro *MacroEditorDialog::create_macro_here (const char *prefix) { lay::MacroEditorTree *mt = current_macro_tree (); MacroCollection *collection = mt->current_macro_collection (); @@ -1954,11 +1959,14 @@ MacroEditorDialog::setup_button_clicked () m_save_all_on_run = data.save_all_on_run; - for (std::map::const_iterator f = m_tab_widgets.begin (); f != m_tab_widgets.end (); ++f) { - f->second->set_ntab (m_ntab); - f->second->set_nindent (m_nindent); - f->second->apply_attributes (); - f->second->set_font (m_font_family, m_font_size); + for (int i = 0; i < tabWidget->count (); ++i) { + MacroEditorPage *page = dynamic_cast (tabWidget->widget (i)); + if (page) { + page->set_ntab (m_ntab); + page->set_nindent (m_nindent); + page->apply_attributes (); + page->set_font (m_font_family, m_font_size); + } } // write configuration @@ -2090,14 +2098,16 @@ BEGIN_PROTECTED } MacroEditorPage *page = dynamic_cast (tabWidget->widget (index)); - if (! page || ! page->macro ()) { + if (! page) { delete tabWidget->currentWidget (); return; } - std::map ::iterator p = m_tab_widgets.find (page->macro ()); - if (p != m_tab_widgets.end ()) { - m_tab_widgets.erase (p); + for (std::map ::iterator p = m_tab_widgets.begin (); p != m_tab_widgets.end (); ++p) { + if (p->second == page) { + m_tab_widgets.erase (p); + break; + } } page->connect_macro (0); @@ -2374,7 +2384,7 @@ MacroEditorDialog::file_changed (const QString &path) { m_changed_files.push_back (path); - // Wait a little to let more to allow for more reload requests to collect + // Wait a little to allow for more reload requests to collect m_file_changed_timer->setInterval (300); m_file_changed_timer->start (); } @@ -2419,79 +2429,6 @@ MacroEditorDialog::sync_file_watcher (lay::MacroCollection * /*collection*/) #endif } -bool -MacroEditorDialog::sync_macros (lay::MacroCollection *current, lay::MacroCollection *actual) -{ - bool ret = false; - - if (actual) { - current->make_readonly (actual->is_readonly ()); - } - - std::vector folders_to_delete; - - for (lay::MacroCollection::child_iterator m = current->begin_children (); m != current->end_children (); ++m) { - lay::MacroCollection *cm = actual ? actual->folder_by_name (m->first) : 0; - if (! cm) { - folders_to_delete.push_back (m->second); - } - } - - if (actual) { - for (lay::MacroCollection::child_iterator m = actual->begin_children (); m != actual->end_children (); ++m) { - lay::MacroCollection *cm = current->folder_by_name (m->first); - if (! cm) { - cm = current->create_folder (m->first.c_str (), false); - ret = true; - } - if (sync_macros(cm, m->second)) { - ret = true; - } - } - } - - // delete folders which do no longer exist - for (std::vector::iterator m = folders_to_delete.begin (); m != folders_to_delete.end (); ++m) { - ret = true; - sync_macros (*m, 0); - current->erase (*m); - } - - std::vector macros_to_delete; - - for (lay::MacroCollection::iterator m = current->begin (); m != current->end (); ++m) { - lay::Macro *cm = actual ? actual->macro_by_name (m->first, m->second->format ()) : 0; - if (! cm) { - macros_to_delete.push_back (m->second); - } - } - - if (actual) { - for (lay::MacroCollection::iterator m = actual->begin (); m != actual->end (); ++m) { - lay::Macro *cm = current->macro_by_name (m->first, m->second->format ()); - if (cm) { - if (*cm != *m->second) { - cm->assign (*m->second); - } - cm->set_readonly (m->second->is_readonly ()); - } else { - cm = current->create (m->first.c_str (), m->second->format ()); - cm->assign (*m->second); - cm->set_readonly (m->second->is_readonly ()); - ret = true; - } - } - } - - // erase macros from collection which are no longer used - for (std::vector::const_iterator m = macros_to_delete.begin (); m != macros_to_delete.end (); ++m) { - current->erase (*m); - ret = true; - } - - return ret; -} - void MacroEditorDialog::refresh_file_watcher () { @@ -2519,18 +2456,13 @@ void MacroEditorDialog::reload_macros () { m_file_watcher->clear (); - - lay::MacroCollection new_root; - - // create a new root - for (lay::MacroCollection::child_iterator c = mp_root->begin_children (); c != mp_root->end_children (); ++c) { - new_root.add_folder (c->second->description (), c->second->path (), c->second->category (), c->second->is_readonly ()); + try { + mp_root->reload (); + refresh_file_watcher (); + } catch (...) { + refresh_file_watcher (); + throw; } - - // and synchronize current with the actual one - sync_macros (mp_root, &new_root); - - refresh_file_watcher (); } void @@ -3058,18 +2990,26 @@ MacroEditorDialog::leave_breakpoint_mode () void MacroEditorDialog::update_ui_to_run_mode () +{ + dm_update_ui_to_run_mode (); +} + +void +MacroEditorDialog::do_update_ui_to_run_mode () { double alpha = 0.95; MacroEditorPage *page = dynamic_cast (tabWidget->currentWidget ()); dbgOn->setEnabled (! m_in_exec); - runButton->setEnabled ((! m_in_exec && (mp_run_macro || (page && page->macro ()->interpreter () != lay::Macro::None))) || m_in_breakpoint); - runThisButton->setEnabled ((! m_in_exec && page && page->macro ()->interpreter () != lay::Macro::None) || m_in_breakpoint); + runButton->setEnabled ((! m_in_exec && (mp_run_macro || (page && page->macro () && page->macro ()->interpreter () != lay::Macro::None))) || m_in_breakpoint); + runThisButton->setEnabled ((! m_in_exec && page && page->macro () && page->macro ()->interpreter () != lay::Macro::None) || m_in_breakpoint); singleStepButton->setEnabled (! m_in_exec || m_in_breakpoint); nextStepButton->setEnabled (! m_in_exec || m_in_breakpoint); stopButton->setEnabled (m_in_exec); pauseButton->setEnabled (m_in_exec && ! m_in_breakpoint); + breakpointButton->setEnabled (page && page->macro ()); + clearBreakpointsButton->setEnabled (page && page->macro ()); for (std::vector::const_iterator mt = m_macro_trees.begin (); mt != m_macro_trees.end (); ++mt) { (*mt)->setEditTriggers (m_in_exec ? QAbstractItemView::NoEditTriggers : QAbstractItemView::SelectedClicked); diff --git a/src/lay/layMacroEditorDialog.h b/src/lay/layMacroEditorDialog.h index 2fc328e10..b89615caf 100644 --- a/src/lay/layMacroEditorDialog.h +++ b/src/lay/layMacroEditorDialog.h @@ -193,6 +193,7 @@ private slots: void macro_changed (Macro *macro); void macro_deleted (Macro *macro); void macro_collection_deleted (MacroCollection *collection); + void macro_collection_changed (MacroCollection *collection); void add_watch (); void edit_watch (); void del_watches (); @@ -249,10 +250,10 @@ private: void run (int stop_stack_depth, lay::Macro *macro); lay::Macro *current_run_macro (); void update_ui_to_run_mode (); + void do_update_ui_to_run_mode (); void set_run_macro (lay::Macro *m); void apply_search (bool if_needed); void process_events (QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents); - bool sync_macros (lay::MacroCollection *current, lay::MacroCollection *actual); void sync_file_watcher (lay::MacroCollection *current); void do_refresh_file_watcher (); void refresh_file_watcher (); @@ -310,6 +311,7 @@ private: QTimer *m_file_changed_timer; std::vector m_changed_files, m_removed_files; tl::DeferredMethod dm_refresh_file_watcher; + tl::DeferredMethod dm_update_ui_to_run_mode; }; } diff --git a/src/lay/laySaltController.cc b/src/lay/laySaltController.cc index 6b799f835..e0330bd5d 100644 --- a/src/lay/laySaltController.cc +++ b/src/lay/laySaltController.cc @@ -33,14 +33,21 @@ namespace lay static const std::string cfg_salt_manager_window_state ("salt-manager-window-state"); SaltController::SaltController () - : mp_salt_dialog (0), mp_mw (0) + : mp_salt_dialog (0), mp_mw (0), m_file_watcher (0), + dm_sync_file_watcher (this, &SaltController::sync_file_watcher), + dm_sync_files (this, &SaltController::sync_files) { - // .. nothing yet .. } void SaltController::initialized (lay::PluginRoot *root) { + if (! m_file_watcher) { + m_file_watcher = new tl::FileSystemWatcher (this); + connect (m_file_watcher, SIGNAL (fileChanged (const QString &)), this, SLOT (file_watcher_triggered ())); + connect (m_file_watcher, SIGNAL (fileRemoved (const QString &)), this, SLOT (file_watcher_triggered ())); + } + mp_mw = dynamic_cast (root); connect (&m_salt, SIGNAL (collections_changed ()), this, SIGNAL (salt_changed ())); @@ -51,6 +58,13 @@ SaltController::uninitialize (lay::PluginRoot * /*root*/) { disconnect (&m_salt, SIGNAL (collections_changed ()), this, SIGNAL (salt_changed ())); + if (m_file_watcher) { + disconnect (m_file_watcher, SIGNAL (fileChanged (const QString &)), this, SLOT (file_watcher_triggered ())); + disconnect (m_file_watcher, SIGNAL (fileRemoved (const QString &)), this, SLOT (file_watcher_triggered ())); + delete m_file_watcher; + m_file_watcher = 0; + } + delete mp_salt_dialog; mp_salt_dialog = 0; mp_mw = 0; @@ -133,20 +147,47 @@ SaltController::show_editor () mp_mw->config_set (cfg_salt_manager_window_state, lay::save_dialog_state (mp_salt_dialog)); } + sync_file_watcher (); + } } +void +SaltController::sync_file_watcher () +{ + m_file_watcher->clear (); + m_file_watcher->enable (false); + for (lay::Salt::flat_iterator g = m_salt.begin_flat (); g != m_salt.end_flat (); ++g) { + m_file_watcher->add_file ((*g)->path ()); + } + m_file_watcher->enable (true); +} + +void +SaltController::sync_files () +{ + tl::log << tl::to_string (tr ("Detected file system change in packages - updating")); + emit salt_changed (); +} + void SaltController::add_path (const std::string &path) { try { tl::log << tl::to_string (tr ("Scanning %1 for packages").arg (tl::to_qstring (path))); m_salt.add_location (path); + dm_sync_file_watcher (); } catch (tl::Exception &ex) { tl::error << ex.msg (); } } +void +SaltController::file_watcher_triggered () +{ + dm_sync_files (); +} + void SaltController::set_salt_mine_url (const std::string &url) { diff --git a/src/lay/laySaltController.h b/src/lay/laySaltController.h index 10f44648f..e1687f667 100644 --- a/src/lay/laySaltController.h +++ b/src/lay/laySaltController.h @@ -27,6 +27,8 @@ #include "layCommon.h" #include "layPlugin.h" #include "laySalt.h" +#include "tlFileSystemWatcher.h" +#include "tlDeferredExecution.h" #include #include @@ -145,6 +147,12 @@ public: */ static SaltController *instance (); +private slots: + /** + * @brief Called when the file watcher detects a change in the file system + */ + void file_watcher_triggered (); + signals: /** * @brief This signal is emitted if the salt changed @@ -156,6 +164,12 @@ private: lay::MainWindow *mp_mw; std::string m_salt_mine_url; lay::Salt m_salt, m_salt_mine; + tl::FileSystemWatcher *m_file_watcher; + tl::DeferredMethod dm_sync_file_watcher; + tl::DeferredMethod dm_sync_files; + + void sync_file_watcher (); + void sync_files (); }; }