From d5bf24666fcfefcabb4f7b62893a646f8fa8e9ba Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Apr 2017 23:58:24 +0200 Subject: [PATCH] WIP: technologies now are synced with salt - The technology list now is synched with the salt package manager so it's basically possible to include technologies into packages. This checkin also contains: - A "NoDeferredMethods" class that blocks execution of deferred methods in a region of code (preferably around message boxes). This prevents side effects when message boxes are shown and deferred methods are processed because of this. That prevention method is used in the macro controller when asking whether to execute autorun macros (that happens in slots and must not interfere with deferred methods). It's also used to protect the exception handlers. - The tech manager dialog's refresh function used to crash because of an invalid tech pointer. --- src/lay/layApplication.cc | 17 ++++++--- src/lay/layMacroController.cc | 11 ++++-- src/lay/laySaltController.cc | 15 ++++++-- src/lay/layTechSetupDialog.cc | 56 ++++++++++++++++++++++++++++-- src/lay/layTechSetupDialog.h | 1 + src/lay/layTechnologyController.cc | 30 ++++++++++++++-- src/lay/layTechnologyController.h | 6 ++++ src/tl/tlDeferredExecution.h | 34 ++++++++++++++++++ 8 files changed, 156 insertions(+), 14 deletions(-) diff --git a/src/lay/layApplication.cc b/src/lay/layApplication.cc index ccd1906c2..3ab77f072 100644 --- a/src/lay/layApplication.cc +++ b/src/lay/layApplication.cc @@ -81,6 +81,9 @@ namespace lay static void ui_exception_handler_tl (const tl::Exception &ex, QWidget *parent) { + // Prevents severe side effects if there are pending deferred methods + tl::NoDeferredMethods silent; + // if any transaction is pending (this may happen when an operation threw an exception) // close transactions. if (lay::MainWindow::instance () && lay::MainWindow::instance ()->manager ().transacting ()) { @@ -123,6 +126,9 @@ static void ui_exception_handler_tl (const tl::Exception &ex, QWidget *parent) static void ui_exception_handler_std (const std::exception &ex, QWidget *parent) { + // Prevents severe side effects if there are pending deferred methods + tl::NoDeferredMethods silent; + // if any transaction is pending (this may happen when an operation threw an exception) // close transactions. if (lay::MainWindow::instance () && lay::MainWindow::instance ()->manager ().transacting ()) { @@ -138,6 +144,9 @@ static void ui_exception_handler_std (const std::exception &ex, QWidget *parent) static void ui_exception_handler_def (QWidget *parent) { + // Prevents severe side effects if there are pending deferred methods + tl::NoDeferredMethods silent; + // if any transaction is pending (this may happen when an operation threw an exception) // close transactions. if (lay::MainWindow::instance () && lay::MainWindow::instance ()->manager ().transacting ()) { @@ -597,10 +606,9 @@ Application::Application (int &argc, char **argv, bool non_ui_mode) if (sc) { - // auto-import technologies + // auto-import salt grains for (std::vector ::const_iterator p = m_klayout_path.begin (); p != m_klayout_path.end (); ++p) { - std::string tp = tl::to_string (QDir (tl::to_qstring (*p)).filePath (QString::fromUtf8 ("salt"))); - sc->add_path (tp); + sc->add_path (*p); } sc->set_salt_mine_url (tl::salt_mine_url ()); @@ -611,8 +619,7 @@ Application::Application (int &argc, char **argv, bool non_ui_mode) // auto-import technologies for (std::vector ::const_iterator p = m_klayout_path.begin (); p != m_klayout_path.end (); ++p) { - std::string tp = tl::to_string (QDir (tl::to_qstring (*p)).filePath (QString::fromUtf8 ("tech"))); - tc->add_path (tp); + tc->add_path (*p); } // import technologies from the command line diff --git a/src/lay/layMacroController.cc b/src/lay/layMacroController.cc index 12c44901f..1b3e3f9f1 100644 --- a/src/lay/layMacroController.cc +++ b/src/lay/layMacroController.cc @@ -429,14 +429,19 @@ MacroController::sync_implicit_macros (bool check_autorun) if (check_autorun) { + // This prevents the message dialog below to issue deferred methods + tl::NoDeferredMethods silent; + bool has_autorun = false; for (std::vector::const_iterator m = new_folders.begin (); m != new_folders.end () && ! has_autorun; ++m) { has_autorun = (*m)->has_autorun (); } - if (has_autorun && QMessageBox::question (mp_mw, QObject::tr ("Run Macros"), QObject::tr ("Some macros associated with new items are configured to run automatically.\n\nChoose 'Yes' to run these macros now. Choose 'No' to not run them."), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - for (std::vector::const_iterator m = new_folders.begin (); m != new_folders.end (); ++m) { - (*m)->autorun (); + if (has_autorun) { + if (QMessageBox::question (mp_mw, QObject::tr ("Run Macros"), QObject::tr ("Some macros associated with new items are configured to run automatically.\n\nChoose 'Yes' to run these macros now. Choose 'No' to not run them."), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + for (std::vector::const_iterator m = new_folders.begin (); m != new_folders.end (); ++m) { + (*m)->autorun (); + } } } diff --git a/src/lay/laySaltController.cc b/src/lay/laySaltController.cc index e0330bd5d..866e0b00e 100644 --- a/src/lay/laySaltController.cc +++ b/src/lay/laySaltController.cc @@ -27,6 +27,8 @@ #include "layQtTools.h" #include "tlLog.h" +#include + namespace lay { @@ -141,7 +143,11 @@ SaltController::show_editor () } } + // while running the dialog, don't watch file events - that would interfere with + // the changes applied by the dialog itself. + m_file_watcher->enable (false); mp_salt_dialog->exec (); + m_file_watcher->enable (true); if (mp_mw) { mp_mw->config_set (cfg_salt_manager_window_state, lay::save_dialog_state (mp_salt_dialog)); @@ -174,9 +180,14 @@ 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); + + std::string tp = tl::to_string (QDir (tl::to_qstring (path)).filePath (QString::fromUtf8 ("salt"))); + + tl::log << tl::to_string (tr ("Scanning %1 for packages").arg (tl::to_qstring (tp))); + m_salt.add_location (tp); + dm_sync_file_watcher (); + } catch (tl::Exception &ex) { tl::error << ex.msg (); } diff --git a/src/lay/layTechSetupDialog.cc b/src/lay/layTechSetupDialog.cc index 996f69a76..405274266 100644 --- a/src/lay/layTechSetupDialog.cc +++ b/src/lay/layTechSetupDialog.cc @@ -465,7 +465,7 @@ TechMacrosPage::commit () static bool s_first_show = true; TechSetupDialog::TechSetupDialog (QWidget *parent) - : QDialog (parent), mp_current_tech (0), mp_current_editor (0), mp_current_tech_component (0) + : QDialog (parent), mp_current_tech (0), mp_current_editor (0), mp_current_tech_component (0), m_current_tech_changed_enabled (true) { setObjectName (QString::fromUtf8 ("tech_setup_dialog")); @@ -532,12 +532,60 @@ TechSetupDialog::clear_components () void TechSetupDialog::refresh_clicked () { + m_current_tech_changed_enabled = false; + BEGIN_PROTECTED + commit_tech_component (); + update_tech (0); + + std::string tech_name; + if (selected_tech ()) { + tech_name = selected_tech ()->name (); + } + + // Save the expanded state of the items + std::set expanded_techs; + for (int i = 0; i < tech_tree->topLevelItemCount (); ++i) { + QTreeWidgetItem *item = tech_tree->topLevelItem (i); + if (item && item->isExpanded ()) { + QVariant d = item->data (0, Qt::UserRole); + if (d != QVariant ()) { + expanded_techs.insert (tl::to_string (d.toString ())); + } + } + } + lay::TechnologyController::instance ()->rescan (m_technologies); - update (); + + update_tech_tree (); + + QTreeWidgetItem *new_item = 0; + + for (int i = 0; i < tech_tree->topLevelItemCount () && !new_item; ++i) { + QTreeWidgetItem *item = tech_tree->topLevelItem (i); + QVariant d = item->data (0, Qt::UserRole); + if (d != QVariant () && tech_name == tl::to_string (d.toString ())) { + new_item = item; + } + } + + tech_tree->setCurrentItem (new_item); + + // restore the expanded state + for (int i = 0; i < tech_tree->topLevelItemCount (); ++i) { + QTreeWidgetItem *item = tech_tree->topLevelItem (i); + QVariant d = item->data (0, Qt::UserRole); + bool expand = (d != QVariant () && expanded_techs.find (tl::to_string (d.toString ())) != expanded_techs.end ()); + item->setExpanded (expand); + } + + update_tech (selected_tech ()); + update_tech_component (); END_PROTECTED + + m_current_tech_changed_enabled = true; } void @@ -971,6 +1019,10 @@ END_PROTECTED void TechSetupDialog::current_tech_changed (QTreeWidgetItem *current, QTreeWidgetItem *previous) { + if (! m_current_tech_changed_enabled) { + return; + } + BEGIN_PROTECTED try { if (current) { diff --git a/src/lay/layTechSetupDialog.h b/src/lay/layTechSetupDialog.h index 0c65840ae..c4c503899 100644 --- a/src/lay/layTechSetupDialog.h +++ b/src/lay/layTechSetupDialog.h @@ -157,6 +157,7 @@ private: std::map m_technology_components; lay::TechnologyComponentEditor *mp_current_editor; lay::TechnologyComponent *mp_current_tech_component; + bool m_current_tech_changed_enabled; }; class LAY_PUBLIC TechComponentSetupDialog diff --git a/src/lay/layTechnologyController.cc b/src/lay/layTechnologyController.cc index 6178f0944..51f7228b6 100644 --- a/src/lay/layTechnologyController.cc +++ b/src/lay/layTechnologyController.cc @@ -25,6 +25,7 @@ #include "layTechSetupDialog.h" #include "layMainWindow.h" #include "layApplication.h" +#include "laySaltController.h" #include "layConfig.h" #include "layQtTools.h" #include "laybasicConfig.h" @@ -82,6 +83,10 @@ TechnologyController::initialized (lay::PluginRoot * /*root*/) { update_menu (); connect_events (); + + if (lay::SaltController::instance ()) { + connect (lay::SaltController::instance (), SIGNAL (salt_changed ()), this, SLOT (sync_with_external_sources ())); + } } void @@ -89,6 +94,10 @@ TechnologyController::uninitialize (lay::PluginRoot * /*root*/) { m_tech_actions.clear (); tl::Object::detach_from_all_events (); + + if (lay::SaltController::instance ()) { + disconnect (lay::SaltController::instance (), SIGNAL (salt_changed ()), this, SLOT (sync_with_external_sources ())); + } } void @@ -466,6 +475,12 @@ TechnologyController::load () rescan (*lay::Technologies::instance ()); } +void +TechnologyController::sync_with_external_sources () +{ + rescan (*lay::Technologies::instance ()); +} + void TechnologyController::rescan (lay::Technologies &technologies) { @@ -479,7 +494,17 @@ TechnologyController::rescan (lay::Technologies &technologies) } } - for (std::vector::const_iterator p = m_paths.begin (); p != m_paths.end (); ++p) { + std::vector paths = m_paths; + + // add the salt grains as potential sources for tech definitions + lay::SaltController *sc = lay::SaltController::instance (); + if (sc) { + for (lay::Salt::flat_iterator g = sc->salt ().begin_flat (); g != sc->salt ().end_flat (); ++g) { + paths.push_back ((*g)->path ()); + } + } + + for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { QDir dir (tl::to_qstring (*p)); if (! dir.exists ()) { @@ -540,7 +565,8 @@ TechnologyController::add_temp_tech (const lay::Technology &t) void TechnologyController::add_path (const std::string &p) { - m_paths.push_back (p); + std::string tp = tl::to_string (QDir (tl::to_qstring (p)).filePath (QString::fromUtf8 ("tech"))); + m_paths.push_back (tp); } static tl::RegisteredClass config_decl (new TechnologyController (), 110, "TechnologyController"); diff --git a/src/lay/layTechnologyController.h b/src/lay/layTechnologyController.h index 69fd5da90..1845f08f1 100644 --- a/src/lay/layTechnologyController.h +++ b/src/lay/layTechnologyController.h @@ -119,6 +119,12 @@ signals: */ void technologies_edited (); +private slots: + /** + * @brief Called when the salt got changed + */ + void sync_with_external_sources (); + private: tl::stable_vector m_tech_actions; std::string m_current_technology; diff --git a/src/tl/tlDeferredExecution.h b/src/tl/tlDeferredExecution.h index 2a8c9537f..7e1136cee 100644 --- a/src/tl/tlDeferredExecution.h +++ b/src/tl/tlDeferredExecution.h @@ -140,6 +140,40 @@ private: void do_execute (); }; +/** + * @brief A protected region that ensures that deferred methods are not executed + * + * This class employs the RAII pattern to block a region of code for execution of + * deferred methods. This is useful to protect message boxes against having a side + * effects of issuing deferred method calls: + * + * @code + * { + * tl::NoDeferredMethods block; + * QMessageBox::warning (...); + * } + * @endcode + */ +class TL_PUBLIC NoDeferredMethods +{ +public: + /** + * @brief Constructor + */ + NoDeferredMethods () + { + DeferredMethodScheduler::enable (false); + } + + /** + * @brief Destructor + */ + ~NoDeferredMethods () + { + DeferredMethodScheduler::enable (true); + } +}; + /** * @brief Deferred execution of a const method *