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 *