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.
This commit is contained in:
Matthias Koefferlein 2017-04-18 23:58:24 +02:00
parent 4460819a6c
commit d5bf24666f
8 changed files with 156 additions and 14 deletions

View File

@ -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 <std::string>::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 <std::string>::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

View File

@ -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<lay::MacroCollection *>::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<lay::MacroCollection *>::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<lay::MacroCollection *>::const_iterator m = new_folders.begin (); m != new_folders.end (); ++m) {
(*m)->autorun ();
}
}
}

View File

@ -27,6 +27,8 @@
#include "layQtTools.h"
#include "tlLog.h"
#include <QDir>
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 ();
}

View File

@ -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<std::string> 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) {

View File

@ -157,6 +157,7 @@ private:
std::map <std::string, lay::TechnologyComponent *> m_technology_components;
lay::TechnologyComponentEditor *mp_current_editor;
lay::TechnologyComponent *mp_current_tech_component;
bool m_current_tech_changed_enabled;
};
class LAY_PUBLIC TechComponentSetupDialog

View File

@ -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<std::string>::const_iterator p = m_paths.begin (); p != m_paths.end (); ++p) {
std::vector<std::string> 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<std::string>::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<lay::PluginDeclaration> config_decl (new TechnologyController (), 110, "TechnologyController");

View File

@ -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 <lay::Action> m_tech_actions;
std::string m_current_technology;

View File

@ -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
*