WIP: technologies are file based by default now

* Introduced "refresh" method of tech setup dialog
* Some refactoring -> tech management is part of
  tech controller
* Macro category management moved to macro controller
This commit is contained in:
Matthias Koefferlein 2017-04-15 01:03:24 +02:00
parent 83b2c150d9
commit 5b422440a1
11 changed files with 603 additions and 249 deletions

View File

@ -34,6 +34,7 @@
#include "layTextProgress.h"
#include "layBackgroundAwareTreeStyle.h"
#include "layMacroController.h"
#include "layTechnologyController.h"
#include "gtf.h"
#include "gsiDecl.h"
#include "gsiInterpreter.h"
@ -55,7 +56,6 @@
#include <QIcon>
#include <QDir>
#include <QDirIterator>
#include <QFileInfo>
#include <QFile>
#include <QAction>
@ -895,41 +895,26 @@ Application::Application (int &argc, char **argv, bool non_ui_mode)
}
if (! m_no_macros) {
// Add the global ruby modules as the first ones.
m_load_macros.insert (m_load_macros.begin (), global_modules.begin (), global_modules.end ());
}
// Scan built-in macros
// These macros are always taken, even if there are no macros requested (they are required to
// fully form the API).
lay::MacroCollection::root ().add_folder (tl::to_string (QObject::tr ("Built-In")), ":/built-in-macros", "macros", true);
lay::MacroCollection::root ().add_folder (tl::to_string (QObject::tr ("Built-In")), ":/built-in-pymacros", "pymacros", true);
m_macro_categories.push_back (std::pair<std::string, std::string> ("macros", tl::to_string (QObject::tr ("Ruby"))));
m_macro_categories.push_back (std::pair<std::string, std::string> ("pymacros", tl::to_string (QObject::tr ("Python"))));
m_macro_categories.push_back (std::pair<std::string, std::string> ("drc", tl::to_string (QObject::tr ("DRC"))));
// Scan for macros and set interpreter path
for (std::vector <std::string>::const_iterator p = m_klayout_path.begin (); p != m_klayout_path.end (); ++p) {
for (size_t c = 0; c < m_macro_categories.size (); ++c) {
std::string mp = tl::to_string (QDir (tl::to_qstring (*p)).filePath (tl::to_qstring (m_macro_categories [c].first)));
// don't scan if macros are disabled
if (! m_no_macros) {
lay::MacroController *mc = lay::MacroController::instance ();
if (mc) {
for (std::vector <std::string>::const_iterator p = m_klayout_path.begin (); p != m_klayout_path.end (); ++p) {
if (p == m_klayout_path.begin ()) {
lay::MacroCollection::root ().add_folder (tl::to_string (QObject::tr ("Local")), mp, m_macro_categories [c].first, false);
mc->add_path (*p, tl::to_string (QObject::tr ("Local")), std::string (), false);
} else if (m_klayout_path.size () == 2) {
lay::MacroCollection::root ().add_folder (tl::to_string (QObject::tr ("Global")), mp, m_macro_categories [c].first, true);
mc->add_path (*p, tl::to_string (QObject::tr ("Global")), std::string (), true);
} else {
lay::MacroCollection::root ().add_folder (tl::to_string (QObject::tr ("Global")) + " - " + *p, mp, m_macro_categories [c].first, true);
mc->add_path (*p, tl::to_string (QObject::tr ("Global")) + " - " + *p, std::string (), true);
}
}
}
ruby_interpreter ().add_path (mp);
python_interpreter ().add_path (mp);
// Install the custom folders
for (std::vector <std::pair<std::string, std::string> >::const_iterator p = custom_macro_paths.begin (); p != custom_macro_paths.end (); ++p) {
mc->add_path (p->first, tl::to_string (QObject::tr ("Project")) + " - " + p->first, p->second, false);
}
}
@ -979,81 +964,43 @@ 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) {
lay::TechnologyController *tc = lay::TechnologyController::instance ();
QDir inst_path_dir (tl::to_qstring (*p));
if (! inst_path_dir.cd (QString::fromUtf8 ("tech"))) {
continue;
if (tc) {
tc->enable_macros (! m_no_macros);
// 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);
}
QStringList name_filters;
name_filters << QString::fromUtf8 ("*.lyt");
// import technologies from the command line
for (std::vector <std::pair<file_type, std::pair<std::string, std::string> > >::iterator f = m_files.begin (); f != m_files.end (); ++f) {
QStringList lyt_files;
QDirIterator di (inst_path_dir.path (), name_filters, QDir::Files, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
while (di.hasNext ()) {
lyt_files << di.next ();
}
lyt_files.sort ();
for (QStringList::const_iterator lf = lyt_files.begin (); lf != lyt_files.end (); ++lf) {
try {
if (f->first == layout_file_with_tech_file) {
if (tl::verbosity () >= 20) {
tl::info << "Auto-importing technology from " << tl::to_string (*lf);
tl::info << "Importing technology from " << f->second.second;
}
lay::Technology t;
t.load (tl::to_string (*lf));
t.set_persisted (false); // don't save that one in the configuration
lay::Technologies::instance ()->add (new lay::Technology (t));
t.load (f->second.second);
tc->add_temp_tech (t);
f->first = layout_file_with_tech;
f->second.second = t.name ();
} catch (tl::Exception &ex) {
tl::warn << tl::to_string (QObject::tr ("Unable to auto-import technology file ")) << tl::to_string (*lf) << ": " << ex.msg ();
}
}
}
// import technologies from the command line
for (std::vector <std::pair<file_type, std::pair<std::string, std::string> > >::iterator f = m_files.begin (); f != m_files.end (); ++f) {
if (f->first == layout_file_with_tech_file) {
if (tl::verbosity () >= 20) {
tl::info << "Importing technology from " << f->second.second;
}
lay::Technology t;
t.load (f->second.second);
t.set_persisted (false); // don't save that one in the configuration
lay::Technologies::instance ()->add (new lay::Technology (t));
f->first = layout_file_with_tech;
f->second.second = t.name ();
}
tc->refresh ();
}
// Install the custom folders
if (! m_no_macros) {
for (std::vector <std::pair<std::string, std::string> >::const_iterator p = custom_macro_paths.begin (); p != custom_macro_paths.end (); ++p) {
lay::MacroCollection::root ().add_folder (tl::to_string (QObject::tr ("Project")) + " - " + p->first, p->first, p->second, false);
// TODO: put somewhere else:
ruby_interpreter ().add_path (p->first);
python_interpreter ().add_path (p->first);
}
}
// Add locations defined by the technologies
sync_tech_macro_locations ();
// If the editable flag was not set, use it from the
// configuration. Since it is too early now, we cannot use the
// configuration once it is read
@ -1722,120 +1669,5 @@ Application::special_app_flag (const std::string &name)
return (env && *env);
}
std::vector<lay::MacroCollection *>
Application::sync_tech_macro_locations ()
{
if (m_no_macros) {
return std::vector<lay::MacroCollection *> ();
}
std::set<std::pair<std::string, std::string> > tech_macro_paths;
std::map<std::pair<std::string, std::string>, std::string> tech_names_by_path;
// Add additional places where the technologies define some macros
for (lay::Technologies::const_iterator t = lay::Technologies::instance ()->begin (); t != lay::Technologies::instance ()->end (); ++t) {
if (t->base_path ().empty ()) {
continue;
}
for (size_t c = 0; c < m_macro_categories.size (); ++c) {
QDir base_dir (tl::to_qstring (t->base_path ()));
if (base_dir.exists ()) {
QDir macro_dir (base_dir.filePath (tl::to_qstring (m_macro_categories [c].first)));
if (macro_dir.exists ()) {
std::string mp = tl::to_string (macro_dir.path ());
std::pair<std::string, std::string> cp (m_macro_categories [c].first, mp);
tech_macro_paths.insert (cp);
std::string &tn = tech_names_by_path [cp];
if (! tn.empty ()) {
tn += ",";
}
tn += t->name ();
}
}
}
}
// delete macro collections which are no longer required or update description
std::vector<lay::MacroCollection *> folders_to_delete;
std::string desc_prefix = tl::to_string (QObject::tr ("Technology")) + " - ";
lay::MacroCollection *root = &lay::MacroCollection::root ();
for (lay::MacroCollection::child_iterator m = root->begin_children (); m != root->end_children (); ++m) {
std::pair<std::string, std::string> cp (m->second->category (), m->second->path ());
if (m->second->virtual_mode () == lay::MacroCollection::TechFolder && m_tech_macro_paths.find (cp) != m_tech_macro_paths.end ()) {
if (tech_macro_paths.find (cp) == tech_macro_paths.end ()) {
// no longer used
folders_to_delete.push_back (m->second);
} else {
// used: update description if required
std::string desc = desc_prefix + tech_names_by_path [cp];
m->second->set_description (desc);
}
}
}
for (std::vector<lay::MacroCollection *>::iterator m = folders_to_delete.begin (); m != folders_to_delete.end (); ++m) {
if (tl::verbosity () >= 20) {
tl::info << "Removing macro folder " << (*m)->path () << ", category '" << (*m)->category () << "' because no longer in use";
}
root->erase (*m);
}
// store new paths
m_tech_macro_paths = tech_macro_paths;
// add new folders
for (lay::MacroCollection::child_iterator m = root->begin_children (); m != root->end_children (); ++m) {
if (m->second->virtual_mode () == lay::MacroCollection::TechFolder) {
std::pair<std::string, std::string> cp (m->second->category (), m->second->path ());
tech_macro_paths.erase (cp);
}
}
std::vector<lay::MacroCollection *> new_folders;
for (std::set<std::pair<std::string, std::string> >::const_iterator p = tech_macro_paths.begin (); p != tech_macro_paths.end (); ++p) {
const std::string &tn = tech_names_by_path [*p];
// TODO: is it wise to make it writeable?
if (tl::verbosity () >= 20) {
tl::info << "Adding macro folder " << p->second << ", category '" << p->first << "' for technologies " << tn;
}
// Add the folder. Note: it may happen that a macro folder for the tech specific macros already exists in
// a non-tech context.
// In that case, the add_folder method will return 0.
lay::MacroCollection *mc = lay::MacroCollection::root ().add_folder (desc_prefix + tn, p->second, p->first, false);
if (mc) {
mc->set_virtual_mode (lay::MacroCollection::TechFolder);
new_folders.push_back (mc);
// TODO: put somewhere else:
ruby_interpreter ().add_path (p->second);
python_interpreter ().add_path (p->second);
}
}
return new_folders;
}
}

View File

@ -202,14 +202,6 @@ public:
*/
bool special_app_flag (const std::string &name);
/**
* @brief Obtain the list of macro categories
*/
const std::vector< std::pair<std::string, std::string> > &macro_categories () const
{
return m_macro_categories;
}
/**
* @brief Return a reference to the Ruby interpreter
*/

View File

@ -44,6 +44,38 @@ MacroController::MacroController ()
connect (&m_temp_macros, SIGNAL (macro_collection_changed (MacroCollection *)), this, SLOT (update_menu_with_macros ()));
}
void
MacroController::initialize (lay::PluginRoot * /*root*/)
{
// Scan built-in macros
// These macros are always taken, even if there are no macros requested (they are required to
// fully form the API).
lay::MacroCollection::root ().add_folder (tl::to_string (QObject::tr ("Built-In")), ":/built-in-macros", "macros", true);
lay::MacroCollection::root ().add_folder (tl::to_string (QObject::tr ("Built-In")), ":/built-in-pymacros", "pymacros", true);
// TODO: consider adding "drc" dynamically and allow more dynamic categories
m_macro_categories.push_back (std::pair<std::string, std::string> ("macros", tl::to_string (QObject::tr ("Ruby"))));
m_macro_categories.push_back (std::pair<std::string, std::string> ("pymacros", tl::to_string (QObject::tr ("Python"))));
m_macro_categories.push_back (std::pair<std::string, std::string> ("drc", tl::to_string (QObject::tr ("DRC"))));
// Scan for macros and set interpreter path
for (std::vector <std::pair<std::string, std::pair<std::string, std::pair<std::string, bool> > > >::const_iterator p = m_paths.begin (); p != m_paths.end (); ++p) {
std::string path = p->first;
std::string description = p->second.first;
std::string cat = p->second.second.first;
bool readonly = p->second.second.second;
for (size_t c = 0; c < m_macro_categories.size (); ++c) {
if (cat.empty () || cat == m_macro_categories [c].first) {
std::string mp = tl::to_string (QDir (tl::to_qstring (p->first)).filePath (tl::to_qstring (m_macro_categories [c].first)));
lay::MacroCollection::root ().add_folder (description, mp, m_macro_categories [c].first, readonly);
}
}
}
}
void
MacroController::initialized (lay::PluginRoot *root)
{
@ -236,6 +268,12 @@ MacroController::refresh ()
}
}
void
MacroController::add_path (const std::string &path, const std::string &description, const std::string &category, bool readonly)
{
m_paths.push_back (std::make_pair (path, std::make_pair (description, std::make_pair (category, readonly))));
}
void
MacroController::add_temp_macro (lay::Macro *m)
{
@ -252,7 +290,7 @@ MacroController::add_macro_items_to_menu (lay::MacroCollection &collection, int
if (! tech || c->second->virtual_mode () != lay::MacroCollection::TechFolder) {
consider = true;
} else {
const std::vector<std::pair<std::string, std::string> > &mc = lay::Application::instance ()->macro_categories ();
const std::vector<std::pair<std::string, std::string> > &mc = macro_categories ();
for (std::vector<std::pair<std::string, std::string> >::const_iterator cc = mc.begin (); cc != mc.end () && !consider; ++cc) {
consider = (c->second->path () == tl::to_string (QDir (tl::to_qstring (tech->base_path ())).filePath (tl::to_qstring (cc->first))));
}

View File

@ -68,6 +68,11 @@ public:
*/
MacroController ();
/**
* @brief Reimplementation of the PluginDeclaration interface
*/
virtual void initialize (lay::PluginRoot *root);
/**
* @brief Reimplementation of the PluginDeclaration interface
*/
@ -117,6 +122,11 @@ public:
*/
void refresh ();
/**
* @brief Adds a search path to the macros
*/
void add_path (const std::string &path, const std::string &description, const std::string &category, bool readonly);
/**
* @brief Adds a temporary macro
*
@ -128,6 +138,14 @@ public:
*/
void add_temp_macro (lay::Macro *m);
/**
* @brief Obtain the list of macro categories
*/
const std::vector< std::pair<std::string, std::string> > &macro_categories () const
{
return m_macro_categories;
}
/**
* @brief Gets the singleton instance for this object
*/
@ -146,6 +164,8 @@ private:
std::vector<lay::Action> m_macro_actions;
std::map<QAction *, lay::Macro *> m_action_to_macro;
lay::MacroCollection m_temp_macros;
std::vector< std::pair<std::string, std::pair<std::string, std::pair<std::string, bool> > > > m_paths;
std::vector< std::pair<std::string, std::string> > m_macro_categories;
void add_macro_items_to_menu (lay::MacroCollection &collection, int &n, std::set<std::string> &groups, const lay::Technology *tech, std::vector<std::pair<std::string, std::string> > *key_bindings);
void do_update_menu_with_macros ();

View File

@ -22,6 +22,7 @@
#include "ui_MacroTemplateSelectionDialog.h"
#include "layMacroController.h"
#include "layMacroEditorTree.h"
#include "layMacroEditorDialog.h"
#include "layMacroEditorSetupDialog.h"
@ -263,7 +264,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 *)));
m_categories = lay::Application::instance ()->macro_categories ();
m_categories = lay::MacroController::instance ()->macro_categories ();
treeTab->clear ();

View File

@ -31,6 +31,8 @@
#include "layApplication.h"
#include "layMacroEditorTree.h"
#include "layMacro.h"
#include "layMacroController.h"
#include "layTechnologyController.h"
#include "tlAssert.h"
#include "tlStream.h"
#include "dbStream.h"
@ -479,15 +481,23 @@ TechSetupDialog::TechSetupDialog (QWidget *parent)
connect (import_action, SIGNAL (triggered ()), this, SLOT (import_clicked ()));
QAction *export_action = new QAction (QObject::tr ("Export Technology"), this);
connect (export_action, SIGNAL (triggered ()), this, SLOT (export_clicked ()));
QAction *refresh_action = new QAction (QObject::tr ("Refresh"), this);
connect (refresh_action, SIGNAL (triggered ()), this, SLOT (refresh_clicked ()));
QAction *separator;
tech_tree->addAction (add_action);
tech_tree->addAction (delete_action);
tech_tree->addAction (rename_action);
QAction *separator = new QAction (this);
separator = new QAction (this);
separator->setSeparator (true);
tech_tree->addAction (separator);
tech_tree->addAction (import_action);
tech_tree->addAction (export_action);
separator = new QAction (this);
separator->setSeparator (true);
tech_tree->addAction (separator);
tech_tree->addAction (refresh_action);
tech_tree->header ()->hide ();
connect (tech_tree, SIGNAL (currentItemChanged (QTreeWidgetItem *, QTreeWidgetItem *)), this, SLOT (current_tech_changed (QTreeWidgetItem *, QTreeWidgetItem *)));
@ -527,23 +537,87 @@ TechSetupDialog::clear_components ()
mp_current_tech_component = 0;
}
int
TechSetupDialog::exec ()
void
TechSetupDialog::refresh_clicked ()
{
BEGIN_PROTECTED
commit ();
lay::TechnologyController::instance ()->refresh ();
update ();
END_PROTECTED
}
void
TechSetupDialog::update ()
{
m_technologies = *lay::Technologies ().instance ();
update_tech_tree ();
tech_tree->setCurrentItem (tech_tree->topLevelItem (0));
update_tech (selected_tech ());
}
bool
TechSetupDialog::commit ()
{
std::string err_msg;
// determine the technology files that need to be deleted and delete them
std::set<std::string> files_before;
for (lay::Technologies::const_iterator t = m_technologies.begin (); t != m_technologies.end (); ++t) {
if (! ! t->tech_file_path ().empty () && ! t->is_persisted ()) {
files_before.insert (t->tech_file_path ());
}
}
for (lay::Technologies::const_iterator t = lay::Technologies::instance ()->begin (); t != lay::Technologies::instance ()->end (); ++t) {
if (! t->tech_file_path ().empty () && ! t->is_persisted () && files_before.find (t->tech_file_path ()) == files_before.end ()) {
// TODO: issue an error if files could not be removed
QFile (tl::to_qstring (t->tech_file_path ())).remove ();
}
}
*lay::Technologies ().instance () = m_technologies;
// save the technologies that need to be saved
// TODO: save only the ones that really need saving
for (lay::Technologies::const_iterator t = lay::Technologies::instance ()->begin (); t != lay::Technologies::instance ()->end (); ++t) {
if (! t->tech_file_path ().empty () && ! t->is_persisted ()) {
try {
t->save (t->tech_file_path ());
} catch (...) {
if (! err_msg.empty ()) {
err_msg += "\n";
}
err_msg += t->tech_file_path ();
}
}
}
if (! err_msg.empty ()) {
QMessageBox::critical (this, QObject::tr ("Error Saving Technology Files"),
QObject::tr ("The following files could not be saved:\n\n") + tl::to_qstring (err_msg),
QMessageBox::Ok);
return false;
} else {
return true;
}
}
int
TechSetupDialog::exec ()
{
update ();
tc_stack->setMinimumSize (tc_stack->sizeHint ());
int ret = QDialog::exec ();
if (ret) {
*lay::Technologies ().instance () = m_technologies;
commit ();
}
// clean up
update_tech (0);
clear_components ();
m_technologies = lay::Technologies ();
update_tech_tree ();
@ -583,7 +657,19 @@ BEGIN_PROTECTED
throw tl::Exception (tl::to_string (QObject::tr ("A technology with this name already exists")));
}
QDir root = QDir (tl::to_qstring (lay::TechnologyController::instance ()->default_root ()));
QDir tech_dir (root.filePath (tn));
if (tech_dir.exists ()) {
throw tl::Exception (tl::to_string (QObject::tr ("A target folder with path '%1' already exists").arg (tech_dir.path ())));
}
if (! root.mkdir (tn)) {
throw tl::Exception (tl::to_string (QObject::tr ("Unable to create target folder '%1'").arg (tech_dir.path ())));
}
lay::Technology *nt = new lay::Technology (*t);
nt->set_tech_file_path (tl::to_string (tech_dir.absoluteFilePath (tn + QString::fromUtf8 (".lyt"))));
nt->set_persisted (false);
nt->set_name (tl::to_string (tn));
nt->set_description (std::string ());
m_technologies.add (nt);
@ -610,8 +696,8 @@ BEGIN_PROTECTED
throw tl::Exception (tl::to_string (QObject::tr ("The default technology cannot be deleted")));
}
if (! t->is_persisted ()) {
throw tl::Exception (tl::to_string (QObject::tr ("Auto-imported technologies cannot be deleted")));
if (t->is_readonly ()) {
throw tl::Exception (tl::to_string (QObject::tr ("This technology is read-only and cannot be deleted")));
}
if (QMessageBox::question (this, QObject::tr ("Deleting Technology"),
@ -623,6 +709,7 @@ BEGIN_PROTECTED
if (i->name () == t->name ()) {
m_technologies.remove (i->name ());
update_tech_tree ();
select_tech (*m_technologies.technology_by_name (std::string ()));
@ -652,8 +739,8 @@ BEGIN_PROTECTED
throw tl::Exception (tl::to_string (QObject::tr ("The default technology cannot be renamed")));
}
if (! t->is_persisted ()) {
throw tl::Exception (tl::to_string (QObject::tr ("Auto-imported technologies cannot be renamed")));
if (t->is_readonly ()) {
throw tl::Exception (tl::to_string (QObject::tr ("This technology is read-only and cannot be renamed")));
}
bool ok = false;
@ -670,10 +757,21 @@ BEGIN_PROTECTED
throw tl::Exception (tl::to_string (QObject::tr ("A technology with this name already exists")));
}
t->set_name (tl::to_string (tn));
if (t->name () != tl::to_string (tn)) {
update_tech_tree ();
select_tech (*t);
t->set_name (tl::to_string (tn));
if (! t->is_persisted () && ! t->tech_file_path().empty ()) {
TipDialog td (this,
tl::to_string (QObject::tr ("<html><body>Renaming of a technology will neither rename the technology file or the folder the file is stored in.<br/>The file or folder needs to be renamed manually.</body></html>")),
"tech-manager-rename-tip");
td.exec_dialog ();
}
update_tech_tree ();
select_tech (*t);
}
}
@ -740,7 +838,7 @@ TechSetupDialog::update_tech_tree ()
for (std::map <std::string, const lay::Technology *>::const_iterator t = tech_by_name.begin (); t != tech_by_name.end (); ++t) {
QFont f (tech_tree->font ());
f.setItalic (! t->second->is_persisted ());
f.setItalic (t->second->is_readonly ());
std::string d;
d += t->first;
@ -753,6 +851,9 @@ TechSetupDialog::update_tech_tree ()
ti->setData (0, Qt::DisplayRole, QVariant (tl::to_qstring (d)));
ti->setData (0, Qt::UserRole, QVariant (tl::to_qstring (t->first)));
ti->setData (0, Qt::FontRole, QVariant (f));
if (! t->second->tech_file_path ().empty ()) {
ti->setData (0, Qt::ToolTipRole, QVariant (tl::to_qstring (t->second->tech_file_path ())));
}
std::vector <std::string> tc_names = t->second->component_names ();
std::map <std::string, const lay::TechnologyComponent *> tc_by_name;
@ -775,12 +876,14 @@ TechSetupDialog::update_tech_tree ()
tci->setData (0, Qt::UserRole + 1, QVariant (tl::to_qstring ("_save_options")));
tci->setData (0, Qt::FontRole, QVariant (f));
const std::vector<std::pair<std::string, std::string> > &mc = lay::Application::instance ()->macro_categories ();
for (std::vector<std::pair<std::string, std::string> >::const_iterator c = mc.begin (); c != mc.end (); ++c) {
tci = new QTreeWidgetItem (ti);
tci->setData (0, Qt::DisplayRole, QVariant (tl::to_qstring (c->second)));
tci->setData (0, Qt::UserRole + 1, QVariant (tl::to_qstring (std::string ("_macros_") + c->first)));
tci->setData (0, Qt::FontRole, QVariant (f));
if (lay::MacroController::instance ()) {
const std::vector<std::pair<std::string, std::string> > &mc = lay::MacroController::instance ()->macro_categories ();
for (std::vector<std::pair<std::string, std::string> >::const_iterator c = mc.begin (); c != mc.end (); ++c) {
tci = new QTreeWidgetItem (ti);
tci->setData (0, Qt::DisplayRole, QVariant (tl::to_qstring (c->second)));
tci->setData (0, Qt::UserRole + 1, QVariant (tl::to_qstring (std::string ("_macros_") + c->first)));
tci->setData (0, Qt::FontRole, QVariant (f));
}
}
for (std::map <std::string, const lay::TechnologyComponent *>::const_iterator c = tc_by_name.begin (); c != tc_by_name.end (); ++c) {
@ -791,7 +894,6 @@ TechSetupDialog::update_tech_tree ()
}
}
}
void
@ -808,28 +910,30 @@ TechSetupDialog::update_tech (lay::Technology *t)
if (t) {
lay::TechnologyComponentEditor *tce_widget = new TechBaseEditorPage (this);
tce_widget->setEnabled (t->is_persisted ());
tce_widget->setEnabled (!t->is_readonly ());
tce_widget->set_technology (t, 0);
tc_stack->addWidget (tce_widget);
m_component_editors.insert (std::make_pair (std::string ("_general"), tce_widget));
const std::vector<std::pair<std::string, std::string> > &mc = lay::Application::instance ()->macro_categories ();
for (std::vector<std::pair<std::string, std::string> >::const_iterator c = mc.begin (); c != mc.end (); ++c) {
tce_widget = new TechMacrosPage (this, c->first, c->second);
tce_widget->setEnabled (t->is_persisted ());
tce_widget->set_technology (t, 0);
tc_stack->addWidget (tce_widget);
m_component_editors.insert (std::make_pair (std::string ("_macros_") + c->first, tce_widget));
if (lay::MacroController::instance ()) {
const std::vector<std::pair<std::string, std::string> > &mc = lay::MacroController::instance ()->macro_categories ();
for (std::vector<std::pair<std::string, std::string> >::const_iterator c = mc.begin (); c != mc.end (); ++c) {
tce_widget = new TechMacrosPage (this, c->first, c->second);
tce_widget->setEnabled (!t->is_readonly ());
tce_widget->set_technology (t, 0);
tc_stack->addWidget (tce_widget);
m_component_editors.insert (std::make_pair (std::string ("_macros_") + c->first, tce_widget));
}
}
tce_widget = new TechLoadOptionsEditorPage (this);
tce_widget->setEnabled (t->is_persisted ());
tce_widget->setEnabled (!t->is_readonly ());
tce_widget->set_technology (t, 0);
tc_stack->addWidget (tce_widget);
m_component_editors.insert (std::make_pair (std::string ("_load_options"), tce_widget));
tce_widget = new TechSaveOptionsEditorPage (this);
tce_widget->setEnabled (t->is_persisted ());
tce_widget->setEnabled (!t->is_readonly ());
tce_widget->set_technology (t, 0);
tc_stack->addWidget (tce_widget);
m_component_editors.insert (std::make_pair (std::string ("_save_options"), tce_widget));
@ -842,7 +946,7 @@ TechSetupDialog::update_tech (lay::Technology *t)
tce_widget = tc->create_editor (this);
if (tce_widget) {
tce_widget->setEnabled (t->is_persisted ());
tce_widget->setEnabled (!t->is_readonly ());
tce_widget->set_technology (t, tc);
tc_stack->addWidget (tce_widget);
m_component_editors.insert (std::make_pair (tc->name (), tce_widget));
@ -936,7 +1040,7 @@ TechSetupDialog::commit_tech_component ()
mp_current_editor->commit ();
}
if (mp_current_tech && mp_current_tech_component && mp_current_tech->is_persisted ()) {
if (mp_current_tech && mp_current_tech_component && !mp_current_tech->is_readonly ()) {
mp_current_tech->set_component (mp_current_tech_component->clone ());

View File

@ -137,6 +137,7 @@ protected slots:
void rename_clicked ();
void import_clicked ();
void export_clicked ();
void refresh_clicked ();
private:
void update_tech_tree ();
@ -148,6 +149,8 @@ private:
std::string selected_tech_component_name ();
void commit_tech_component ();
void clear_components ();
bool commit ();
void update ();
lay::Technologies m_technologies;
lay::Technology *mp_current_tech;

View File

@ -29,6 +29,9 @@
#include "laybasicConfig.h"
#include <QMessageBox>
#include <QDir>
#include <QDirIterator>
#include <QFileInfo>
namespace lay
{
@ -43,11 +46,17 @@ std::string tech_string_from_name (const std::string &tn)
}
TechnologyController::TechnologyController ()
: PluginDeclaration (), mp_editor (0), mp_mw (0)
: PluginDeclaration (), mp_editor (0), mp_mw (0), m_no_macros (false)
{
m_current_technology_updated = false;
}
void
TechnologyController::enable_macros (bool enable)
{
m_no_macros = !enable;
}
TechnologyController *
TechnologyController::instance ()
{
@ -60,9 +69,17 @@ TechnologyController::instance ()
return 0;
}
void
TechnologyController::initialize (lay::PluginRoot * /*root*/)
{
// .. nothing yet ..
}
void
TechnologyController::initialized (lay::PluginRoot *root)
{
sync_tech_macro_locations ();
mp_mw = dynamic_cast <lay::MainWindow *> (root);
if (mp_mw) {
mp_editor = new lay::TechSetupDialog (mp_mw);
@ -272,7 +289,7 @@ TechnologyController::show_editor ()
{
if (mp_editor && mp_editor->exec ()) {
std::vector<lay::MacroCollection *> nm = lay::Application::instance ()->sync_tech_macro_locations ();
std::vector<lay::MacroCollection *> nm = sync_tech_macro_locations ();
bool has_autorun = false;
for (std::vector<lay::MacroCollection *>::const_iterator m = nm.begin (); m != nm.end () && ! has_autorun; ++m) {
@ -295,6 +312,204 @@ TechnologyController::show_editor ()
}
}
const std::string &
TechnologyController::default_root ()
{
tl_assert (!m_paths.empty ());
return m_paths.front ();
}
void
TechnologyController::refresh ()
{
try {
lay::Technologies::instance ()->begin_updates ();
lay::Technologies::instance ()->clear ();
for (std::vector<std::string>::const_iterator p = m_paths.begin (); p != m_paths.end (); ++p) {
QDir dir (tl::to_qstring (*p));
if (! dir.exists ()) {
continue;
}
QStringList name_filters;
name_filters << QString::fromUtf8 ("*.lyt");
QStringList lyt_files;
QDirIterator di (dir.path (), name_filters, QDir::Files, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
while (di.hasNext ()) {
lyt_files << di.next ();
}
lyt_files.sort ();
for (QStringList::const_iterator lf = lyt_files.begin (); lf != lyt_files.end (); ++lf) {
try {
if (tl::verbosity () >= 20) {
tl::info << "Auto-importing technology from " << tl::to_string (*lf);
}
lay::Technology t;
t.load (tl::to_string (*lf));
t.set_persisted (false); // don't save that one in the configuration
t.set_readonly (! QFileInfo (dir.filePath (*lf)).isWritable ());
lay::Technologies::instance ()->add (new lay::Technology (t));
} catch (tl::Exception &ex) {
tl::warn << tl::to_string (QObject::tr ("Unable to auto-import technology file ")) << tl::to_string (*lf) << ": " << ex.msg ();
}
}
}
for (std::vector<lay::Technology>::const_iterator t = m_temp_tech.begin (); t != m_temp_tech.end (); ++t) {
lay::Technology *tech = new lay::Technology (*t);
tech->set_persisted (false); // don't save that one in the configuration
tech->set_readonly (true);
lay::Technologies::instance ()->add (tech);
}
lay::Technologies::instance ()->end_updates ();
} catch (...) {
lay::Technologies::instance ()->end_updates ();
throw;
}
}
void
TechnologyController::add_temp_tech (const lay::Technology &t)
{
m_temp_tech.push_back (t);
}
void
TechnologyController::add_path (const std::string &p)
{
m_paths.push_back (p);
}
std::vector<lay::MacroCollection *>
TechnologyController::sync_tech_macro_locations ()
{
lay::MacroController *mc = lay::MacroController::instance ();
if (! mc || m_no_macros) {
return std::vector<lay::MacroCollection *> ();
}
std::set<std::pair<std::string, std::string> > tech_macro_paths;
std::map<std::pair<std::string, std::string>, std::string> tech_names_by_path;
// Add additional places where the technologies define some macros
for (lay::Technologies::const_iterator t = lay::Technologies::instance ()->begin (); t != lay::Technologies::instance ()->end (); ++t) {
if (t->base_path ().empty ()) {
continue;
}
for (size_t c = 0; c < mc->macro_categories ().size (); ++c) {
QDir base_dir (tl::to_qstring (t->base_path ()));
if (base_dir.exists ()) {
QDir macro_dir (base_dir.filePath (tl::to_qstring (mc->macro_categories () [c].first)));
if (macro_dir.exists ()) {
std::string mp = tl::to_string (macro_dir.path ());
std::pair<std::string, std::string> cp (mc->macro_categories () [c].first, mp);
tech_macro_paths.insert (cp);
std::string &tn = tech_names_by_path [cp];
if (! tn.empty ()) {
tn += ",";
}
tn += t->name ();
}
}
}
}
// delete macro collections which are no longer required or update description
std::vector<lay::MacroCollection *> folders_to_delete;
std::string desc_prefix = tl::to_string (QObject::tr ("Technology")) + " - ";
lay::MacroCollection *root = &lay::MacroCollection::root ();
for (lay::MacroCollection::child_iterator m = root->begin_children (); m != root->end_children (); ++m) {
std::pair<std::string, std::string> cp (m->second->category (), m->second->path ());
if (m->second->virtual_mode () == lay::MacroCollection::TechFolder && m_tech_macro_paths.find (cp) != m_tech_macro_paths.end ()) {
if (tech_macro_paths.find (cp) == tech_macro_paths.end ()) {
// no longer used
folders_to_delete.push_back (m->second);
} else {
// used: update description if required
std::string desc = desc_prefix + tech_names_by_path [cp];
m->second->set_description (desc);
}
}
}
for (std::vector<lay::MacroCollection *>::iterator m = folders_to_delete.begin (); m != folders_to_delete.end (); ++m) {
if (tl::verbosity () >= 20) {
tl::info << "Removing macro folder " << (*m)->path () << ", category '" << (*m)->category () << "' because no longer in use";
}
root->erase (*m);
}
// store new paths
m_tech_macro_paths = tech_macro_paths;
// add new folders
for (lay::MacroCollection::child_iterator m = root->begin_children (); m != root->end_children (); ++m) {
if (m->second->virtual_mode () == lay::MacroCollection::TechFolder) {
std::pair<std::string, std::string> cp (m->second->category (), m->second->path ());
tech_macro_paths.erase (cp);
}
}
std::vector<lay::MacroCollection *> new_folders;
for (std::set<std::pair<std::string, std::string> >::const_iterator p = tech_macro_paths.begin (); p != tech_macro_paths.end (); ++p) {
const std::string &tn = tech_names_by_path [*p];
// TODO: is it wise to make it writeable?
if (tl::verbosity () >= 20) {
tl::info << "Adding macro folder " << p->second << ", category '" << p->first << "' for technologies " << tn;
}
// Add the folder. Note: it may happen that a macro folder for the tech specific macros already exists in
// a non-tech context.
// In that case, the add_folder method will return 0.
lay::MacroCollection *mc = lay::MacroCollection::root ().add_folder (desc_prefix + tn, p->second, p->first, false);
if (mc) {
mc->set_virtual_mode (lay::MacroCollection::TechFolder);
new_folders.push_back (mc);
}
}
return new_folders;
}
static tl::RegisteredClass<lay::PluginDeclaration> config_decl (new TechnologyController (), 110, "TechnologyController");
}

View File

@ -29,11 +29,14 @@
#include "layTechnology.h"
#include "layAbstractMenu.h"
#include <vector>
namespace lay
{
class TechSetupDialog;
class MainWindow;
class MacroCollection;
/**
* @brief A "controller" for the technologies
@ -52,6 +55,7 @@ public:
*/
TechnologyController ();
void initialize (lay::PluginRoot *root);
void initialized (lay::PluginRoot *root);
void uninitialize (lay::PluginRoot *root);
@ -69,6 +73,41 @@ public:
return m_active_technology;
}
/**
* @brief Enables or disables macros
* If macros are enabled, the macro tree contains the macros defined within the technologies.
* This flag needs to be set initially and before the technology tree is updated.
*/
void enable_macros (bool enable);
/**
* @brief Adds a path as a search path for technologies
* "refresh" needs to be called after search paths have been added.
*/
void add_path (const std::string &path);
/**
* @brief Adds a temporary technology
* Temporary technologies are additional technologies which are added to the list of technologies
* but are not persisted or editable.
* "refresh" needs to be called after temp technologies have been added.
*/
void add_temp_tech (const lay::Technology &t);
/**
* @brief Updates the technology collection with the technologies from the search path and teh temp technologies
*/
void refresh ();
/**
* @brief Gets the default root folder
* The default root is the first one of the paths added with add_path.
*/
const std::string &default_root ();
/**
* @brief Gets the singleton instance of the controller
*/
static TechnologyController *instance ();
signals:
@ -92,6 +131,10 @@ private:
bool m_current_technology_updated;
lay::TechSetupDialog *mp_editor;
lay::MainWindow *mp_mw;
bool m_no_macros;
std::vector<std::string> m_paths;
std::vector<lay::Technology> m_temp_tech;
std::set<std::pair<std::string, std::string> > m_tech_macro_paths;
void update_after_change ();
void technologies_changed ();
@ -101,6 +144,7 @@ private:
bool menu_activated (const std::string &symbol) const;
void update_current_technology ();
void update_menu ();
std::vector<lay::MacroCollection *> sync_tech_macro_locations ();
};
}

View File

@ -38,6 +38,8 @@ namespace lay
Technologies::Technologies ()
{
m_technologies.push_back (new Technology (std::string (""), "(Default)"));
m_changed = false;
m_in_update = false;
}
Technologies::Technologies (const Technologies &other)
@ -128,7 +130,7 @@ Technologies::add (Technology *technology)
technology->technology_changed_with_sender_event.add (this, &Technologies::technology_changed);
}
technologies_changed_event ();
technologies_changed ();
}
void
@ -137,12 +139,52 @@ Technologies::remove (const std::string &name)
for (tl::stable_vector<Technology>::iterator t = m_technologies.begin (); t != m_technologies.end (); ++t) {
if (t->name () == name) {
m_technologies.erase (t);
technologies_changed_event ();
technologies_changed ();
break;
}
}
}
void
Technologies::clear ()
{
if (! m_technologies.empty ()) {
m_technologies.clear ();
technologies_changed ();
}
}
void
Technologies::technologies_changed ()
{
if (m_in_update) {
m_changed = true;
} else {
technologies_changed_event ();
}
}
void
Technologies::begin_updates ()
{
tl_assert (! m_in_update);
m_in_update = true;
m_changed = false;
}
void
Technologies::end_updates ()
{
if (m_in_update) {
m_in_update = false;
if (m_changed) {
m_changed = false;
technologies_changed ();
}
}
}
bool
Technologies::has_technology (const std::string &name) const
{
@ -171,13 +213,13 @@ Technologies::technology_by_name (const std::string &name)
// Technology implementation
Technology::Technology ()
: m_name (), m_description (), m_dbu (0.001), m_persisted (true)
: m_name (), m_description (), m_dbu (0.001), m_persisted (true), m_readonly (false)
{
init ();
}
Technology::Technology (const std::string &name, const std::string &description)
: m_name (name), m_description (description), m_dbu (0.001), m_persisted (true)
: m_name (name), m_description (description), m_dbu (0.001), m_persisted (true), m_readonly (false)
{
init ();
}
@ -350,7 +392,9 @@ Technology::load (const std::string &fn)
xml_struct.parse (source, *this);
// use the tech file's path as the default base path
set_default_base_path (tl::to_string (QFileInfo (tl::to_qstring (fn)).absoluteDir ().path ()));
std::string lyt_file = tl::to_string (QFileInfo (tl::to_qstring (fn)).absoluteDir ().path ());
set_default_base_path (lyt_file);
set_tech_file_path (lyt_file);
}
void

View File

@ -133,6 +133,22 @@ public:
*/
void remove (const std::string &name);
/**
* @brief Clears the list of technologies
*/
void clear ();
/**
* @brief Begins a bulk operation
* This method will disable "technologies_changed" events until (later) end_updates () is called.
*/
void begin_updates ();
/**
* @brief Ends a bulk operation
*/
void end_updates ();
/**
* @brief Checks, if a technology with the given name exists
*/
@ -191,8 +207,15 @@ protected:
technology_changed_event (t);
}
/**
* @brief Sends the technologies_changed event
*/
void technologies_changed ();
private:
tl::stable_vector<Technology> m_technologies;
bool m_changed;
bool m_in_update;
};
/**
@ -308,6 +331,23 @@ public:
}
}
/**
* @brief Gets the path of the tech file if the technology was loaded from a tech file
*/
const std::string &tech_file_path () const
{
return m_lyt_file;
}
/**
* @brief Sets the path of the tech file
* This method is intended for internal use only.
*/
void set_tech_file_path (const std::string &lyt_file)
{
m_lyt_file = lyt_file;
}
/**
* @brief Gets the description
*/
@ -497,6 +537,24 @@ public:
m_persisted = f;
}
/**
* @brief Returns a flag indicating whether the technology is readonly
*
* If the flag is false, the technology can be edited. Otherwise it's locked for editing.
*/
bool is_readonly () const
{
return m_readonly;
}
/**
* @brief Sets a flag indicating whether the technology is readonly
*/
void set_readonly (bool f)
{
m_readonly = f;
}
/**
* @brief An event indicating that the technology has changed
*/
@ -514,9 +572,12 @@ private:
db::LoadLayoutOptions m_load_layout_options;
db::SaveLayoutOptions m_save_layout_options;
std::string m_lyp_path;
std::string m_lyt_path;
bool m_add_other_layers;
std::vector <TechnologyComponent *> m_components;
bool m_persisted;
bool m_readonly;
std::string m_lyt_file;
void init ();