From 1b129758d84e1c49e3234fc5ebf4bb51836f8ade Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 Apr 2022 23:27:33 +0200 Subject: [PATCH] More range for NoQt option - covers lym (without MacroCollection), drc, lvs and buddies now --- src/buddies/src/bd/strmxor.cc | 4 +- src/klayout.pro | 24 +- src/lay/lay/layMacroController.h | 2 +- src/lay/lay/layMacroEditorTree.cc | 2 +- src/laybasic/laybasic/layLayoutCanvas.h | 3 +- src/laybasic/laybasic/layLayoutView.h | 1 + src/lym/lym/gsiDeclLymMacro.cc | 7 +- src/lym/lym/lym.pro | 2 + src/lym/lym/lymMacro.cc | 972 +---------------- src/lym/lym/lymMacro.h | 529 +--------- src/lym/lym/lymMacroCollection.cc | 987 ++++++++++++++++++ src/lym/lym/lymMacroCollection.h | 594 +++++++++++ src/lym/lym/lymMacroInterpreter.cc | 6 +- src/plugins/tools/view_25d/view_25d.pro | 14 +- src/tl/unit_tests/tlFileSystemWatcherTests.cc | 2 + 15 files changed, 1657 insertions(+), 1492 deletions(-) create mode 100644 src/lym/lym/lymMacroCollection.cc create mode 100644 src/lym/lym/lymMacroCollection.h diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 03de6cf80..1e5cf7722 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -408,7 +408,7 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) db::Layout layout_b; { - tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Loading file (A): ")) + infile_a); + tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Loading file (A): ")) + infile_a); db::LoadLayoutOptions load_options; generic_reader_options_a.configure (load_options); @@ -416,7 +416,7 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) } { - tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Loading file (B): ")) + infile_b); + tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Loading file (B): ")) + infile_b); db::LoadLayoutOptions load_options; generic_reader_options_b.configure (load_options); diff --git a/src/klayout.pro b/src/klayout.pro index 1cea2a7a7..9d584c003 100644 --- a/src/klayout.pro +++ b/src/klayout.pro @@ -11,6 +11,8 @@ SUBDIRS = \ lib \ plugins \ unit_tests \ + buddies \ + lym \ !equals(HAVE_QT, "0") { @@ -20,8 +22,6 @@ SUBDIRS = \ laybasic \ lay \ ant \ - buddies \ - lym \ img \ edt \ fontgen \ @@ -60,21 +60,22 @@ lib.depends += db plugins.depends += lib rdb db -!equals(HAVE_QT, "0") { +buddies.depends += plugins lym $$LANG_DEPENDS +lym.depends += gsi $$LANG_DEPENDS - buddies.depends += plugins lym $$LANG_DEPENDS +equals(HAVE_RUBY, "1") { + SUBDIRS += drc lvs + MAIN_DEPENDS += drc lvs + drc.depends += rdb lym + lvs.depends += drc +} + +!equals(HAVE_QT, "0") { equals(HAVE_PYTHON, "1") { pymod.depends += lay } - equals(HAVE_RUBY, "1") { - SUBDIRS += drc lvs - MAIN_DEPENDS += drc lvs - drc.depends += rdb lym - lvs.depends += drc - } - equals(HAVE_QTBINDINGS, "1") { SUBDIRS += gsiqt @@ -89,7 +90,6 @@ plugins.depends += lib rdb db plugins.depends += lay ant - lym.depends += gsi $$LANG_DEPENDS laybasic.depends += rdb lym ant.depends += laybasic img.depends += laybasic diff --git a/src/lay/lay/layMacroController.h b/src/lay/lay/layMacroController.h index cae8e979b..707cc44e9 100644 --- a/src/lay/lay/layMacroController.h +++ b/src/lay/lay/layMacroController.h @@ -26,7 +26,7 @@ #include "layCommon.h" #include "layPlugin.h" -#include "lymMacro.h" +#include "lymMacroCollection.h" #include "tlObject.h" #include "tlDeferredExecution.h" #include "tlFileSystemWatcher.h" diff --git a/src/lay/lay/layMacroEditorTree.cc b/src/lay/lay/layMacroEditorTree.cc index 544930dd0..a6035d641 100644 --- a/src/lay/lay/layMacroEditorTree.cc +++ b/src/lay/lay/layMacroEditorTree.cc @@ -23,7 +23,7 @@ #include "layMacroEditorTree.h" #include "layMacroEditorDialog.h" -#include "lymMacro.h" +#include "lymMacroCollection.h" #include "tlExceptions.h" #include "tlInternational.h" #include "tlException.h" diff --git a/src/laybasic/laybasic/layLayoutCanvas.h b/src/laybasic/laybasic/layLayoutCanvas.h index 34660f835..bbdbfcfcd 100644 --- a/src/laybasic/laybasic/layLayoutCanvas.h +++ b/src/laybasic/laybasic/layLayoutCanvas.h @@ -171,8 +171,6 @@ public: void update_image (); - virtual void paintEvent (QPaintEvent *); - /** * @brief Specifies the global transformation which is always applied first */ @@ -386,6 +384,7 @@ private: QMutex m_mutex; + virtual void paintEvent (QPaintEvent *); virtual void resizeEvent (QResizeEvent *); virtual bool event (QEvent *e); virtual void key_event (unsigned int key, unsigned int buttons); diff --git a/src/laybasic/laybasic/layLayoutView.h b/src/laybasic/laybasic/layLayoutView.h index 0ad398392..9fd1519b9 100644 --- a/src/laybasic/laybasic/layLayoutView.h +++ b/src/laybasic/laybasic/layLayoutView.h @@ -2673,6 +2673,7 @@ public slots: void min_hier_changed (int i); void max_hier_changed (int i); +private: // event handlers used to connect to the layout object's events void signal_hier_changed (); void signal_bboxes_from_layer_changed (unsigned int cv_index, unsigned int layer_index); diff --git a/src/lym/lym/gsiDeclLymMacro.cc b/src/lym/lym/gsiDeclLymMacro.cc index 7d22afa72..3bce22d44 100644 --- a/src/lym/lym/gsiDeclLymMacro.cc +++ b/src/lym/lym/gsiDeclLymMacro.cc @@ -27,6 +27,7 @@ #include "gsiEnums.h" #include "lymMacroInterpreter.h" #include "lymMacro.h" +#include "lymMacroCollection.h" #include "rba.h" #include "tlClassRegistry.h" @@ -204,7 +205,7 @@ public: lym::Macro *create_template (const std::string &url) { if (! mp_registration) { - throw std::runtime_error (tl::to_string (QObject::tr ("MacroInterpreter::create_template must be called after register"))); + throw std::runtime_error (tl::to_string (tr ("MacroInterpreter::create_template must be called after register"))); } lym::Macro *m = new lym::Macro (); @@ -472,7 +473,11 @@ gsi::ClassExt inject_Format_in_parent (decl_FormatEnum.def static lym::Macro *macro_by_path (const std::string &path) { +#if defined(HAVE_QT) return lym::MacroCollection::root ().find_macro (path); +#else + return 0; +#endif } static std::string real_path (const std::string &path, int line) diff --git a/src/lym/lym/lym.pro b/src/lym/lym/lym.pro index 18dff3666..9a87d4c8b 100644 --- a/src/lym/lym/lym.pro +++ b/src/lym/lym/lym.pro @@ -9,12 +9,14 @@ DEFINES += MAKE_LYM_LIBRARY SOURCES = \ gsiDeclLymMacro.cc \ lymMacroInterpreter.cc \ + lymMacroCollection.cc \ lymMacro.cc \ HEADERS = \ lymCommon.h \ lymInclude.h \ lymMacroInterpreter.h \ + lymMacroCollection.h \ lymMacro.h \ INCLUDEPATH += $$TL_INC $$GSI_INC diff --git a/src/lym/lym/lymMacro.cc b/src/lym/lym/lymMacro.cc index 429a4fbf2..26fbd4708 100644 --- a/src/lym/lym/lymMacro.cc +++ b/src/lym/lym/lymMacro.cc @@ -22,6 +22,7 @@ #include "lymMacro.h" +#include "lymMacroCollection.h" #include "lymMacroInterpreter.h" #include "tlExceptions.h" #include "gsiDecl.h" @@ -39,39 +40,42 @@ #include "rba.h" #include "pya.h" -#include -#include -#include -#include +#include "tlFileUtils.h" +#include "tlUri.h" #include #include #include #include + namespace lym { // ---------------------------------------------------------------------- Macro::Macro () - : m_modified (true), m_readonly (false), m_autorun (false), m_autorun_default (false), m_autorun_early (false), m_priority (0), m_show_in_menu (false), m_is_file (false), mp_parent (0), m_interpreter (None), m_format (Macro::NoFormat) + : m_modified (true), m_readonly (false), m_autorun (false), m_autorun_default (false), m_autorun_early (false), m_priority (0), m_show_in_menu (false), m_is_file (false), m_interpreter (None), m_format (Macro::NoFormat) { - // .. nothing yet .. + mp_parent = 0; } void Macro::on_menu_needs_update () { +#if defined(HAVE_QT) // forward the signal to the root collection - the main window will attach to this MacroCollection::root ().on_menu_needs_update (); +#endif } void Macro::on_changed () { +#if defined(HAVE_QT) emit changed (); if (mp_parent) { mp_parent->on_macro_changed (this); } +#endif } void Macro::assign (const lym::Macro &other) @@ -133,8 +137,7 @@ bool Macro::del () if (tl::verbosity () >= 20) { tl::log << "Deleting macro " << path (); } - QFile f (tl::to_qstring (path ())); - return f.remove (); + return tl::rm_file (path ()); } else { return true; } @@ -254,7 +257,7 @@ void Macro::load_from (const std::string &fn) } } else { - throw tl::Exception (tl::to_string (QObject::tr ("Unable to determine format for file from suffix or format spec ")) + fn); + throw tl::Exception (tl::to_string (tr ("Unable to determine format for file from suffix or format spec ")) + fn); } m_modified = true; @@ -270,7 +273,7 @@ void Macro::load_from_string (const std::string &text, const std::string &url) tl::log << "Loading macro from " << url; } - if (format_from_suffix (tl::to_string (QUrl (tl::to_qstring (url)).path ()), m_interpreter, m_dsl_interpreter, m_autorun_default, m_format)) { + if (format_from_suffix (tl::URI (url).path (), m_interpreter, m_dsl_interpreter, m_autorun_default, m_format)) { m_autorun = m_autorun_default; @@ -291,7 +294,7 @@ void Macro::load_from_string (const std::string &text, const std::string &url) } } else { - throw tl::Exception (tl::to_string (QObject::tr ("Unable to determine format for file from suffix ")) + url); + throw tl::Exception (tl::to_string (tr ("Unable to determine format for file from suffix ")) + url); } m_modified = true; @@ -306,8 +309,7 @@ void Macro::load () bool Macro::format_from_suffix (const std::string &fn, Macro::Interpreter &interpreter, std::string &dsl_name, bool &autorun_pref, Macro::Format &format) { - std::string suffix = tl::to_string (QFileInfo (tl::to_qstring (fn)).suffix ()); - return format_from_suffix_string (suffix, interpreter, dsl_name, autorun_pref, format); + return format_from_suffix_string (tl::extension (fn), interpreter, dsl_name, autorun_pref, format); } std::pair @@ -425,10 +427,9 @@ std::string Macro::path () const std::string suffix = suffix_for_format (m_interpreter, m_dsl_interpreter, m_format); if (mp_parent) { - return tl::to_string (QFileInfo (QDir (tl::to_qstring (mp_parent->path ())), tl::to_qstring (m_name + suffix)).filePath ()); - } else { - return m_name + suffix; + return tl::combine_path (mp_parent->path (), m_name + suffix); } + return m_name + suffix; } void Macro::set_file_path (const std::string &fp) @@ -459,8 +460,7 @@ bool Macro::rename (const std::string &n) if (tl::verbosity () >= 20) { tl::log << "Renaming macro " << path () << " to " << n; } - QFile f (tl::to_qstring (path ())); - if (! f.rename (QFileInfo (QDir (tl::to_qstring (parent ()->path ())), tl::to_qstring (n + suffix)).filePath ())) { + if (! tl::rename_file (path (), tl::combine_path (parent ()->path (), n + suffix))) { return false; } } @@ -478,9 +478,8 @@ std::string Macro::dir () const { if (mp_parent) { return mp_parent->path (); - } else { - return std::string (); } + return tl::dirname (path ()); } std::string Macro::display_string () const @@ -948,7 +947,7 @@ void Macro::install_doc () const --i; if (cls) { - tl::error << tl::to_string (QObject::tr ("Reading class doc from ")) << path () << ": " << tl::to_string (QObject::tr ("Duplicate @class")); + tl::error << tl::to_string (tr ("Reading class doc from ")) << path () << ": " << tl::to_string (tr ("Duplicate @class")); return; } @@ -970,7 +969,7 @@ void Macro::install_doc () const } } if (! super_cls) { - tl::error << tl::to_string (QObject::tr ("Reading class doc from ")) << path () << ": " << tl::to_string (QObject::tr ("Cannot find super class: ")) << super_cls_name; + tl::error << tl::to_string (tr ("Reading class doc from ")) << path () << ": " << tl::to_string (tr ("Cannot find super class: ")) << super_cls_name; return; } } @@ -986,7 +985,7 @@ void Macro::install_doc () const } else if (ex.test ("@method") || (st = ex.test ("@static_method")) == true) { if (cls == 0) { - tl::error << tl::to_string (QObject::tr ("Reading class doc from ")) << path () << ": " << tl::to_string (QObject::tr ("@method without preceding @class")); + tl::error << tl::to_string (tr ("Reading class doc from ")) << path () << ": " << tl::to_string (tr ("@method without preceding @class")); } else { std::string n; @@ -1082,932 +1081,5 @@ int Macro::run () const return 0; } -// ---------------------------------------------------------------------- - -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 { - emit about_to_change (); - } -} - -void MacroCollection::on_menu_needs_update () -{ - emit menu_needs_update (); -} - -void MacroCollection::on_changed () -{ - // Note: it is very important that each on_changed occurs after exactly one begin_changes. - // (See #459 for example) - emit changed (); - on_macro_collection_changed (this); -} - -void MacroCollection::on_macro_collection_changed (MacroCollection *mc) -{ - if (mp_parent) { - mp_parent->on_macro_collection_changed (mc); - } else { - emit macro_collection_changed (mc); - } -} - -void MacroCollection::on_child_deleted (MacroCollection *mc) -{ - emit child_deleted (mc); - on_macro_collection_deleted (mc); -} - -void MacroCollection::on_macro_collection_deleted (MacroCollection *mc) -{ - if (mp_parent) { - mp_parent->on_macro_collection_deleted (mc); - } else { - emit macro_collection_deleted (mc); - } -} - -void MacroCollection::on_macro_deleted_here (Macro *macro) -{ - emit macro_deleted_here (macro); - on_macro_deleted (macro); -} - -void MacroCollection::on_macro_deleted (Macro *macro) -{ - if (mp_parent) { - mp_parent->on_macro_deleted (macro); - } else { - emit macro_deleted (macro); - } -} - -void MacroCollection::on_macro_changed (Macro *macro) -{ - if (mp_parent) { - mp_parent->on_macro_changed (macro); - } else { - emit macro_changed (macro); - } -} - -void MacroCollection::collect_used_nodes (std::set ¯os, std::set ¯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 ::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 ::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 ::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 ::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::to_string (QFileInfo (QDir (tl::to_qstring (mp_parent->path ())), tl::to_qstring (m_path)).filePath ()); - } 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 &path, const std::string &cat, bool readonly, bool force_create) -{ - if (! path.empty () && path[0] == ':') { - readonly = true; - } else { - - QFileInfo file_info (tl::to_qstring (path)); - - if (! file_info.exists ()) { - - // 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: " << path; - } - return 0; - - } else { - - if (tl::verbosity () >= 20) { - tl::log << "Folder does not exist yet - trying to create it: " << path; - } - if (! QDir::root ().mkpath (file_info.absoluteFilePath ())) { - if (tl::verbosity () >= 10) { - tl::error << "Unable to create folder path: " << path; - } - return 0; - } - } - - file_info.refresh (); - - } - - if (! file_info.isDir ()) { - if (tl::verbosity () >= 10) { - tl::error << "Folder is not a directory: " << path; - } - return 0; - } - - QString cp = file_info.canonicalFilePath (); - if (cp.isEmpty ()) { - return 0; - } - - for (child_iterator f = m_folders.begin (); f != m_folders.end (); ++f) { - // skip, if that folder is in the collection already - if (QFileInfo (tl::to_qstring (f->first)).canonicalFilePath () == cp) { - return 0; - } - } - - if (! readonly && ! file_info.isWritable ()) { - readonly = true; - if (tl::verbosity () >= 20) { - tl::log << "Folder is read-only: " << path; - } - } - - } - - begin_changes (); - - MacroCollection *mc = m_folders.insert (std::make_pair (path, new MacroCollection ())).first->second; - mc->set_name (path); - mc->set_description (description); - mc->set_category (cat); - mc->set_readonly (readonly); - mc->scan (path); - mc->set_parent (this); - - on_changed (); - on_macro_changed (0); - - return mc; -} - -void MacroCollection::rescan () -{ - for (std::map ::const_iterator m = m_folders.begin (); m != m_folders.end (); ++m) { - m->second->scan (m->first); - } -} - -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; - }; - -} - -void MacroCollection::scan (const std::string &path) -{ - if (tl::verbosity () >= 20) { - tl::info << "Scanning macro path " << path << " (readonly=" << m_readonly << ")"; - } - - if (! path.empty () && path[0] == ':') { - - ResourceWithChildren res (tl::to_qstring (path)); - QStringList children = res.children (); - children.sort (); - - for (QStringList::const_iterator c = children.begin (); c != children.end (); ++c) { - - std::string url = path + "/" + tl::to_string (*c); - QResource res (tl::to_qstring (url)); - if (res.size () > 0) { - - QByteArray data; -#if QT_VERSION >= 0x60000 - if (res.compressionAlgorithm () == QResource::ZlibCompression) { -#else - if (res.isCompressed ()) { -#endif - data = qUncompress ((const unsigned char *)res.data (), (int)res.size ()); - } else { - data = QByteArray ((const char *)res.data (), (int)res.size ()); - } - - try { - - Macro::Format format = Macro::NoFormat; - Macro::Interpreter interpreter = Macro::None; - std::string dsl_name; - bool autorun = false; - - if (Macro::format_from_suffix (tl::to_string (*c), interpreter, dsl_name, autorun, format)) { - - std::string n = tl::to_string (QFileInfo (*c).baseName ()); - - 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 = m_macros.insert (std::make_pair (n, new Macro ()))->second; - 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_string (std::string (data.constData (), data.size ()), url); - m->set_readonly (m_readonly); - m->reset_modified (); - m->set_is_file (); - m->set_parent (this); - } - - } - - } catch (tl::Exception &ex) { - tl::error << "Reading " << url << ": " << ex.msg (); - } - - } - - } - - } else { - - QDir dir (tl::to_qstring (path)); - QStringList filters; - filters << QString::fromUtf8 ("*.lym"); - filters << QString::fromUtf8 ("*.txt"); - // TODO: should be either *.rb or *.python, depending on the category. - // Right now we rely on the folders not containing foreign files. - filters << QString::fromUtf8 ("*.rb"); - filters << QString::fromUtf8 ("*.py"); - - // add the suffixes in the DSL interpreter declarations - for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { - if (! cls->suffix ().empty ()) { - filters << tl::to_qstring ("*." + cls->suffix ()); - } - } - - QStringList files = dir.entryList (filters, QDir::Files); - for (QStringList::ConstIterator f = files.begin (); f != files.end (); ++f) { - - std::unique_ptr new_macro; - - try { - - std::string n = tl::to_string (QFileInfo (*f).completeBaseName ()); - std::string mp = tl::to_string (dir.absoluteFilePath (*f)); - - Macro::Format format = Macro::NoFormat; - Macro::Interpreter interpreter = Macro::None; - std::string dsl_name; - bool autorun = false; - - if (Macro::format_from_suffix (tl::to_string (*f), 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_format (format); - m->set_autorun_default (autorun); - m->set_autorun (autorun); - m->set_interpreter (interpreter); - m->set_dsl_interpreter (dsl_name); - m->set_name (n); - m->load_from (mp); - m->reset_modified (); - m->set_readonly (m_readonly); - 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 " << tl::to_string (*f) << " in " << path << ": " << ex.msg (); - } - - } - - QStringList folders = dir.entryList (QDir::Dirs | QDir::NoDotAndDotDot); - for (QStringList::ConstIterator f = folders.begin (); f != folders.end (); ++f) { - - try { - - std::string n = tl::to_string (*f); - MacroCollection *&mc = m_folders.insert (std::make_pair (n, (MacroCollection *) 0)).first->second; - if (! mc) { - mc = new MacroCollection (); - mc->set_name (n); - mc->set_virtual_mode (NotVirtual); - bool ro = (m_readonly || ! QFileInfo (dir.filePath (*f)).isWritable ()); - mc->set_readonly (ro); - mc->scan (tl::to_string (dir.filePath (*f))); - mc->set_parent (this); - } - - } catch (tl::Exception &ex) { - tl::error << 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; - } - QFile f (tl::to_qstring (path ())); - begin_changes (); - if (! f.rename (QFileInfo (QDir (tl::to_qstring (mp_parent->path ())), tl::to_qstring (n)).filePath ())) { - 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 && ! QDir (tl::to_qstring (path ())).mkdir (tl::to_qstring (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) -{ - QDir d (tl::to_qstring (path ())); - QDir dd = QFileInfo (tl::to_qstring (m->path ())).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. - QDir dm (tl::to_qstring (m->dir ())); - while (true) { - - std::string folder_name = tl::to_string (dm.dirName ()); - if (! dm.cdUp ()) { - 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_parent (this); - on_changed (); - return mc->add (m); - } - - } - - } - - return false; -} - -bool MacroCollection::del () -{ - if (tl::verbosity () >= 20) { - tl::info << "Deleting macro folder " << path (); - } - return QDir ().rmdir (tl::to_qstring (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 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::iterator m = folders_to_delete.begin (); m != folders_to_delete.end (); ++m) { - ret = true; - sync_macros (*m, 0, safe); - current->erase (*m); - } - - std::vector 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::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 *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 *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 *already_executed) -{ - autorun_for (*this, false, already_executed); -} - -void MacroCollection::autorun_early (std::set *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); - } -} - } diff --git a/src/lym/lym/lymMacro.h b/src/lym/lym/lymMacro.h index 619be68a6..29ce328ea 100644 --- a/src/lym/lym/lymMacro.h +++ b/src/lym/lym/lymMacro.h @@ -31,7 +31,9 @@ #include #include -#include +#if defined(HAVE_QT) +# include +#endif namespace lym { @@ -55,11 +57,15 @@ class MacroCollection; * a macro can be bound to an arbitrary interpreter and decides * by itself which interpreter to use. */ -class LYM_PUBLIC Macro - : public QObject, +class LYM_PUBLIC Macro : +#if defined(HAVE_QT) + public QObject, +#endif public tl::Object { -Q_OBJECT +#if defined(HAVE_QT) +Q_OBJECT +#endif public: /** @@ -587,12 +593,14 @@ public: return !(*this == other); } +#if defined(HAVE_QT) signals: /** * @brief This signal is sent when the macro changes */ void changed (); - +#endif + private: friend class MacroCollection; @@ -646,517 +654,6 @@ private: Macro &operator= (const Macro &d); }; -/** - * @brief Represents a collection of macros - * - * A collection is representing a set of macros, usually associated with - * a folder containing *.lym, *.rb or other script files. - */ -class LYM_PUBLIC MacroCollection - : public QObject -{ -Q_OBJECT - -public: - typedef std::multimap ::iterator iterator; - typedef std::multimap ::const_iterator const_iterator; - typedef std::map ::iterator child_iterator; - typedef std::map ::const_iterator const_child_iterator; - - /** - * @brief Some constants for virtual_mode - */ - enum FolderType { - NotVirtual = 0, - ProjectFolder = 1, - TechFolder = 2, - SaltFolder = 3 - }; - - /** - * @brief Constructor - * - * The default constructor create - */ - MacroCollection (); - - /** - * @brief Destructor - */ - ~MacroCollection (); - - /** - * @brief Add a folder (will also scan the folder) - * - * @return A pointer to the new collection if successful - * - * If force_create is true (the default), the folder will be created if it does not - * exist yet. On error, 0 is returned. - */ - MacroCollection *add_folder (const std::string &description, const std::string &path, const std::string &category, bool readonly, bool force_create = true); - - /** - * @brief Gets the category tag of the collection - * - * A category tag can be used to categorize the collections. For example, DRC categories are handled differently - * from the other categories. - */ - const std::string &category () const - { - return m_category; - } - - /** - * @brief Sets the category tags - */ - void set_category (const std::string &d) - { - m_category = d; - } - - /** - * @brief Collect all Macro and MacroCollection objects inside a tree starting from this collection - */ - void collect_used_nodes(std::set ¯os, std::set ¯o_collections); - - /** - * @brief Saves all macros in the collection - * - * Saves only those macros that have is_modified and whose path is set. - */ - void save (); - - /** - * @brief Delete the original folder (the directory behind the macro) - * - * Returns true if the folder was deleted successfully. - * The folder cannot be deleted if it contains any files, also some that are not listed because - * they don't end with .lym, .rb or similar. - */ - bool del (); - - /** - * @brief Gets the name of the collection - * - * For virtual collections this is the path. - */ - std::string name () const - { - return m_path; - } - - /** - * @brief Gets the path of the folder representing that collection - */ - std::string path () const; - - /** - * @brief Returns the parent of the macro collection - * - * Returns 0, if there is no parent of this collection (this is the root) - */ - lym::MacroCollection *parent () - { - return mp_parent; - } - - /** - * @brief Returns the parent of the macro collection (const version) - * - * Returns 0, if there is no parent of this collection (this is the root) - */ - const lym::MacroCollection *parent () const - { - return mp_parent; - } - - /** - * @brief Returns != 0, if the macro collection is a virtual node - * - * A virtual node does not correspond to a location in the file system. - * A virtual node cannot have macros but only children. - * The return value indicates the kind of virtual use. - */ - int virtual_mode () const - { - return m_virtual_mode; - } - - /** - * @brief Sets the virtual mode - * - * See virtual_mode for details about the virtual mode. - */ - void set_virtual_mode (int m) - { - m_virtual_mode = m; - } - - /** - * @brief Gets a value indicating whether the macro collection is readonly - */ - bool is_readonly () const - { - return m_readonly; - } - - /** - * @brief Sets a value indicating whether the macro is readonly - * In contrast to the private \set_readonly method, this version delivers a "changed" signal when - * the flag changed. - */ - void make_readonly (bool f); - - /** - * @brief Gets the macro collection's description text - */ - const std::string &description () const - { - return m_description; - } - - /** - * @brief Sets the description - */ - void set_description (const std::string &d) - { - m_description = d; - } - - /** - * @brief Gets the display string - */ - std::string display_string () const; - - /** - * @brief Rename a Macro - * - * Renames the macro. If the macro is a file, the file will be renamed as well. - * This method will return true, if the rename was successful. - */ - bool rename (const std::string &n); - - /** - * @brief Adds a macro to the collection - * - * If a macro with the name of the new macro already exists, it is replaced - * (like in the file system). This method will traverse the tree to find - * the location of the macro using the path information of the macro and insert - * the macro there. - * - * The collection becomes the owner of the object passed to this method - * - * @return true, if the macro could be added successfully. - */ - bool add (lym::Macro *m); - - /** - * @brief Adds a macro in an unspecific way - * - * "unspecific" means that the path is not looked up - the macro is - * added irregardless whether the path matches or not. - * This is a way to build macro collections without connection - * to some file system point. - */ - void add_unspecific (lym::Macro *m); - - /** - * @brief Empties the collection - * Note: only the unspecific on_changed event is generated. - */ - void clear (); - - /** - * @brief Erases the given macro from the list - * - * This does not remove the file but just remove the macro object. - * This will also delete the macro object. - */ - void erase (lym::Macro *m); - - /** - * @brief Erases the entry with the given iterator - */ - void erase (iterator i); - - /** - * @brief Erases the given macro collection from the list of child collections - * - * This does not remove the directory but just removes the macro collection object. - * This will also delete the macro collection object. - */ - void erase (lym::MacroCollection *m); - - /** - * @brief Erases the folder with the given iterator - */ - void erase (child_iterator i); - - /** - * @brief Creates a new macro in that collection (with a new name) - * - * If a name is given, it is used as a prefix to create a unique name for a macro with that format. - */ - lym::Macro *create (const char *name = 0, Macro::Format format = Macro::NoFormat); - - /** - * @brief Creates a new macro collection in that collection (with a new name) - * - * If a name is given, it is used as a prefix to create a unique name. - * This method will also create the directory for this folder. - * If not successful, it will return 0. - */ - lym::MacroCollection *create_folder (const char *name = 0, bool mkdir = true); - - /** - * @brief Gets the begin iterator of the macros - */ - iterator begin () - { - return m_macros.begin (); - } - - /** - * @brief Gets the end iterator of the macros - */ - iterator end () - { - return m_macros.end (); - } - - /** - * @brief Gets the begin iterator of the macros (const version) - */ - const_iterator begin () const - { - return m_macros.begin (); - } - - /** - * @brief Gets the end iterator of the macros (const version) - */ - const_iterator end () const - { - return m_macros.end (); - } - - /** - * @brief Gets the begin iterator of the folders - */ - child_iterator begin_children () - { - return m_folders.begin (); - } - - /** - * @brief Gets the end iterator of the folders - */ - child_iterator end_children () - { - return m_folders.end (); - } - - /** - * @brief Gets the begin iterator of the folders (const version) - */ - const_child_iterator begin_children () const - { - return m_folders.begin (); - } - - /** - * @brief Gets the end iterator of the folders (const version) - */ - const_child_iterator end_children () const - { - return m_folders.end (); - } - - /** - * @brief Gets a macro by name - * - * If no macro with that name exists, this method will return 0. - */ - Macro *macro_by_name (const std::string &name, Macro::Format format); - - /** - * @brief Gets a macro by name - * - * If no macro with that name exists, this method will return 0. - */ - const Macro *macro_by_name (const std::string &name, Macro::Format format) const; - - /** - * @brief Gets a folder by name - * - * If no folder with that name exists, this method will return 0. - */ - MacroCollection *folder_by_name (const std::string &name); - - /** - * @brief Gets a folder by name - * - * If no folder with that name exists, this method will return 0. - */ - const MacroCollection *folder_by_name (const std::string &name) const; - - /** - * @brief Finds a macro by path - * - * This method is called from the root collection and delivers the macro which - * matches the given path or 0. - */ - lym::Macro *find_macro (const std::string &path); - - /** - * @brief Returns true, if the collection has an autorun macro - */ - bool has_autorun () const; - - /** - * @brief Runs all macros marked with auto-run - */ - void autorun (std::set *already_executed = 0); - - /** - * @brief Returns true, if the collection has an early autorun macro - */ - bool has_autorun_early () const; - - /** - * @brief Runs all macros marked with early auto-run - */ - void autorun_early (std::set *already_executed = 0); - - /** - * @brief Redo the scan (will add new files or folders) - * - * This method must be called on root. - */ - void rescan (); - - /** - * @brief Reloads the macro collection - * - * This method is similar to rescan, but it will also remove folders and macros. - * In safe mode (safe = true), modified macros won't be overwritten. - */ - void reload (bool safe); - - /** - * @brief Gets the root of the macro hierarchy corresponding to the configuration space - */ - static MacroCollection &root (); - - /** - * @brief Dump the macro tree (for debugging) - */ - void dump (int l = 0); - -signals: - /** - * @brief This signal is sent when the collection changes - */ - void changed (); - - /** - * @brief This signal is sent by collection when a child collection is deleted in this collection - */ - void child_deleted (lym::MacroCollection *); - - /** - * @brief This signal is sent by the root object when a macro collection is deleted - */ - void macro_collection_deleted (lym::MacroCollection *); - - /** - * @brief This signal is sent by collection when a macro is deleted in this collection - */ - void macro_deleted_here (lym::Macro *); - - /** - * @brief This signal is sent by the root object when a macro is deleted - */ - void macro_deleted (lym::Macro *); - - /** - * @brief This signal is sent by the root object when a macro changes - * - * This signal is only emitted by the root, but it may originate from a - * macro inside the tree. - */ - void macro_changed (lym::Macro *); - - /** - * @brief This signal is sent by the root object when a macro collection changes - * - * This signal is only emitted by the root, but it may originate from a - * macro collection inside the tree. - */ - void macro_collection_changed (lym::MacroCollection *); - - /** - * @brief This signal is sent by the root object befor the macro collection changes - */ - void about_to_change (); - - /** - * @brief This signal is emitted from the collection root if the menu needs to be updated - */ - void menu_needs_update (); - -private: - friend class Macro; - - std::string m_path; - std::string m_description; - std::string m_category; - std::multimap m_macros; - std::map m_folders; - lym::MacroCollection *mp_parent; - int m_virtual_mode; - bool m_readonly; - - void on_child_deleted (MacroCollection *mc); - void on_macro_collection_deleted (MacroCollection *mc); - void on_macro_deleted_here (Macro *macro); - void on_macro_deleted (Macro *macro); - void on_macro_changed (Macro *macro); - void on_macro_collection_changed (MacroCollection *mc); - void on_changed (); - void on_menu_needs_update (); - - /** - * @brief Scans a folder creating the macro collection - */ - void scan (const std::string &path); - - void rename_macro (Macro *macro, const std::string &new_name); - - void begin_changes (); - - void set_name (const std::string &n) - { - m_path = n; - } - - void set_parent (lym::MacroCollection *parent) - { - mp_parent = parent; - } - - void set_readonly (bool f) - { - m_readonly = f; - } - - void do_clear (); - - // no copying - MacroCollection (const MacroCollection &d); - MacroCollection &operator= (const MacroCollection &d); -}; - } #endif diff --git a/src/lym/lym/lymMacroCollection.cc b/src/lym/lym/lymMacroCollection.cc new file mode 100644 index 000000000..5ea264937 --- /dev/null +++ b/src/lym/lym/lymMacroCollection.cc @@ -0,0 +1,987 @@ + +/* + + 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" + +#if defined(HAVE_QT) + +#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 "rba.h" +#include "pya.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +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 { + emit about_to_change (); + } +} + +void MacroCollection::on_menu_needs_update () +{ + emit menu_needs_update (); +} + +void MacroCollection::on_changed () +{ + // Note: it is very important that each on_changed occurs after exactly one begin_changes. + // (See #459 for example) + emit changed (); + on_macro_collection_changed (this); +} + +void MacroCollection::on_macro_collection_changed (MacroCollection *mc) +{ + if (mp_parent) { + mp_parent->on_macro_collection_changed (mc); + } else { + emit macro_collection_changed (mc); + } +} + +void MacroCollection::on_child_deleted (MacroCollection *mc) +{ + emit child_deleted (mc); + on_macro_collection_deleted (mc); +} + +void MacroCollection::on_macro_collection_deleted (MacroCollection *mc) +{ + if (mp_parent) { + mp_parent->on_macro_collection_deleted (mc); + } else { + emit macro_collection_deleted (mc); + } +} + +void MacroCollection::on_macro_deleted_here (Macro *macro) +{ + emit macro_deleted_here (macro); + on_macro_deleted (macro); +} + +void MacroCollection::on_macro_deleted (Macro *macro) +{ + if (mp_parent) { + mp_parent->on_macro_deleted (macro); + } else { + emit macro_deleted (macro); + } +} + +void MacroCollection::on_macro_changed (Macro *macro) +{ + if (mp_parent) { + mp_parent->on_macro_changed (macro); + } else { + emit macro_changed (macro); + } +} + +void MacroCollection::collect_used_nodes (std::set ¯os, std::set ¯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 ::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 ::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 ::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 ::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::to_string (QFileInfo (QDir (tl::to_qstring (mp_parent->path ())), tl::to_qstring (m_path)).filePath ()); + } 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 &path, const std::string &cat, bool readonly, bool force_create) +{ + if (! path.empty () && path[0] == ':') { + readonly = true; + } else { + + QFileInfo file_info (tl::to_qstring (path)); + + if (! file_info.exists ()) { + + // 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: " << path; + } + return 0; + + } else { + + if (tl::verbosity () >= 20) { + tl::log << "Folder does not exist yet - trying to create it: " << path; + } + if (! QDir::root ().mkpath (file_info.absoluteFilePath ())) { + if (tl::verbosity () >= 10) { + tl::error << "Unable to create folder path: " << path; + } + return 0; + } + } + + file_info.refresh (); + + } + + if (! file_info.isDir ()) { + if (tl::verbosity () >= 10) { + tl::error << "Folder is not a directory: " << path; + } + return 0; + } + + QString cp = file_info.canonicalFilePath (); + if (cp.isEmpty ()) { + return 0; + } + + for (child_iterator f = m_folders.begin (); f != m_folders.end (); ++f) { + // skip, if that folder is in the collection already + if (QFileInfo (tl::to_qstring (f->first)).canonicalFilePath () == cp) { + return 0; + } + } + + if (! readonly && ! file_info.isWritable ()) { + readonly = true; + if (tl::verbosity () >= 20) { + tl::log << "Folder is read-only: " << path; + } + } + + } + + begin_changes (); + + MacroCollection *mc = m_folders.insert (std::make_pair (path, new MacroCollection ())).first->second; + mc->set_name (path); + mc->set_description (description); + mc->set_category (cat); + mc->set_readonly (readonly); + mc->scan (path); + mc->set_parent (this); + + on_changed (); + on_macro_changed (0); + + return mc; +} + +void MacroCollection::rescan () +{ + for (std::map ::const_iterator m = m_folders.begin (); m != m_folders.end (); ++m) { + m->second->scan (m->first); + } +} + +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; + }; + +} + +void MacroCollection::scan (const std::string &path) +{ + if (tl::verbosity () >= 20) { + tl::info << "Scanning macro path " << path << " (readonly=" << m_readonly << ")"; + } + + if (! path.empty () && path[0] == ':') { + + ResourceWithChildren res (tl::to_qstring (path)); + QStringList children = res.children (); + children.sort (); + + for (QStringList::const_iterator c = children.begin (); c != children.end (); ++c) { + + std::string url = path + "/" + tl::to_string (*c); + QResource res (tl::to_qstring (url)); + if (res.size () > 0) { + + QByteArray data; +#if QT_VERSION >= 0x60000 + if (res.compressionAlgorithm () == QResource::ZlibCompression) { +#else + if (res.isCompressed ()) { +#endif + data = qUncompress ((const unsigned char *)res.data (), (int)res.size ()); + } else { + data = QByteArray ((const char *)res.data (), (int)res.size ()); + } + + try { + + Macro::Format format = Macro::NoFormat; + Macro::Interpreter interpreter = Macro::None; + std::string dsl_name; + bool autorun = false; + + if (Macro::format_from_suffix (tl::to_string (*c), interpreter, dsl_name, autorun, format)) { + + std::string n = tl::to_string (QFileInfo (*c).baseName ()); + + 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 = m_macros.insert (std::make_pair (n, new Macro ()))->second; + 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_string (std::string (data.constData (), data.size ()), url); + m->set_readonly (m_readonly); + m->reset_modified (); + m->set_is_file (); + m->set_parent (this); + } + + } + + } catch (tl::Exception &ex) { + tl::error << "Reading " << url << ": " << ex.msg (); + } + + } + + } + + } else { + + QDir dir (tl::to_qstring (path)); + QStringList filters; + filters << QString::fromUtf8 ("*.lym"); + filters << QString::fromUtf8 ("*.txt"); + // TODO: should be either *.rb or *.python, depending on the category. + // Right now we rely on the folders not containing foreign files. + filters << QString::fromUtf8 ("*.rb"); + filters << QString::fromUtf8 ("*.py"); + + // add the suffixes in the DSL interpreter declarations + for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { + if (! cls->suffix ().empty ()) { + filters << tl::to_qstring ("*." + cls->suffix ()); + } + } + + QStringList files = dir.entryList (filters, QDir::Files); + for (QStringList::ConstIterator f = files.begin (); f != files.end (); ++f) { + + std::unique_ptr new_macro; + + try { + + std::string n = tl::to_string (QFileInfo (*f).completeBaseName ()); + std::string mp = tl::to_string (dir.absoluteFilePath (*f)); + + Macro::Format format = Macro::NoFormat; + Macro::Interpreter interpreter = Macro::None; + std::string dsl_name; + bool autorun = false; + + if (Macro::format_from_suffix (tl::to_string (*f), 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_format (format); + m->set_autorun_default (autorun); + m->set_autorun (autorun); + m->set_interpreter (interpreter); + m->set_dsl_interpreter (dsl_name); + m->set_name (n); + m->load_from (mp); + m->reset_modified (); + m->set_readonly (m_readonly); + 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 " << tl::to_string (*f) << " in " << path << ": " << ex.msg (); + } + + } + + QStringList folders = dir.entryList (QDir::Dirs | QDir::NoDotAndDotDot); + for (QStringList::ConstIterator f = folders.begin (); f != folders.end (); ++f) { + + try { + + std::string n = tl::to_string (*f); + MacroCollection *&mc = m_folders.insert (std::make_pair (n, (MacroCollection *) 0)).first->second; + if (! mc) { + mc = new MacroCollection (); + mc->set_name (n); + mc->set_virtual_mode (NotVirtual); + bool ro = (m_readonly || ! QFileInfo (dir.filePath (*f)).isWritable ()); + mc->set_readonly (ro); + mc->scan (tl::to_string (dir.filePath (*f))); + mc->set_parent (this); + } + + } catch (tl::Exception &ex) { + tl::error << 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; + } + QFile f (tl::to_qstring (path ())); + begin_changes (); + if (! f.rename (QFileInfo (QDir (tl::to_qstring (mp_parent->path ())), tl::to_qstring (n)).filePath ())) { + 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 && ! QDir (tl::to_qstring (path ())).mkdir (tl::to_qstring (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) +{ + QDir d (tl::to_qstring (path ())); + QDir dd = QFileInfo (tl::to_qstring (m->path ())).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. + QDir dm (tl::to_qstring (m->dir ())); + while (true) { + + std::string folder_name = tl::to_string (dm.dirName ()); + if (! dm.cdUp ()) { + 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_parent (this); + on_changed (); + return mc->add (m); + } + + } + + } + + return false; +} + +bool MacroCollection::del () +{ + if (tl::verbosity () >= 20) { + tl::info << "Deleting macro folder " << path (); + } + return QDir ().rmdir (tl::to_qstring (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 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::iterator m = folders_to_delete.begin (); m != folders_to_delete.end (); ++m) { + ret = true; + sync_macros (*m, 0, safe); + current->erase (*m); + } + + std::vector 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::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 *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 *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 *already_executed) +{ + autorun_for (*this, false, already_executed); +} + +void MacroCollection::autorun_early (std::set *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); + } +} + +} + +#endif diff --git a/src/lym/lym/lymMacroCollection.h b/src/lym/lym/lymMacroCollection.h new file mode 100644 index 000000000..9df9f3d87 --- /dev/null +++ b/src/lym/lym/lymMacroCollection.h @@ -0,0 +1,594 @@ + +/* + + 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 + +*/ + + +#ifndef HDR_lymMacroCollection +#define HDR_lymMacroCollection + +#include "lymCommon.h" +#include "lymMacro.h" + +#if defined(HAVE_QT) + +#include +#include +#include + +#include + +namespace lym +{ + +/** + * @brief Represents a collection of macros + * + * A collection is representing a set of macros, usually associated with + * a folder containing *.lym, *.rb or other script files. + */ +class LYM_PUBLIC MacroCollection + : public QObject +{ +Q_OBJECT + +public: + typedef std::multimap ::iterator iterator; + typedef std::multimap ::const_iterator const_iterator; + typedef std::map ::iterator child_iterator; + typedef std::map ::const_iterator const_child_iterator; + + /** + * @brief Some constants for virtual_mode + */ + enum FolderType { + NotVirtual = 0, + ProjectFolder = 1, + TechFolder = 2, + SaltFolder = 3 + }; + + /** + * @brief Constructor + * + * The default constructor create + */ + MacroCollection (); + + /** + * @brief Destructor + */ + ~MacroCollection (); + + /** + * @brief Add a folder (will also scan the folder) + * + * @return A pointer to the new collection if successful + * + * If force_create is true (the default), the folder will be created if it does not + * exist yet. On error, 0 is returned. + */ + MacroCollection *add_folder (const std::string &description, const std::string &path, const std::string &category, bool readonly, bool force_create = true); + + /** + * @brief Gets the category tag of the collection + * + * A category tag can be used to categorize the collections. For example, DRC categories are handled differently + * from the other categories. + */ + const std::string &category () const + { + return m_category; + } + + /** + * @brief Sets the category tags + */ + void set_category (const std::string &d) + { + m_category = d; + } + + /** + * @brief Collect all Macro and MacroCollection objects inside a tree starting from this collection + */ + void collect_used_nodes(std::set ¯os, std::set ¯o_collections); + + /** + * @brief Saves all macros in the collection + * + * Saves only those macros that have is_modified and whose path is set. + */ + void save (); + + /** + * @brief Delete the original folder (the directory behind the macro) + * + * Returns true if the folder was deleted successfully. + * The folder cannot be deleted if it contains any files, also some that are not listed because + * they don't end with .lym, .rb or similar. + */ + bool del (); + + /** + * @brief Gets the name of the collection + * + * For virtual collections this is the path. + */ + std::string name () const + { + return m_path; + } + + /** + * @brief Gets the path of the folder representing that collection + */ + std::string path () const; + + /** + * @brief Returns the parent of the macro collection + * + * Returns 0, if there is no parent of this collection (this is the root) + */ + lym::MacroCollection *parent () + { + return mp_parent; + } + + /** + * @brief Returns the parent of the macro collection (const version) + * + * Returns 0, if there is no parent of this collection (this is the root) + */ + const lym::MacroCollection *parent () const + { + return mp_parent; + } + + /** + * @brief Returns != 0, if the macro collection is a virtual node + * + * A virtual node does not correspond to a location in the file system. + * A virtual node cannot have macros but only children. + * The return value indicates the kind of virtual use. + */ + int virtual_mode () const + { + return m_virtual_mode; + } + + /** + * @brief Sets the virtual mode + * + * See virtual_mode for details about the virtual mode. + */ + void set_virtual_mode (int m) + { + m_virtual_mode = m; + } + + /** + * @brief Gets a value indicating whether the macro collection is readonly + */ + bool is_readonly () const + { + return m_readonly; + } + + /** + * @brief Sets a value indicating whether the macro is readonly + * In contrast to the private \set_readonly method, this version delivers a "changed" signal when + * the flag changed. + */ + void make_readonly (bool f); + + /** + * @brief Gets the macro collection's description text + */ + const std::string &description () const + { + return m_description; + } + + /** + * @brief Sets the description + */ + void set_description (const std::string &d) + { + m_description = d; + } + + /** + * @brief Gets the display string + */ + std::string display_string () const; + + /** + * @brief Rename a Macro + * + * Renames the macro. If the macro is a file, the file will be renamed as well. + * This method will return true, if the rename was successful. + */ + bool rename (const std::string &n); + + /** + * @brief Adds a macro to the collection + * + * If a macro with the name of the new macro already exists, it is replaced + * (like in the file system). This method will traverse the tree to find + * the location of the macro using the path information of the macro and insert + * the macro there. + * + * The collection becomes the owner of the object passed to this method + * + * @return true, if the macro could be added successfully. + */ + bool add (lym::Macro *m); + + /** + * @brief Adds a macro in an unspecific way + * + * "unspecific" means that the path is not looked up - the macro is + * added irregardless whether the path matches or not. + * This is a way to build macro collections without connection + * to some file system point. + */ + void add_unspecific (lym::Macro *m); + + /** + * @brief Empties the collection + * Note: only the unspecific on_changed event is generated. + */ + void clear (); + + /** + * @brief Erases the given macro from the list + * + * This does not remove the file but just remove the macro object. + * This will also delete the macro object. + */ + void erase (lym::Macro *m); + + /** + * @brief Erases the entry with the given iterator + */ + void erase (iterator i); + + /** + * @brief Erases the given macro collection from the list of child collections + * + * This does not remove the directory but just removes the macro collection object. + * This will also delete the macro collection object. + */ + void erase (lym::MacroCollection *m); + + /** + * @brief Erases the folder with the given iterator + */ + void erase (child_iterator i); + + /** + * @brief Creates a new macro in that collection (with a new name) + * + * If a name is given, it is used as a prefix to create a unique name for a macro with that format. + */ + lym::Macro *create (const char *name = 0, Macro::Format format = Macro::NoFormat); + + /** + * @brief Creates a new macro collection in that collection (with a new name) + * + * If a name is given, it is used as a prefix to create a unique name. + * This method will also create the directory for this folder. + * If not successful, it will return 0. + */ + lym::MacroCollection *create_folder (const char *name = 0, bool mkdir = true); + + /** + * @brief Gets the begin iterator of the macros + */ + iterator begin () + { + return m_macros.begin (); + } + + /** + * @brief Gets the end iterator of the macros + */ + iterator end () + { + return m_macros.end (); + } + + /** + * @brief Gets the begin iterator of the macros (const version) + */ + const_iterator begin () const + { + return m_macros.begin (); + } + + /** + * @brief Gets the end iterator of the macros (const version) + */ + const_iterator end () const + { + return m_macros.end (); + } + + /** + * @brief Gets the begin iterator of the folders + */ + child_iterator begin_children () + { + return m_folders.begin (); + } + + /** + * @brief Gets the end iterator of the folders + */ + child_iterator end_children () + { + return m_folders.end (); + } + + /** + * @brief Gets the begin iterator of the folders (const version) + */ + const_child_iterator begin_children () const + { + return m_folders.begin (); + } + + /** + * @brief Gets the end iterator of the folders (const version) + */ + const_child_iterator end_children () const + { + return m_folders.end (); + } + + /** + * @brief Gets a macro by name + * + * If no macro with that name exists, this method will return 0. + */ + Macro *macro_by_name (const std::string &name, Macro::Format format); + + /** + * @brief Gets a macro by name + * + * If no macro with that name exists, this method will return 0. + */ + const Macro *macro_by_name (const std::string &name, Macro::Format format) const; + + /** + * @brief Gets a folder by name + * + * If no folder with that name exists, this method will return 0. + */ + MacroCollection *folder_by_name (const std::string &name); + + /** + * @brief Gets a folder by name + * + * If no folder with that name exists, this method will return 0. + */ + const MacroCollection *folder_by_name (const std::string &name) const; + + /** + * @brief Finds a macro by path + * + * This method is called from the root collection and delivers the macro which + * matches the given path or 0. + */ + lym::Macro *find_macro (const std::string &path); + + /** + * @brief Returns true, if the collection has an autorun macro + */ + bool has_autorun () const; + + /** + * @brief Runs all macros marked with auto-run + */ + void autorun (std::set *already_executed = 0); + + /** + * @brief Returns true, if the collection has an early autorun macro + */ + bool has_autorun_early () const; + + /** + * @brief Runs all macros marked with early auto-run + */ + void autorun_early (std::set *already_executed = 0); + + /** + * @brief Redo the scan (will add new files or folders) + * + * This method must be called on root. + */ + void rescan (); + + /** + * @brief Reloads the macro collection + * + * This method is similar to rescan, but it will also remove folders and macros. + * In safe mode (safe = true), modified macros won't be overwritten. + */ + void reload (bool safe); + + /** + * @brief Gets the root of the macro hierarchy corresponding to the configuration space + */ + static MacroCollection &root (); + + /** + * @brief Dump the macro tree (for debugging) + */ + void dump (int l = 0); + +signals: + /** + * @brief This signal is sent when the collection changes + */ + void changed (); + + /** + * @brief This signal is sent by collection when a child collection is deleted in this collection + */ + void child_deleted (lym::MacroCollection *); + + /** + * @brief This signal is sent by the root object when a macro collection is deleted + */ + void macro_collection_deleted (lym::MacroCollection *); + + /** + * @brief This signal is sent by collection when a macro is deleted in this collection + */ + void macro_deleted_here (lym::Macro *); + + /** + * @brief This signal is sent by the root object when a macro is deleted + */ + void macro_deleted (lym::Macro *); + + /** + * @brief This signal is sent by the root object when a macro changes + * + * This signal is only emitted by the root, but it may originate from a + * macro inside the tree. + */ + void macro_changed (lym::Macro *); + + /** + * @brief This signal is sent by the root object when a macro collection changes + * + * This signal is only emitted by the root, but it may originate from a + * macro collection inside the tree. + */ + void macro_collection_changed (lym::MacroCollection *); + + /** + * @brief This signal is sent by the root object befor the macro collection changes + */ + void about_to_change (); + + /** + * @brief This signal is emitted from the collection root if the menu needs to be updated + */ + void menu_needs_update (); + +private: + friend class Macro; + + std::string m_path; + std::string m_description; + std::string m_category; + std::multimap m_macros; + std::map m_folders; + lym::MacroCollection *mp_parent; + int m_virtual_mode; + bool m_readonly; + + void on_child_deleted (MacroCollection *mc); + void on_macro_collection_deleted (MacroCollection *mc); + void on_macro_deleted_here (Macro *macro); + void on_macro_deleted (Macro *macro); + void on_macro_changed (Macro *macro); + void on_macro_collection_changed (MacroCollection *mc); + void on_changed (); + void on_menu_needs_update (); + + /** + * @brief Scans a folder creating the macro collection + */ + void scan (const std::string &path); + + void rename_macro (Macro *macro, const std::string &new_name); + + void begin_changes (); + + void set_name (const std::string &n) + { + m_path = n; + } + + void set_parent (lym::MacroCollection *parent) + { + mp_parent = parent; + } + + void set_readonly (bool f) + { + m_readonly = f; + } + + void do_clear (); + + // no copying + MacroCollection (const MacroCollection &d); + MacroCollection &operator= (const MacroCollection &d); +}; + +} + +#else // without QT: + +#include + +namespace lym +{ + +/** + * @brief Dummy implementation for Qt-less builds. + * + * This dummy implementation does not provide any services but acts as a dummy anchor + * for lym::Macro. + * + * Without Qt, the MacroCollection does not make much sense as there is no + * application specific file system without an Application object. + */ +class LYM_PUBLIC MacroCollection +{ +public: + MacroCollection () + { + // .. nothing yet .. + } + + std::string path () const + { + return std::string (); + } + + void rename_macro (lym::Macro *, const std::string &) + { + // .. nothing yet .. + } +}; + +} + +#endif + +#endif + diff --git a/src/lym/lym/lymMacroInterpreter.cc b/src/lym/lym/lymMacroInterpreter.cc index 77f437345..532bff93d 100644 --- a/src/lym/lym/lymMacroInterpreter.cc +++ b/src/lym/lym/lymMacroInterpreter.cc @@ -29,13 +29,15 @@ #include "tlClassRegistry.h" #include "tlInclude.h" +#include + namespace lym { tl::Executable * MacroInterpreter::executable (const lym::Macro *) const { - throw tl::Exception (tl::to_string (QObject::tr ("executable() implementation missing for DSL interpreter"))); + throw tl::Exception (tl::to_string (tr ("executable() implementation missing for DSL interpreter"))); } bool @@ -131,7 +133,7 @@ MacroInterpreter::execute_macro (const lym::Macro *macro) } } - throw tl::Exception (tl::to_string (QObject::tr ("No interpreter registered for DSL type '")) + macro->dsl_interpreter () + "'"); + throw tl::Exception (tl::to_string (tr ("No interpreter registered for DSL type '")) + macro->dsl_interpreter () + "'"); } std::string diff --git a/src/plugins/tools/view_25d/view_25d.pro b/src/plugins/tools/view_25d/view_25d.pro index 138f0890d..3fa284d8a 100644 --- a/src/plugins/tools/view_25d/view_25d.pro +++ b/src/plugins/tools/view_25d/view_25d.pro @@ -1,12 +1,16 @@ TEMPLATE = subdirs -contains(QT_CONFIG, opengl) { +!equals(HAVE_QT, "0") { + + contains(QT_CONFIG, opengl) { + + greaterThan(QT_MAJOR_VERSION, 4) { + SUBDIRS = lay_plugin unit_tests + } + + unit_tests.depends += lay_plugin - greaterThan(QT_MAJOR_VERSION, 4) { - SUBDIRS = lay_plugin unit_tests } - unit_tests.depends += lay_plugin - } diff --git a/src/tl/unit_tests/tlFileSystemWatcherTests.cc b/src/tl/unit_tests/tlFileSystemWatcherTests.cc index fda2a0157..da732dd60 100644 --- a/src/tl/unit_tests/tlFileSystemWatcherTests.cc +++ b/src/tl/unit_tests/tlFileSystemWatcherTests.cc @@ -20,6 +20,7 @@ */ +#if defined(HAVE_QT) #include "tlFileSystemWatcher.h" #include "tlString.h" @@ -218,3 +219,4 @@ TEST(3) EXPECT_EQ (changed_spy.count (), 0); } +#endif