mirror of https://github.com/KLayout/klayout.git
977 lines
24 KiB
C++
977 lines
24 KiB
C++
|
|
/*
|
|
|
|
KLayout Layout Viewer
|
|
Copyright (C) 2006-2022 Matthias Koefferlein
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
|
#include "lymMacroCollection.h"
|
|
|
|
#include "lymMacroInterpreter.h"
|
|
#include "tlExceptions.h"
|
|
#include "gsiDecl.h"
|
|
#include "gsiInterpreter.h"
|
|
|
|
#include "tlString.h"
|
|
#include "tlStableVector.h"
|
|
#include "tlClassRegistry.h"
|
|
#include "tlLog.h"
|
|
#include "tlXMLParser.h"
|
|
#include "tlGlobPattern.h"
|
|
#include "tlInclude.h"
|
|
#include "tlProgress.h"
|
|
#include "tlFileUtils.h"
|
|
#include "tlResources.h"
|
|
|
|
#include "rba.h"
|
|
#include "pya.h"
|
|
|
|
#if defined(HAVE_QT)
|
|
# include <QResource>
|
|
#endif
|
|
|
|
#include <fstream>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <set>
|
|
|
|
namespace lym
|
|
{
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
static MacroCollection ms_root;
|
|
|
|
MacroCollection::MacroCollection ()
|
|
: mp_parent (0), m_virtual_mode (ProjectFolder), m_readonly (false)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
MacroCollection::~MacroCollection ()
|
|
{
|
|
do_clear ();
|
|
}
|
|
|
|
void MacroCollection::do_clear ()
|
|
{
|
|
for (iterator m = begin (); m != end (); ++m) {
|
|
delete m->second;
|
|
}
|
|
m_macros.clear ();
|
|
|
|
for (child_iterator mm = begin_children (); mm != end_children (); ++mm) {
|
|
delete mm->second;
|
|
}
|
|
m_folders.clear ();
|
|
}
|
|
|
|
void MacroCollection::begin_changes ()
|
|
{
|
|
// Note: it is very important that each on_changed occurs after exactly one begin_changes.
|
|
// (See #459 for example)
|
|
if (mp_parent) {
|
|
mp_parent->begin_changes ();
|
|
} else {
|
|
#if defined(HAVE_QT)
|
|
emit about_to_change ();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void MacroCollection::on_menu_needs_update ()
|
|
{
|
|
#if defined(HAVE_QT)
|
|
emit menu_needs_update ();
|
|
#endif
|
|
}
|
|
|
|
void MacroCollection::on_changed ()
|
|
{
|
|
// Note: it is very important that each on_changed occurs after exactly one begin_changes.
|
|
// (See #459 for example)
|
|
#if defined(HAVE_QT)
|
|
emit changed ();
|
|
#endif
|
|
on_macro_collection_changed (this);
|
|
}
|
|
|
|
void MacroCollection::on_macro_collection_changed (MacroCollection *mc)
|
|
{
|
|
if (mp_parent) {
|
|
mp_parent->on_macro_collection_changed (mc);
|
|
} else {
|
|
#if defined(HAVE_QT)
|
|
emit macro_collection_changed (mc);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void MacroCollection::on_child_deleted (MacroCollection *mc)
|
|
{
|
|
#if defined(HAVE_QT)
|
|
emit child_deleted (mc);
|
|
#endif
|
|
on_macro_collection_deleted (mc);
|
|
}
|
|
|
|
void MacroCollection::on_macro_collection_deleted (MacroCollection *mc)
|
|
{
|
|
if (mp_parent) {
|
|
mp_parent->on_macro_collection_deleted (mc);
|
|
} else {
|
|
#if defined(HAVE_QT)
|
|
emit macro_collection_deleted (mc);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void MacroCollection::on_macro_deleted_here (Macro *macro)
|
|
{
|
|
#if defined(HAVE_QT)
|
|
emit macro_deleted_here (macro);
|
|
#endif
|
|
on_macro_deleted (macro);
|
|
}
|
|
|
|
void MacroCollection::on_macro_deleted (Macro *macro)
|
|
{
|
|
if (mp_parent) {
|
|
mp_parent->on_macro_deleted (macro);
|
|
} else {
|
|
#if defined(HAVE_QT)
|
|
emit macro_deleted (macro);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void MacroCollection::on_macro_changed (Macro *macro)
|
|
{
|
|
if (mp_parent) {
|
|
mp_parent->on_macro_changed (macro);
|
|
} else {
|
|
#if defined(HAVE_QT)
|
|
emit macro_changed (macro);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void MacroCollection::collect_used_nodes (std::set <Macro *> ¯os, std::set <MacroCollection *> ¯o_collections)
|
|
{
|
|
for (MacroCollection::child_iterator c = begin_children (); c != end_children (); ++c) {
|
|
macro_collections.insert (c->second);
|
|
c->second->collect_used_nodes (macros, macro_collections);
|
|
}
|
|
for (MacroCollection::iterator c = begin (); c != end (); ++c) {
|
|
macros.insert (c->second);
|
|
}
|
|
}
|
|
|
|
Macro *MacroCollection::macro_by_name (const std::string &name, Macro::Format format)
|
|
{
|
|
std::multimap <std::string, Macro *>::iterator i = m_macros.find (name);
|
|
while (i != m_macros.end () && i->first == name) {
|
|
if (format == Macro::NoFormat || i->second->format () == format) {
|
|
return i->second;
|
|
}
|
|
++i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const Macro *MacroCollection::macro_by_name (const std::string &name, Macro::Format format) const
|
|
{
|
|
std::multimap <std::string, Macro *>::const_iterator i = m_macros.find (name);
|
|
while (i != m_macros.end () && i->first == name) {
|
|
if (format == Macro::NoFormat || i->second->format () == format) {
|
|
return i->second;
|
|
}
|
|
++i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
MacroCollection *MacroCollection::folder_by_name (const std::string &name)
|
|
{
|
|
std::map <std::string, MacroCollection *>::iterator i = m_folders.find (name);
|
|
if (i != m_folders.end ()) {
|
|
return i->second;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
const MacroCollection *MacroCollection::folder_by_name (const std::string &name) const
|
|
{
|
|
std::map <std::string, MacroCollection *>::const_iterator i = m_folders.find (name);
|
|
if (i != m_folders.end ()) {
|
|
return i->second;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
std::string MacroCollection::path () const
|
|
{
|
|
if (m_virtual_mode) {
|
|
return m_path;
|
|
} else if (mp_parent) {
|
|
return tl::combine_path (mp_parent->path (), m_path);
|
|
} else {
|
|
return m_path;
|
|
}
|
|
}
|
|
|
|
std::string MacroCollection::display_string () const
|
|
{
|
|
if (m_virtual_mode) {
|
|
return "[" + m_description + "]";
|
|
} else {
|
|
std::string r = name ();
|
|
if (! m_description.empty ()) {
|
|
r += " - " + m_description;
|
|
}
|
|
return r;
|
|
}
|
|
}
|
|
|
|
void
|
|
MacroCollection::make_readonly (bool f)
|
|
{
|
|
if (m_readonly != f) {
|
|
begin_changes ();
|
|
m_readonly = f;
|
|
on_changed ();
|
|
}
|
|
}
|
|
|
|
MacroCollection *
|
|
MacroCollection::add_folder (const std::string &description, const std::string &p, const std::string &cat, bool readonly, bool force_create)
|
|
{
|
|
if (! p.empty () && p[0] == ':') {
|
|
|
|
readonly = true;
|
|
|
|
} else {
|
|
|
|
std::string fp = p;
|
|
if (! tl::is_absolute (fp)) {
|
|
fp = tl::combine_path (path (), fp);
|
|
}
|
|
|
|
if (! tl::file_exists (fp)) {
|
|
|
|
// 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: " << fp;
|
|
}
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
if (tl::verbosity () >= 20) {
|
|
tl::log << "Folder does not exist yet - trying to create it: " << fp;
|
|
}
|
|
if (! tl::mkpath (fp)) {
|
|
if (tl::verbosity () >= 10) {
|
|
tl::error << "Unable to create folder path: " << fp;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (! tl::is_dir (fp)) {
|
|
if (tl::verbosity () >= 10) {
|
|
tl::error << "Folder is not a directory: " << fp;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
for (child_iterator f = m_folders.begin (); f != m_folders.end (); ++f) {
|
|
// skip, if that folder is in the collection already
|
|
if (f->second->path () == fp) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (! readonly && ! tl::is_writable (fp)) {
|
|
readonly = true;
|
|
if (tl::verbosity () >= 20) {
|
|
tl::log << "Folder is read-only: " << fp;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
begin_changes ();
|
|
|
|
MacroCollection *mc = m_folders.insert (std::make_pair (p, new MacroCollection ())).first->second;
|
|
mc->set_name (p);
|
|
mc->set_description (description);
|
|
mc->set_category (cat);
|
|
mc->set_readonly (readonly);
|
|
mc->scan ();
|
|
mc->set_parent (this);
|
|
|
|
on_changed ();
|
|
on_macro_changed (0);
|
|
|
|
return mc;
|
|
}
|
|
|
|
void MacroCollection::rescan ()
|
|
{
|
|
for (std::map <std::string, MacroCollection *>::const_iterator m = m_folders.begin (); m != m_folders.end (); ++m) {
|
|
m->second->scan ();
|
|
}
|
|
}
|
|
|
|
#if defined(HAVE_QT)
|
|
namespace {
|
|
|
|
/**
|
|
* @brief A QResource variant that allows access to the children
|
|
*/
|
|
class ResourceWithChildren
|
|
: public QResource
|
|
{
|
|
public:
|
|
ResourceWithChildren (const QString &path) : QResource (path) { }
|
|
using QResource::children;
|
|
};
|
|
|
|
}
|
|
#endif
|
|
|
|
void MacroCollection::scan ()
|
|
{
|
|
std::string p = path ();
|
|
|
|
if (tl::verbosity () >= 20) {
|
|
tl::info << "Scanning macro path " << p << " (readonly=" << m_readonly << ")";
|
|
}
|
|
|
|
if (! p.empty () && p[0] == ':') {
|
|
|
|
#if defined(HAVE_QT)
|
|
|
|
ResourceWithChildren res (tl::to_qstring (p));
|
|
QStringList children = res.children ();
|
|
children.sort ();
|
|
|
|
for (auto c = children.begin (); c != children.end (); ++c) {
|
|
|
|
std::string url = p + "/" + tl::to_string (*c);
|
|
QResource res (tl::to_qstring (url));
|
|
if (res.size () > 0) {
|
|
create_entry (url);
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
std::vector<std::string> res = tl::find_resources (std::string (p, 1) + "/*");
|
|
for (auto c = res.begin (); c != res.end (); ++c) {
|
|
create_entry (":" + *c);
|
|
}
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
std::set<std::string> suffixes;
|
|
suffixes.insert ("lym");
|
|
suffixes.insert ("txt");
|
|
// TODO: should be either *.rb or *.python, depending on the category.
|
|
// Right now we rely on the folders not containing foreign files.
|
|
suffixes.insert ("rb");
|
|
suffixes.insert ("py");
|
|
|
|
// add the suffixes in the DSL interpreter declarations
|
|
for (tl::Registrar<lym::MacroInterpreter>::iterator cls = tl::Registrar<lym::MacroInterpreter>::begin (); cls != tl::Registrar<lym::MacroInterpreter>::end (); ++cls) {
|
|
if (! cls->suffix ().empty ()) {
|
|
suffixes.insert (cls->suffix ());
|
|
}
|
|
}
|
|
|
|
std::vector<std::string> files = tl::dir_entries (p, true /*with_files*/, false /*with_dirs*/, true /*without_dotfiles*/);
|
|
for (auto f = files.begin (); f != files.end (); ++f) {
|
|
if (suffixes.find (tl::extension_last (*f)) != suffixes.end ()) {
|
|
create_entry (tl::combine_path (p, *f));
|
|
}
|
|
}
|
|
|
|
std::vector<std::string> dirs = tl::dir_entries (p, false /*with_files*/, true /*with_dirs*/, true /*without_dotfiles*/);
|
|
for (auto f = dirs.begin (); f != dirs.end (); ++f) {
|
|
|
|
std::string fp = tl::combine_path (p, *f);
|
|
if (! tl::is_dir (fp)) {
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
|
|
MacroCollection *&mc = m_folders.insert (std::make_pair (*f, (MacroCollection *) 0)).first->second;
|
|
if (! mc) {
|
|
mc = new MacroCollection ();
|
|
mc->set_name (*f);
|
|
mc->set_virtual_mode (NotVirtual);
|
|
bool ro = (m_readonly || ! tl::is_writable (fp));
|
|
mc->set_readonly (ro);
|
|
mc->set_parent (this);
|
|
mc->scan ();
|
|
}
|
|
|
|
} catch (tl::Exception &ex) {
|
|
tl::error << ex.msg ();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
MacroCollection::create_entry (const std::string &path)
|
|
{
|
|
try {
|
|
|
|
std::string n = tl::complete_basename (path);
|
|
|
|
Macro::Format format = Macro::NoFormat;
|
|
Macro::Interpreter interpreter = Macro::None;
|
|
std::string dsl_name;
|
|
bool autorun = false;
|
|
|
|
std::unique_ptr<lym::Macro> new_macro;
|
|
|
|
if (Macro::format_from_suffix (path, interpreter, dsl_name, autorun, format)) {
|
|
|
|
iterator mm = m_macros.find (n);
|
|
bool found = false;
|
|
while (mm != m_macros.end () && mm->first == n && ! found) {
|
|
if ((interpreter == Macro::None || mm->second->interpreter () == interpreter) &&
|
|
(dsl_name.empty () || mm->second->dsl_interpreter () == dsl_name) &&
|
|
mm->second->format () == format) {
|
|
found = true;
|
|
}
|
|
++mm;
|
|
}
|
|
if (! found) {
|
|
Macro *m = new Macro ();
|
|
new_macro.reset (m);
|
|
m->set_interpreter (interpreter);
|
|
m->set_autorun_default (autorun);
|
|
m->set_autorun (autorun);
|
|
m->set_dsl_interpreter (dsl_name);
|
|
m->set_format (format);
|
|
m->set_name (n);
|
|
m->load_from (path);
|
|
m->set_readonly (m_readonly);
|
|
m->reset_modified ();
|
|
m->set_is_file ();
|
|
m->set_parent (this);
|
|
}
|
|
|
|
}
|
|
|
|
if (new_macro.get ()) {
|
|
m_macros.insert (std::make_pair (n, new_macro.release ()));
|
|
}
|
|
|
|
} catch (tl::Exception &ex) {
|
|
tl::error << "Reading " << path << ": " << ex.msg ();
|
|
}
|
|
}
|
|
|
|
void MacroCollection::clear ()
|
|
{
|
|
begin_changes ();
|
|
do_clear ();
|
|
on_changed ();
|
|
}
|
|
|
|
void MacroCollection::erase (lym::Macro *mp)
|
|
{
|
|
for (iterator m = m_macros.begin (); m != m_macros.end (); ++m) {
|
|
if (m->second == mp) {
|
|
begin_changes ();
|
|
on_macro_deleted_here (mp);
|
|
m_macros.erase (m);
|
|
delete mp;
|
|
on_changed ();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MacroCollection::erase (lym::MacroCollection *mp)
|
|
{
|
|
for (child_iterator f = m_folders.begin (); f != m_folders.end (); ++f) {
|
|
if (f->second == mp) {
|
|
begin_changes ();
|
|
on_child_deleted (mp);
|
|
m_folders.erase (f);
|
|
delete mp;
|
|
on_changed ();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MacroCollection::erase (iterator i)
|
|
{
|
|
begin_changes ();
|
|
on_macro_deleted_here (i->second);
|
|
delete i->second;
|
|
m_macros.erase (i);
|
|
on_changed ();
|
|
}
|
|
|
|
void MacroCollection::erase (child_iterator i)
|
|
{
|
|
begin_changes ();
|
|
on_child_deleted (i->second);
|
|
delete i->second;
|
|
m_folders.erase (i);
|
|
on_changed ();
|
|
}
|
|
|
|
void MacroCollection::save ()
|
|
{
|
|
for (child_iterator f = m_folders.begin (); f != m_folders.end (); ++f) {
|
|
f->second->save ();
|
|
}
|
|
|
|
for (iterator m = m_macros.begin (); m != m_macros.end (); ++m) {
|
|
if (m->second->is_modified () && ! m->second->is_readonly () && ! m->second->path ().empty ()) {
|
|
try {
|
|
m->second->save ();
|
|
} catch (tl::Exception &ex) {
|
|
tl::error << ex.msg ();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool MacroCollection::rename (const std::string &n)
|
|
{
|
|
if (tl::verbosity () >= 20) {
|
|
tl::info << "Renaming macro folder " << path () << " to " << n;
|
|
}
|
|
begin_changes ();
|
|
if (! tl::rename_file (path (), n)) {
|
|
on_changed ();
|
|
return false;
|
|
} else {
|
|
m_path = n;
|
|
on_changed ();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
lym::MacroCollection *MacroCollection::create_folder (const char *prefix, bool mkdir)
|
|
{
|
|
std::string name;
|
|
int n = 0;
|
|
do {
|
|
name = (prefix ? prefix : "new_folder");
|
|
if (n > 0) {
|
|
name += "_" + tl::to_string (n);
|
|
}
|
|
if (m_folders.find (name) == m_folders.end ()) {
|
|
break;
|
|
}
|
|
++n;
|
|
} while (true);
|
|
|
|
if (mkdir && ! tl::mkpath (tl::combine_path (path (), name))) {
|
|
return 0;
|
|
}
|
|
|
|
begin_changes ();
|
|
|
|
lym::MacroCollection *m = m_folders.insert (std::make_pair (name, new lym::MacroCollection ())).first->second;
|
|
m->set_virtual_mode (NotVirtual);
|
|
m->set_name (name);
|
|
m->set_parent (this);
|
|
|
|
on_changed ();
|
|
|
|
return m;
|
|
}
|
|
|
|
lym::Macro *MacroCollection::create (const char *prefix, Macro::Format format)
|
|
{
|
|
std::string name;
|
|
int n = 0;
|
|
do {
|
|
name = (prefix ? prefix : "new_macro");
|
|
if (n > 0) {
|
|
name += "_" + tl::to_string (n);
|
|
}
|
|
if (! macro_by_name (name, format)) {
|
|
break;
|
|
}
|
|
++n;
|
|
} while (true);
|
|
|
|
begin_changes ();
|
|
|
|
lym::Macro *m = m_macros.insert (std::make_pair (name, new lym::Macro ()))->second;
|
|
m->set_name (name);
|
|
m->set_parent (this);
|
|
|
|
on_changed ();
|
|
|
|
return m;
|
|
}
|
|
|
|
void MacroCollection::add_unspecific (lym::Macro *m)
|
|
{
|
|
begin_changes ();
|
|
m_macros.insert (std::make_pair (m->name (), m));
|
|
m->set_parent (this);
|
|
on_changed ();
|
|
}
|
|
|
|
bool MacroCollection::add (lym::Macro *m)
|
|
{
|
|
std::string d = tl::normalize_path (path ());
|
|
std::string dd = tl::normalize_path (m->dir ());
|
|
|
|
if (d == dd) {
|
|
|
|
begin_changes ();
|
|
m_macros.insert (std::make_pair (m->name (), m));
|
|
m->set_parent (this);
|
|
on_changed ();
|
|
return true;
|
|
|
|
} else {
|
|
|
|
for (child_iterator c = begin_children (); c != end_children (); ++c) {
|
|
if (c->second->add (m)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// try to detect new child folders. If that is the case, create that folder and add
|
|
// the macro there.
|
|
std::string dm = tl::normalize_path (m->dir ());
|
|
while (true) {
|
|
|
|
std::string folder_name = tl::filename (dm);
|
|
dm = tl::dirname (dm);
|
|
if (dm.empty () || dm == ".") {
|
|
break;
|
|
}
|
|
|
|
if (dm == d) {
|
|
|
|
begin_changes ();
|
|
lym::MacroCollection *mc = m_folders.insert (std::make_pair (folder_name, new MacroCollection ())).first->second;
|
|
mc->set_virtual_mode (NotVirtual);
|
|
mc->set_name (folder_name);
|
|
mc->set_parent (this);
|
|
on_changed ();
|
|
|
|
return mc->add (m);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool MacroCollection::del ()
|
|
{
|
|
if (tl::verbosity () >= 20) {
|
|
tl::info << "Deleting macro folder " << path ();
|
|
}
|
|
return tl::rm_dir_recursive (path ());
|
|
}
|
|
|
|
void MacroCollection::rename_macro (Macro *macro, const std::string &new_name)
|
|
{
|
|
iterator m = m_macros.find (macro->name ());
|
|
while (m != m_macros.end () && m->first == macro->name ()) {
|
|
if (m->second == macro) {
|
|
m_macros.erase (m);
|
|
m_macros.insert (std::make_pair (new_name, macro));
|
|
return;
|
|
}
|
|
++m;
|
|
}
|
|
}
|
|
|
|
lym::Macro *MacroCollection::find_macro (const std::string &path)
|
|
{
|
|
for (iterator m = begin (); m != end (); ++m) {
|
|
if (m->second->path () == path) {
|
|
return m->second;
|
|
}
|
|
}
|
|
|
|
for (child_iterator mc = begin_children (); mc != end_children (); ++mc) {
|
|
lym::Macro *macro = mc->second->find_macro (path);
|
|
if (macro) {
|
|
return macro;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
MacroCollection &MacroCollection::root ()
|
|
{
|
|
return ms_root;
|
|
}
|
|
|
|
static bool sync_macros (lym::MacroCollection *current, lym::MacroCollection *actual, bool safe)
|
|
{
|
|
bool ret = false;
|
|
|
|
if (actual) {
|
|
current->make_readonly (actual->is_readonly ());
|
|
}
|
|
|
|
std::vector<lym::MacroCollection *> folders_to_delete;
|
|
|
|
for (lym::MacroCollection::child_iterator m = current->begin_children (); m != current->end_children (); ++m) {
|
|
lym::MacroCollection *cm = actual ? actual->folder_by_name (m->first) : 0;
|
|
if (! cm) {
|
|
folders_to_delete.push_back (m->second);
|
|
}
|
|
}
|
|
|
|
if (actual) {
|
|
for (lym::MacroCollection::child_iterator m = actual->begin_children (); m != actual->end_children (); ++m) {
|
|
lym::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, safe)) {
|
|
ret = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// delete folders which do no longer exist
|
|
for (std::vector<lym::MacroCollection *>::iterator m = folders_to_delete.begin (); m != folders_to_delete.end (); ++m) {
|
|
ret = true;
|
|
sync_macros (*m, 0, safe);
|
|
current->erase (*m);
|
|
}
|
|
|
|
std::vector<lym::Macro *> macros_to_delete;
|
|
|
|
for (lym::MacroCollection::iterator m = current->begin (); m != current->end (); ++m) {
|
|
lym::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 (lym::MacroCollection::iterator m = actual->begin (); m != actual->end (); ++m) {
|
|
lym::Macro *cm = current->macro_by_name (m->first, m->second->format ());
|
|
if (cm) {
|
|
if (*cm != *m->second && (! safe || ! cm->is_modified ())) {
|
|
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<lym::Macro *>::const_iterator m = macros_to_delete.begin (); m != macros_to_delete.end (); ++m) {
|
|
current->erase (*m);
|
|
ret = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void MacroCollection::reload (bool safe)
|
|
{
|
|
// create a new collection and synchronize
|
|
|
|
lym::MacroCollection new_collection;
|
|
for (lym::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, safe);
|
|
}
|
|
|
|
static bool has_autorun_for (const lym::MacroCollection &collection, bool early)
|
|
{
|
|
for (lym::MacroCollection::const_child_iterator c = collection.begin_children (); c != collection.end_children (); ++c) {
|
|
if (has_autorun_for (*c->second, early)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
for (lym::MacroCollection::const_iterator c = collection.begin (); c != collection.end (); ++c) {
|
|
if ((early && c->second->is_autorun_early ()) || (!early && c->second->is_autorun () && !c->second->is_autorun_early ())) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool MacroCollection::has_autorun () const
|
|
{
|
|
return has_autorun_for (*this, false);
|
|
}
|
|
|
|
bool MacroCollection::has_autorun_early () const
|
|
{
|
|
return has_autorun_for (*this, true);
|
|
}
|
|
|
|
static int collect_priority (lym::MacroCollection &collection, bool early, int from_prio)
|
|
{
|
|
int p = -1;
|
|
|
|
for (lym::MacroCollection::child_iterator c = collection.begin_children (); c != collection.end_children (); ++c) {
|
|
int pp = collect_priority (*c->second, early, from_prio);
|
|
if (pp >= from_prio && (p < 0 || pp < p)) {
|
|
p = pp;
|
|
}
|
|
}
|
|
|
|
for (lym::MacroCollection::iterator c = collection.begin (); c != collection.end (); ++c) {
|
|
if (c->second->can_run () && ((early && c->second->is_autorun_early ()) || (!early && c->second->is_autorun () && !c->second->is_autorun_early ()))) {
|
|
int pp = c->second->priority ();
|
|
if (pp >= from_prio && (p < 0 || pp < p)) {
|
|
p = pp;
|
|
}
|
|
}
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
static void autorun_for_prio (lym::MacroCollection &collection, bool early, std::set<std::string> *executed_already, int prio)
|
|
{
|
|
for (lym::MacroCollection::child_iterator c = collection.begin_children (); c != collection.end_children (); ++c) {
|
|
autorun_for_prio (*c->second, early, executed_already, prio);
|
|
}
|
|
|
|
for (lym::MacroCollection::iterator c = collection.begin (); c != collection.end (); ++c) {
|
|
|
|
if (c->second->priority () == prio && c->second->can_run () && ((early && c->second->is_autorun_early ()) || (!early && c->second->is_autorun () && !c->second->is_autorun_early ()))) {
|
|
|
|
if (!executed_already || executed_already->find (c->second->path ()) == executed_already->end ()) {
|
|
|
|
BEGIN_PROTECTED_SILENT
|
|
c->second->run ();
|
|
c->second->install_doc ();
|
|
END_PROTECTED_SILENT
|
|
|
|
if (executed_already) {
|
|
executed_already->insert (c->second->path ());
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
static void autorun_for (lym::MacroCollection &collection, bool early, std::set<std::string> *executed_already)
|
|
{
|
|
int prio = 0;
|
|
while (true) {
|
|
int p = collect_priority (collection, early, prio);
|
|
if (p < prio) {
|
|
break;
|
|
}
|
|
autorun_for_prio (collection, early, executed_already, p);
|
|
prio = p + 1;
|
|
}
|
|
}
|
|
|
|
void MacroCollection::autorun (std::set<std::string> *already_executed)
|
|
{
|
|
autorun_for (*this, false, already_executed);
|
|
}
|
|
|
|
void MacroCollection::autorun_early (std::set<std::string> *already_executed)
|
|
{
|
|
autorun_for (*this, true, already_executed);
|
|
}
|
|
|
|
void MacroCollection::dump (int l)
|
|
{
|
|
for (int i = 0; i < l; ++i) { printf (" "); }
|
|
printf ("----\n");
|
|
for (int i = 0; i < l; ++i) { printf (" "); }
|
|
printf ("Collection: %s\n", name ().c_str ());
|
|
for (int i = 0; i < l; ++i) { printf (" "); }
|
|
printf ("Collection-path: %s\n", path ().c_str ());
|
|
for (int i = 0; i < l; ++i) { printf (" "); }
|
|
printf ("Collection-description: %s\n", description ().c_str ());
|
|
for (int i = 0; i < l; ++i) { printf (" "); }
|
|
printf("Collection-readonly: %d\n", is_readonly ());
|
|
printf ("\n");
|
|
|
|
for (iterator m = begin (); m != end (); ++m) {
|
|
for (int i = 0; i < l; ++i) { printf (" "); }
|
|
printf("Name: %s%s\n", m->second->name ().c_str (), m->second->is_modified() ? "*" : "");
|
|
for (int i = 0; i < l; ++i) { printf (" "); }
|
|
printf(" Path: %s\n", m->second->path ().c_str ());
|
|
for (int i = 0; i < l; ++i) { printf (" "); }
|
|
printf(" Readonly: %d\n", m->second->is_readonly ());
|
|
for (int i = 0; i < l; ++i) { printf (" "); }
|
|
printf(" Autorun: %d\n", m->second->is_autorun ());
|
|
for (int i = 0; i < l; ++i) { printf (" "); }
|
|
printf(" Autorun-early: %d\n", m->second->is_autorun_early ());
|
|
for (int i = 0; i < l; ++i) { printf (" "); }
|
|
printf(" Description: %s\n", m->second->description ().c_str ());
|
|
}
|
|
|
|
for (child_iterator m = begin_children (); m != end_children (); ++m) {
|
|
m->second->dump (l + 1);
|
|
}
|
|
}
|
|
|
|
}
|
|
|