mirror of https://github.com/KLayout/klayout.git
1427 lines
44 KiB
C++
1427 lines
44 KiB
C++
|
|
/*
|
|
|
|
KLayout Layout Viewer
|
|
Copyright (C) 2006-2017 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 "layApplication.h"
|
|
#include "laySignalHandler.h"
|
|
#include "laybasicConfig.h"
|
|
#include "layConfig.h"
|
|
#include "layMainWindow.h"
|
|
#include "layMacroEditorDialog.h"
|
|
#include "layNativePlugin.h"
|
|
#include "layVersion.h"
|
|
#include "layMacro.h"
|
|
#include "laySignalHandler.h"
|
|
#include "layRuntimeErrorForm.h"
|
|
#include "layProgress.h"
|
|
#include "layTextProgress.h"
|
|
#include "layBackgroundAwareTreeStyle.h"
|
|
#include "layMacroController.h"
|
|
#include "layTechnologyController.h"
|
|
#include "laySaltController.h"
|
|
#include "gtf.h"
|
|
#include "gsiDecl.h"
|
|
#include "gsiInterpreter.h"
|
|
#include "rba.h"
|
|
#include "pya.h"
|
|
#include "rdb.h"
|
|
#include "dbStatic.h"
|
|
#include "dbLibrary.h"
|
|
#include "dbLibraryManager.h"
|
|
#include "tlExceptions.h"
|
|
#include "tlException.h"
|
|
#include "tlAssert.h"
|
|
#include "tlLog.h"
|
|
#include "tlString.h"
|
|
#include "tlSystemPaths.h"
|
|
#include "tlExpression.h"
|
|
#include "tlExceptions.h"
|
|
#include "tlInternational.h"
|
|
#include "tlArch.h"
|
|
|
|
#include <QIcon>
|
|
#include <QDir>
|
|
#include <QFileInfo>
|
|
#include <QFile>
|
|
#include <QAction>
|
|
#include <QMessageBox>
|
|
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <algorithm>
|
|
|
|
#ifdef _WIN32
|
|
# include <windows.h>
|
|
#else
|
|
# include <dlfcn.h>
|
|
#endif
|
|
|
|
namespace lay
|
|
{
|
|
|
|
// --------------------------------------------------------------------------------
|
|
// Exception handlers
|
|
|
|
static void ui_exception_handler_tl (const tl::Exception &ex, QWidget *parent)
|
|
{
|
|
// Prevents severe side effects if there are pending deferred methods
|
|
tl::NoDeferredMethods silent;
|
|
|
|
// if any transaction is pending (this may happen when an operation threw an exception)
|
|
// close transactions.
|
|
if (lay::MainWindow::instance () && lay::MainWindow::instance ()->manager ().transacting ()) {
|
|
lay::MainWindow::instance ()->manager ().commit ();
|
|
}
|
|
|
|
const tl::ExitException *gsi_exit = dynamic_cast <const tl::ExitException *> (&ex);
|
|
const tl::ScriptError *gsi_excpt = dynamic_cast <const tl::ScriptError *> (&ex);
|
|
|
|
if (gsi_exit) {
|
|
// exit exceptions are not shown - they are issued when a script is aborted
|
|
} else if (gsi_excpt) {
|
|
|
|
// show and GSI errors in the context of the macro editor if that is open
|
|
if (! parent && lay::MacroEditorDialog::instance () && lay::MacroEditorDialog::instance ()->isVisible ()) {
|
|
parent = lay::MacroEditorDialog::instance ();
|
|
}
|
|
if (! parent) {
|
|
parent = QApplication::activeWindow () ? QApplication::activeWindow () : lay::MainWindow::instance ();
|
|
}
|
|
|
|
if (gsi_excpt->line () > 0) {
|
|
tl::error << gsi_excpt->sourcefile () << ":" << gsi_excpt->line () << ": "
|
|
<< gsi_excpt->msg () << tl::to_string (QObject::tr (" (class ")) << gsi_excpt->cls () << ")";
|
|
} else {
|
|
tl::error << gsi_excpt->msg () << tl::to_string (QObject::tr (" (class ")) << gsi_excpt->cls () << ")";
|
|
}
|
|
|
|
lay::RuntimeErrorForm error_dialog (parent, "ruby_error_form", gsi_excpt);
|
|
error_dialog.exec ();
|
|
|
|
} else {
|
|
tl::error << ex.msg ();
|
|
if (! parent) {
|
|
parent = QApplication::activeWindow () ? QApplication::activeWindow () : lay::MainWindow::instance ();
|
|
}
|
|
QMessageBox::critical (parent, QObject::tr ("Error"), tl::to_qstring (ex.msg ()));
|
|
}
|
|
}
|
|
|
|
static void ui_exception_handler_std (const std::exception &ex, QWidget *parent)
|
|
{
|
|
// Prevents severe side effects if there are pending deferred methods
|
|
tl::NoDeferredMethods silent;
|
|
|
|
// if any transaction is pending (this may happen when an operation threw an exception)
|
|
// close transactions.
|
|
if (lay::MainWindow::instance () && lay::MainWindow::instance ()->manager ().transacting ()) {
|
|
lay::MainWindow::instance ()->manager ().commit ();
|
|
}
|
|
|
|
tl::error << ex.what ();
|
|
if (! parent) {
|
|
parent = QApplication::activeWindow () ? QApplication::activeWindow () : lay::MainWindow::instance ();
|
|
}
|
|
QMessageBox::critical (parent, QObject::tr ("Error"), tl::to_qstring (ex.what ()));
|
|
}
|
|
|
|
static void ui_exception_handler_def (QWidget *parent)
|
|
{
|
|
// Prevents severe side effects if there are pending deferred methods
|
|
tl::NoDeferredMethods silent;
|
|
|
|
// if any transaction is pending (this may happen when an operation threw an exception)
|
|
// close transactions.
|
|
if (lay::MainWindow::instance () && lay::MainWindow::instance ()->manager ().transacting ()) {
|
|
lay::MainWindow::instance ()->manager ().commit ();
|
|
}
|
|
|
|
if (! parent) {
|
|
parent = QApplication::activeWindow () ? QApplication::activeWindow () : lay::MainWindow::instance ();
|
|
}
|
|
QMessageBox::critical (parent, QObject::tr ("Error"), QObject::tr ("An unspecific error occured"));
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------
|
|
|
|
static Application *ms_instance = 0;
|
|
|
|
static PluginDescriptor load_plugin (const std::string &pp)
|
|
{
|
|
PluginDescriptor desc;
|
|
desc.path = pp;
|
|
|
|
klp_init_func_t init_func = 0;
|
|
static const char *init_func_name = "klp_init";
|
|
|
|
// NOTE: since we are using a different suffix ("*.klp"), we can't use QLibrary.
|
|
#ifdef _WIN32
|
|
// there is no "dlopen" on mingw, so we need to emulate it.
|
|
HINSTANCE handle = LoadLibraryW ((const wchar_t *) tl::to_qstring (pp).constData ());
|
|
if (! handle) {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Unable to load plugin: %s with error message: %s ")), pp, GetLastError ());
|
|
}
|
|
init_func = reinterpret_cast<klp_init_func_t> (GetProcAddress (handle, init_func_name));
|
|
#else
|
|
void *handle;
|
|
handle = dlopen (tl::string_to_system (pp).c_str (), RTLD_LAZY);
|
|
if (! handle) {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Unable to load plugin: %s")), pp);
|
|
}
|
|
init_func = reinterpret_cast<klp_init_func_t> (dlsym (handle, init_func_name));
|
|
#endif
|
|
|
|
// If present, call the initialization function to fetch some details from the plugin
|
|
if (init_func) {
|
|
const char *version = 0;
|
|
const char *description = 0;
|
|
(*init_func) (&desc.autorun, &desc.autorun_early, &version, &description);
|
|
if (version) {
|
|
desc.version = version;
|
|
}
|
|
if (description) {
|
|
desc.description = description;
|
|
}
|
|
}
|
|
|
|
tl::log << "Loaded plugin '" << pp << "'";
|
|
|
|
return desc;
|
|
}
|
|
|
|
Application::Application (int &argc, char **argv, bool non_ui_mode)
|
|
: QApplication (argc, argv, !non_ui_mode),
|
|
m_lyp_map_all_cvs (true),
|
|
m_lyp_add_default (false),
|
|
m_write_config_file (true),
|
|
m_gtf_replay_rate (0),
|
|
m_gtf_replay_stop (-1),
|
|
m_no_macros (false),
|
|
m_same_view (false),
|
|
m_sync_mode (false),
|
|
m_no_gui (false),
|
|
m_vo_mode (false),
|
|
m_editable (false),
|
|
m_enable_undo (true),
|
|
mp_qapp (0),
|
|
mp_qapp_gui (0),
|
|
mp_ruby_interpreter (0),
|
|
mp_python_interpreter (0),
|
|
mp_mw (0),
|
|
mp_pr (0),
|
|
mp_pb (0),
|
|
mp_plugin_root (0),
|
|
mp_recorder (0)
|
|
{
|
|
// TODO: offer a strict mode for exception handling where this takes place:
|
|
// lay::Application::instance ()->exit (1);
|
|
if (! non_ui_mode) {
|
|
tl::set_ui_exception_handlers (ui_exception_handler_tl, ui_exception_handler_std, ui_exception_handler_def);
|
|
}
|
|
|
|
mp_qapp = this;
|
|
mp_qapp_gui = (non_ui_mode ? 0 : this);
|
|
|
|
mp_dm_scheduler.reset (new tl::DeferredMethodScheduler ());
|
|
|
|
// install a special style proxy to overcome the issue of black-on-black tree expanders
|
|
if (mp_qapp_gui) {
|
|
mp_qapp_gui->setStyle (new lay::BackgroundAwareTreeStyle (0));
|
|
}
|
|
|
|
// initialize the system codecs (Hint: this must be done after the QApplication is initialized because
|
|
// it will call setlocale)
|
|
tl::initialize_codecs ();
|
|
|
|
// transscribe the arguments to UTF8
|
|
std::vector<std::string> args;
|
|
args.reserve (argc);
|
|
for (int i = 0; i < argc; ++i) {
|
|
args.push_back (argv [i]);
|
|
}
|
|
|
|
#if defined(KLAYOUT_VIEWER_ONLY)
|
|
// viewer-only mode compiled in
|
|
m_vo_mode = true;
|
|
#else
|
|
// determine viewer-only mode from executable name. "klayout_vo*" will enable
|
|
// viewer-only mode
|
|
std::string vo_exe_name (std::string (lay::Version::exe_name ()) + "_vo");
|
|
if (! args.empty () && std::string (tl::to_string (QFileInfo (tl::to_qstring (args.front ())).fileName ()), 0, vo_exe_name.size ()) == vo_exe_name) {
|
|
m_vo_mode = true;
|
|
}
|
|
#endif
|
|
|
|
tl_assert (ms_instance == 0);
|
|
ms_instance = this;
|
|
|
|
std::string gtf_record;
|
|
bool gtf_save_incremental = false;
|
|
|
|
// get and create the klayout appdata folder if required
|
|
m_appdata_path = tl::get_appdata_path ();
|
|
|
|
// get the installation path
|
|
m_inst_path = tl::get_inst_path ();
|
|
|
|
// get the KLayout path
|
|
m_klayout_path = tl::get_klayout_path ();
|
|
|
|
if (mp_qapp_gui) {
|
|
|
|
// create the configuration files paths and collect the initialization config files
|
|
// (the ones used for reset) into m_initial_config_files.
|
|
{
|
|
// Fallback to ~/.layviewrc for backward compatibility
|
|
QDir qd (QDir::home ());
|
|
QString filename = QString::fromUtf8 (".layviewrc");
|
|
if (qd.exists (filename) && QFileInfo (qd.absoluteFilePath (filename)).isReadable ()) {
|
|
m_config_files.push_back (tl::to_string (qd.absoluteFilePath (filename)));
|
|
m_config_file_to_delete = m_config_files.back ();
|
|
}
|
|
}
|
|
|
|
m_config_file_to_write = tl::to_string (QDir (tl::to_qstring (m_appdata_path)).absoluteFilePath (QString::fromUtf8 ("klayoutrc")));
|
|
|
|
// Hint: the order is reverse in the sense that the first one wins ...
|
|
for (std::vector <std::string>::const_iterator p = m_klayout_path.end (); p != m_klayout_path.begin (); ) {
|
|
--p;
|
|
QDir qd (tl::to_qstring (*p));
|
|
QString filename = QString::fromUtf8 ("klayoutrc");
|
|
if (qd.exists (filename) && QFileInfo (qd.absoluteFilePath (filename)).isReadable ()) {
|
|
m_config_files.push_back (tl::to_string (qd.absoluteFilePath (filename)));
|
|
if (m_config_files.back () != m_config_file_to_write) {
|
|
m_initial_config_files.push_back (m_config_files.back ());
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Try to locate the native plugins:
|
|
// Native plugins are DLL's or SO's disguised as "*.klp" files.
|
|
// The are installed either
|
|
// - directly in one of the KLAYOUT_PATH directories
|
|
// - in a folder named by the architecture (i.e. "i686-win32-mingw" or "x86_64-linux-gcc") below
|
|
// one of these folders
|
|
// - in one of the Salt packages
|
|
// - in one of the Salt packages, in a folder named after the architecture
|
|
|
|
std::string version = lay::Version::version ();
|
|
std::vector<std::string> vv = tl::split (version, ".");
|
|
|
|
std::string arch_string = tl::arch_string ();
|
|
std::vector<std::string> as = tl::split (arch_string, "-");
|
|
if (as.size () > 2) {
|
|
as.resize (2);
|
|
}
|
|
std::string short_arch_string = tl::join (as, "-");
|
|
|
|
for (std::vector <std::string>::const_iterator p = m_klayout_path.begin (); p != m_klayout_path.end (); ++p) {
|
|
|
|
std::set<std::string> modules;
|
|
|
|
std::vector<QString> klp_paths;
|
|
klp_paths.push_back (tl::to_qstring (*p));
|
|
klp_paths.push_back (QDir (klp_paths.back ()).filePath (tl::to_qstring (tl::arch_string ())));
|
|
|
|
lay::Salt salt;
|
|
salt.add_location (tl::to_string (QDir (tl::to_qstring (*p)).filePath (QString::fromUtf8 ("salt"))));
|
|
|
|
// Build the search path for the *.klp files. The search priority is for example:
|
|
// salt/mypackage/x86_64-linux-gcc-0.25.1
|
|
// salt/mypackage/x86_64-linux-gcc-0.25
|
|
// salt/mypackage/x86_64-linux-gcc-0
|
|
// salt/mypackage/x86_64-linux-gcc
|
|
// salt/mypackage/x86_64-linux
|
|
// salt/mypackage
|
|
|
|
for (lay::Salt::flat_iterator g = salt.begin_flat (); g != salt.end_flat (); ++g) {
|
|
QDir dir = QDir (tl::to_qstring ((*g)->path ()));
|
|
klp_paths.push_back (dir.filePath (tl::to_qstring (arch_string + "-" + lay::Version::version())));
|
|
if (vv.size () >= 2) {
|
|
klp_paths.push_back (dir.filePath (tl::to_qstring (arch_string + "-" + vv[0] + "." + vv[1])));
|
|
}
|
|
if (vv.size () >= 1) {
|
|
klp_paths.push_back (dir.filePath (tl::to_qstring (arch_string + "-" + vv[0])));
|
|
}
|
|
klp_paths.push_back (dir.filePath (tl::to_qstring (arch_string + "-" + tl::to_string (lay::Version::version ()))));
|
|
klp_paths.push_back (dir.filePath (tl::to_qstring (arch_string)));
|
|
klp_paths.push_back (dir.filePath (tl::to_qstring (short_arch_string)));
|
|
klp_paths.push_back (tl::to_qstring ((*g)->path ()));
|
|
}
|
|
|
|
QStringList name_filters;
|
|
name_filters << QString::fromUtf8 ("*.klp");
|
|
|
|
for (std::vector<QString>::const_iterator p = klp_paths.begin (); p != klp_paths.end (); ++p) {
|
|
|
|
QStringList inst_modules = QDir (*p).entryList (name_filters);
|
|
inst_modules.sort ();
|
|
|
|
for (QStringList::const_iterator im = inst_modules.begin (); im != inst_modules.end (); ++im) {
|
|
QFileInfo klp_file (*p, *im);
|
|
if (klp_file.exists () && klp_file.isReadable ()) {
|
|
std::string m = tl::to_string (klp_file.absoluteFilePath ());
|
|
std::string mn = tl::to_string (klp_file.fileName ());
|
|
if (modules.find (mn) == modules.end ()) {
|
|
m_native_plugins.push_back (load_plugin (m));
|
|
modules.insert (mn);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
std::vector <std::pair<std::string, std::string> > custom_macro_paths;
|
|
m_no_macros = false;
|
|
|
|
// currently: technology is always set to make "default" technology the default
|
|
bool tech_set = true;
|
|
std::string tech;
|
|
std::string tech_file;
|
|
|
|
bool editable_set = false;
|
|
|
|
for (int i = 1; i < argc; ++i) {
|
|
|
|
const std::string &a = args [i];
|
|
|
|
if (a == "-d" && (i + 1) < argc) {
|
|
|
|
int v = 0;
|
|
tl::from_string (args [++i], v);
|
|
v = std::max (0, v);
|
|
tl::verbosity (v);
|
|
|
|
} else if (a == "-l" && (i + 1) < argc) {
|
|
|
|
m_layer_props_file = args [++i];
|
|
|
|
} else if (a == "-lx") {
|
|
|
|
m_lyp_add_default = true;
|
|
|
|
} else if (a == "-lf") {
|
|
|
|
m_lyp_map_all_cvs = false;
|
|
|
|
} else if (a == "-u" && (i + 1) < argc) {
|
|
|
|
m_session_file = args [++i];
|
|
|
|
} else if (a == "-wd" && (i + 1) < argc) {
|
|
|
|
std::string v;
|
|
const char *p = args [++i].c_str ();
|
|
const char *n0 = p;
|
|
while (*p && *p != '=') {
|
|
++p;
|
|
}
|
|
std::string n (n0, p - n0);
|
|
if (*p == '=') {
|
|
tl::Eval::set_global_var (n, tl::Variant (v));
|
|
} else {
|
|
tl::Eval::set_global_var (n, tl::Variant (true));
|
|
}
|
|
|
|
} else if (a == "-rd" && (i + 1) < argc) {
|
|
|
|
std::string v;
|
|
const char *p = args [++i].c_str ();
|
|
const char *n0 = p;
|
|
while (*p && *p != '=') {
|
|
++p;
|
|
}
|
|
std::string n (n0, p - n0);
|
|
m_variables.push_back (std::make_pair (n, v));
|
|
if (*p == '=') {
|
|
m_variables.back ().second = ++p;
|
|
}
|
|
|
|
} else if (a == "-rm" && (i + 1) < argc) {
|
|
|
|
m_load_macros.push_back (args [++i]);
|
|
|
|
} else if (a == "-r" && (i + 1) < argc) {
|
|
|
|
m_run_macro = args [++i];
|
|
|
|
} else if (a == "-rx") {
|
|
|
|
m_no_macros = true;
|
|
|
|
} else if (a == "-gr" && (i + 1) < argc) {
|
|
|
|
gtf_record = args [++i];
|
|
|
|
} else if (a == "-gi") {
|
|
|
|
gtf_save_incremental = true;
|
|
|
|
} else if (a == "-gp" && (i + 1) < argc) {
|
|
|
|
m_gtf_replay = args [++i];
|
|
// test mode replay forces sync mode to true
|
|
m_sync_mode = true;
|
|
|
|
} else if (a == "-gx" && (i + 1) < argc) {
|
|
|
|
int r = 0;
|
|
tl::from_string (args [++i], r);
|
|
m_gtf_replay_rate = std::max (0, r);
|
|
|
|
} else if (a == "-gb" && (i + 1) < argc) {
|
|
|
|
int s = 0;
|
|
tl::from_string (args [++i], s);
|
|
m_gtf_replay_stop = std::max (0, s);
|
|
|
|
} else if (a == "-c" && (i + 1) < argc) {
|
|
|
|
m_config_files.clear ();
|
|
m_config_files.push_back (args [++i]);
|
|
m_initial_config_files = m_config_files;
|
|
m_config_file_to_write = m_config_files.back ();
|
|
|
|
} else if (a == "-nc") {
|
|
|
|
m_config_files.clear ();
|
|
m_initial_config_files = m_config_files;
|
|
m_config_file_to_write.clear ();
|
|
|
|
} else if (a == "-n" && (i + 1) < argc) {
|
|
|
|
tech = args [++i];
|
|
tech_file.clear ();
|
|
tech_set = true;
|
|
|
|
} else if (a == "-nn" && (i + 1) < argc) {
|
|
|
|
tech_file = args [++i];
|
|
tech.clear ();
|
|
tech_set = true;
|
|
|
|
} else if (a == "-p" && (i + 1) < argc) {
|
|
|
|
m_native_plugins.push_back (load_plugin (args [++i]));
|
|
|
|
} else if (a == "-s") {
|
|
|
|
m_same_view = true;
|
|
|
|
} else if (a == "-e") {
|
|
|
|
m_editable = ! m_vo_mode;
|
|
editable_set = true;
|
|
|
|
} else if (a == "-ne") {
|
|
|
|
m_editable = false;
|
|
editable_set = true;
|
|
|
|
} else if (a == "-i") {
|
|
|
|
m_enable_undo = false;
|
|
|
|
} else if (a == "-ni") {
|
|
|
|
m_enable_undo = true;
|
|
|
|
} else if (a == "-j" && (i + 1) < argc) {
|
|
|
|
custom_macro_paths.push_back (std::pair<std::string, std::string> (args [++i], "macros"));
|
|
|
|
} else if (a == "-nt") {
|
|
|
|
m_write_config_file = true;
|
|
|
|
} else if (a == "-t") {
|
|
|
|
m_write_config_file = false;
|
|
|
|
} else if (a == "-z") {
|
|
|
|
m_no_gui = true;
|
|
|
|
} else if (a == "-zz") {
|
|
|
|
m_no_gui = true;
|
|
// other consequences have been dealt with before
|
|
|
|
} else if (a == "-b") {
|
|
|
|
// -nc:
|
|
m_config_files.clear ();
|
|
m_initial_config_files = m_config_files;
|
|
m_config_file_to_write.clear ();
|
|
|
|
// -rx:
|
|
m_no_macros = true;
|
|
|
|
// -zz:
|
|
m_no_gui = true;
|
|
// other consequences have been dealt with before
|
|
|
|
} else if (a == "-x") {
|
|
|
|
m_sync_mode = true;
|
|
|
|
} else if (a == "-v") {
|
|
|
|
tl::info << lay::Version::name () << " " << lay::Version::version ();
|
|
exit (0);
|
|
|
|
} else if (a == "-h") {
|
|
|
|
tl::info << usage () << tl::noendl;
|
|
exit (0);
|
|
|
|
} else if (a == "-m" && (i + 1) < argc) {
|
|
|
|
m_files.push_back (std::make_pair (rdb_file, std::make_pair (std::string (args [++i]), std::string ())));
|
|
|
|
} else if (a[0] == '-') {
|
|
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Unknown option: ")) + a);
|
|
|
|
} else if (tech_set) {
|
|
|
|
if (! tech.empty ()) {
|
|
m_files.push_back (std::make_pair (layout_file_with_tech, std::make_pair (a, tech)));
|
|
} else if (! tech_file.empty ()) {
|
|
m_files.push_back (std::make_pair (layout_file_with_tech_file, std::make_pair (a, tech_file)));
|
|
} else {
|
|
m_files.push_back (std::make_pair (layout_file, std::make_pair (a, std::string ())));
|
|
}
|
|
|
|
} else {
|
|
|
|
m_files.push_back (std::make_pair (layout_file, std::make_pair (a, std::string ())));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// initialize the GSI class system (Variant binding, Expression support)
|
|
// We have to do this now since plugins may register GSI classes and before the
|
|
// ruby interpreter, because it depends on a proper class system.
|
|
gsi::initialize ();
|
|
|
|
// initialize the tl::Expression subsystem with GSI-bound classes
|
|
gsi::initialize_expressions ();
|
|
|
|
// create the ruby and python interpreter instances now.
|
|
// Hint: we do this after load_plugin, because that way the plugins can register GSI classes and methods.
|
|
// TODO: do this through some auto-registration
|
|
mp_ruby_interpreter = new rba::RubyInterpreter ();
|
|
mp_python_interpreter = new pya::PythonInterpreter ();
|
|
|
|
// Read some configuration values that we need early
|
|
bool editable_from_config = false;
|
|
|
|
{
|
|
lay::PluginRoot cfg;
|
|
|
|
for (std::vector <std::string>::const_iterator c = m_config_files.begin (); c != m_config_files.end (); ++c) {
|
|
try {
|
|
cfg.read_config (*c);
|
|
} catch (...) { }
|
|
}
|
|
|
|
try {
|
|
cfg.config_get (cfg_edit_mode, editable_from_config);
|
|
} catch (...) { }
|
|
|
|
try {
|
|
std::string mp;
|
|
cfg.config_get (cfg_custom_macro_paths, mp);
|
|
tl::Extractor ex (mp.c_str ());
|
|
while (! ex.at_end ()) {
|
|
std::string p;
|
|
ex.read_word_or_quoted (p);
|
|
custom_macro_paths.push_back (std::pair<std::string, std::string> (p, "macros"));
|
|
if (ex.test (":")) {
|
|
ex.read_word (custom_macro_paths.back ().second);
|
|
}
|
|
ex.test (";");
|
|
}
|
|
} catch (...) { }
|
|
|
|
}
|
|
|
|
if (! m_no_gui) {
|
|
// Install the signal handlers after the interpreters, so we can be sure we
|
|
// installed our handler.
|
|
install_signal_handlers ();
|
|
}
|
|
|
|
lay::SaltController *sc = lay::SaltController::instance ();
|
|
lay::TechnologyController *tc = lay::TechnologyController::instance ();
|
|
lay::MacroController *mc = lay::MacroController::instance ();
|
|
|
|
if (sc) {
|
|
|
|
// auto-import salt grains
|
|
for (std::vector <std::string>::const_iterator p = m_klayout_path.begin (); p != m_klayout_path.end (); ++p) {
|
|
sc->add_path (*p);
|
|
}
|
|
|
|
sc->set_salt_mine_url (tl::salt_mine_url ());
|
|
|
|
}
|
|
|
|
if (tc) {
|
|
|
|
// auto-import technologies
|
|
for (std::vector <std::string>::const_iterator p = m_klayout_path.begin (); p != m_klayout_path.end (); ++p) {
|
|
tc->add_path (*p);
|
|
}
|
|
|
|
// import technologies from the command line
|
|
for (std::vector <std::pair<file_type, std::pair<std::string, std::string> > >::iterator f = m_files.begin (); f != m_files.end (); ++f) {
|
|
|
|
if (f->first == layout_file_with_tech_file) {
|
|
|
|
if (tl::verbosity () >= 20) {
|
|
tl::info << "Importing technology from " << f->second.second;
|
|
}
|
|
|
|
lay::Technology t;
|
|
t.load (f->second.second);
|
|
|
|
tc->add_temp_tech (t);
|
|
|
|
f->first = layout_file_with_tech;
|
|
f->second.second = t.name ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tc->load ();
|
|
|
|
}
|
|
|
|
if (mc) {
|
|
|
|
mc->enable_implicit_macros (! m_no_macros);
|
|
|
|
if (! m_no_macros) {
|
|
|
|
// Add the global ruby modules as the first ones.
|
|
// TODO: this is a deprecated feature.
|
|
std::vector<std::string> global_modules = scan_global_modules ();
|
|
m_load_macros.insert (m_load_macros.begin (), global_modules.begin (), global_modules.end ());
|
|
|
|
for (std::vector <std::string>::const_iterator p = m_klayout_path.begin (); p != m_klayout_path.end (); ++p) {
|
|
if (p == m_klayout_path.begin ()) {
|
|
mc->add_path (*p, tl::to_string (QObject::tr ("Local")), std::string (), false);
|
|
} else if (m_klayout_path.size () == 2) {
|
|
mc->add_path (*p, tl::to_string (QObject::tr ("Global")), std::string (), true);
|
|
} else {
|
|
mc->add_path (*p, tl::to_string (QObject::tr ("Global")) + " - " + *p, std::string (), true);
|
|
}
|
|
}
|
|
|
|
// Install the custom folders
|
|
for (std::vector <std::pair<std::string, std::string> >::const_iterator p = custom_macro_paths.begin (); p != custom_macro_paths.end (); ++p) {
|
|
mc->add_path (p->first, tl::to_string (QObject::tr ("Project")) + " - " + p->first, p->second, false);
|
|
}
|
|
|
|
}
|
|
|
|
// Actually load the macros
|
|
mc->load ();
|
|
|
|
}
|
|
|
|
// If the editable flag was not set, use it from the
|
|
// configuration. Since it is too early now, we cannot use the
|
|
// configuration once it is read
|
|
if (! editable_set && ! m_vo_mode) {
|
|
m_editable = editable_from_config;
|
|
}
|
|
|
|
db::set_default_editable_mode (m_editable);
|
|
db::enable_transactions (m_enable_undo);
|
|
|
|
if (mp_qapp_gui) {
|
|
mp_qapp_gui->setWindowIcon (QIcon (QString::fromUtf8 (":/logo.png")));
|
|
#if QT_VERSION >= 0x040500
|
|
mp_qapp_gui->setAttribute (Qt::AA_DontShowIconsInMenus, false);
|
|
#endif
|
|
}
|
|
|
|
if (mp_qapp_gui && ! gtf_record.empty ()) {
|
|
// since the recorder tracks QAction connections etc., it must be instantiated before every other
|
|
// object performing a gtf::action_connect for example
|
|
mp_recorder = new gtf::Recorder (mp_qapp_gui, gtf_record);
|
|
mp_recorder->save_incremental (gtf_save_incremental);
|
|
}
|
|
|
|
tl::Eval::set_global_var ("appdata_path", tl::Variant (m_appdata_path));
|
|
tl::Eval::set_global_var ("inst_path", tl::Variant (m_inst_path));
|
|
|
|
tl::Variant kp (m_klayout_path.begin (), m_klayout_path.end ());
|
|
tl::Eval::set_global_var ("klayout_path", kp);
|
|
|
|
// call "autorun_early" on all plugins that wish so
|
|
for (std::vector <lay::PluginDescriptor>::const_iterator p = m_native_plugins.begin (); p != m_native_plugins.end (); ++p) {
|
|
if (p->autorun_early) {
|
|
(*p->autorun_early) ();
|
|
}
|
|
}
|
|
|
|
// run all early autorun macros
|
|
lay::MacroCollection::root ().autorun_early ();
|
|
|
|
// rescan the folders because early autorun macros might have added
|
|
// suffixes through the MacroInterpreter interface.
|
|
lay::MacroCollection::root ().rescan ();
|
|
|
|
if (mp_qapp_gui) {
|
|
mp_mw = new lay::MainWindow (mp_qapp_gui, "main_window");
|
|
QObject::connect (mp_mw, SIGNAL (closed ()), mp_qapp_gui, SLOT (quit ()));
|
|
mp_plugin_root = mp_mw;
|
|
} else {
|
|
mp_pr = new lay::ProgressReporter ();
|
|
mp_pb = new TextProgress (10 /*verbosity level*/);
|
|
mp_pr->set_progress_bar (mp_pb);
|
|
mp_plugin_root = new lay::PluginRoot ();
|
|
}
|
|
|
|
// initialize the plugins for the first time
|
|
if (tl::verbosity () >= 20) {
|
|
tl::info << "Initializing plugins:";
|
|
}
|
|
for (tl::Registrar<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::end (); ++cls) {
|
|
lay::PluginDeclaration *pd = const_cast<lay::PluginDeclaration *> (&*cls);
|
|
if (tl::verbosity () >= 20) {
|
|
tl::info << " " << cls.current_name () << " [" << cls.current_position () << "]";
|
|
}
|
|
pd->initialize (mp_plugin_root);
|
|
}
|
|
|
|
// establish the configuration
|
|
mp_plugin_root->config_setup ();
|
|
|
|
// Some info output
|
|
if (tl::verbosity () >= 20) {
|
|
|
|
tl::info << "KLayout path:";
|
|
for (std::vector <std::string>::const_iterator c = m_klayout_path.begin (); c != m_klayout_path.end (); ++c) {
|
|
tl::info << " " << *c;
|
|
}
|
|
tl::info << "Config file to write: " << m_config_file_to_write;
|
|
tl::info << "Config files to read:";
|
|
for (std::vector <std::string>::const_iterator c = m_config_files.begin (); c != m_config_files.end (); ++c) {
|
|
tl::info << " " << *c;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
Application::~Application ()
|
|
{
|
|
tl::set_ui_exception_handlers (0, 0, 0);
|
|
|
|
if (! ms_instance) {
|
|
return;
|
|
}
|
|
|
|
shutdown ();
|
|
}
|
|
|
|
std::vector<std::string>
|
|
Application::scan_global_modules ()
|
|
{
|
|
// NOTE:
|
|
// this is deprecated functionality - for backward compatibility, global "*.rbm" and "*.pym" modules
|
|
// are still considered. The desired solution is autorun macros.
|
|
|
|
// try to locate a global rbainit file and rbm modules
|
|
std::vector<std::string> global_modules;
|
|
std::set<std::string> modules;
|
|
|
|
// try to locate a global plugins
|
|
for (std::vector <std::string>::const_iterator p = m_klayout_path.begin (); p != m_klayout_path.end (); ++p) {
|
|
|
|
#if 0
|
|
// deprecated functionality
|
|
QFileInfo rbainit_file (tl::to_qstring (*p), QString::fromUtf8 ("rbainit"));
|
|
if (rbainit_file.exists () && rbainit_file.isReadable ()) {
|
|
std::string m = tl::to_string (rbainit_file.absoluteFilePath ());
|
|
if (modules.find (m) == modules.end ()) {
|
|
global_modules.push_back (m);
|
|
modules.insert (m);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
QDir inst_path_dir (tl::to_qstring (*p));
|
|
|
|
QStringList name_filters;
|
|
name_filters << QString::fromUtf8 ("*.rbm");
|
|
name_filters << QString::fromUtf8 ("*.pym");
|
|
|
|
QStringList inst_modules = inst_path_dir.entryList (name_filters);
|
|
inst_modules.sort ();
|
|
|
|
for (QStringList::const_iterator im = inst_modules.begin (); im != inst_modules.end (); ++im) {
|
|
QFileInfo rbm_file (tl::to_qstring (*p), *im);
|
|
if (rbm_file.exists () && rbm_file.isReadable ()) {
|
|
std::string m = tl::to_string (rbm_file.absoluteFilePath ());
|
|
if (modules.find (m) == modules.end ()) {
|
|
tl::warn << tl::to_string (tr ("Global modules are deprecated. Turn '%1'' into an autorun macro instead and put it into 'macros' or 'pymacros'.").arg (tl::to_qstring (m)));
|
|
global_modules.push_back (m);
|
|
modules.insert (m);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return global_modules;
|
|
}
|
|
|
|
bool
|
|
Application::notify (QObject *receiver, QEvent *e)
|
|
{
|
|
// Note: due to a bug in some Qt versions (i.e. 4.8.3) throwing exceptions across
|
|
// signals may not be safe. Hence the local BEGIN_PROTECTED .. END_PROTECTED approach
|
|
// is still preferred over the global solution through "notify"
|
|
|
|
bool ret = true;
|
|
BEGIN_PROTECTED
|
|
ret = QApplication::notify (receiver, e);
|
|
END_PROTECTED
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
Application::exit (int result)
|
|
{
|
|
if (! result) {
|
|
finish ();
|
|
}
|
|
shutdown ();
|
|
::exit (result);
|
|
}
|
|
|
|
void
|
|
Application::finish ()
|
|
{
|
|
// save the recorded test events
|
|
if (mp_mw && mp_recorder && mp_recorder->recording ()) {
|
|
mp_recorder->stop ();
|
|
mp_recorder->save ();
|
|
}
|
|
|
|
if (mp_plugin_root && m_write_config_file) {
|
|
|
|
if (! m_config_file_to_write.empty ()) {
|
|
if (tl::verbosity () >= 20) {
|
|
tl::info << tl::to_string (QObject::tr ("Updating configuration file ")) << m_config_file_to_write;
|
|
}
|
|
mp_plugin_root->write_config (m_config_file_to_write);
|
|
}
|
|
if (! m_config_file_to_delete.empty () && m_config_file_to_delete != m_config_file_to_write) {
|
|
if (tl::verbosity () >= 20) {
|
|
tl::info << tl::to_string (QObject::tr ("Deleting configuration file ")) << m_config_file_to_delete;
|
|
}
|
|
QFile::remove (tl::to_qstring (m_config_file_to_delete));
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
Application::shutdown ()
|
|
{
|
|
// uninitialize the plugins
|
|
for (tl::Registrar<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::end (); ++cls) {
|
|
lay::PluginDeclaration *pd = const_cast<lay::PluginDeclaration *> (&*cls);
|
|
pd->uninitialize (mp_plugin_root);
|
|
}
|
|
|
|
if (mp_mw) {
|
|
delete mp_mw;
|
|
mp_mw = 0;
|
|
mp_plugin_root = 0;
|
|
} else if (mp_plugin_root) {
|
|
delete mp_plugin_root;
|
|
mp_plugin_root = 0;
|
|
}
|
|
|
|
// delete all other top level widgets for safety - we don't want Ruby clean them up for us
|
|
QWidgetList tl_widgets = topLevelWidgets ();
|
|
for (QWidgetList::iterator w = tl_widgets.begin (); w != tl_widgets.end (); ++w) {
|
|
delete *w;
|
|
}
|
|
|
|
if (mp_pr) {
|
|
delete mp_pr;
|
|
mp_pr = 0;
|
|
}
|
|
|
|
if (mp_pb) {
|
|
delete mp_pb;
|
|
mp_pb = 0;
|
|
}
|
|
|
|
if (mp_recorder) {
|
|
delete mp_recorder;
|
|
mp_recorder = 0;
|
|
}
|
|
|
|
if (mp_ruby_interpreter) {
|
|
delete mp_ruby_interpreter;
|
|
mp_ruby_interpreter = 0;
|
|
}
|
|
|
|
if (mp_python_interpreter) {
|
|
delete mp_python_interpreter;
|
|
mp_python_interpreter = 0;
|
|
}
|
|
|
|
mp_qapp = 0;
|
|
mp_qapp_gui = 0;
|
|
ms_instance = 0;
|
|
}
|
|
|
|
Application *
|
|
Application::instance ()
|
|
{
|
|
return ms_instance;
|
|
}
|
|
|
|
std::string
|
|
Application::version () const
|
|
{
|
|
return std::string (lay::Version::name ()) + " " + lay::Version::version ();
|
|
}
|
|
|
|
std::string
|
|
Application::usage ()
|
|
{
|
|
std::string r;
|
|
r = std::string (lay::Version::exe_name ()) + " [<options>] [<file>] ..\n";
|
|
r += tl::to_string (QObject::tr ("options")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -b Batch mode (same as -zz -nc -rx)")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -c <config file> Use this configuration file")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -nc Don't use a configuration file (implies -t)")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -d <debug level> Set debug level")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -e Editable mode (allow editing of files)")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -ne Readonly mode (editing of files is disabled)")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -gr <file name> Record GUI test file")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -gp <file name> Replay GUI test file")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -gb <line number> Replay GUI test file up to (including) line")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -gx <millisec> Replay rate for GUI test file")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -gi Incremental logs for GUI test file")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -i Disable undo buffering (less memory requirements)")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -ni Enable undo buffering (default, overrides previous -i option)")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -j <path> Add the given path to the macro project paths")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -l <lyp file> Use layer properties file")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -lx With -l: add other layers as well")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -lf With -l: use the lyp file as it is (no expansion to multiple layouts)")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -m <database file> Load RDB (report database) file (into previous layout view)")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -n <technology> Technology to use for next layout(s) on command line")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -nn <tech file> Technology file (.lyt) to use for next layout(s) on command line")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -p <plugin> Load the plugin (can be used multiple times)")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -r <script> Execute main script on startup (after having loaded files etc.)")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -rm <script> Execute module on startup (can be used multiple times)")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -rd <name>=<value> Specify skript variable")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -rx Ignore all implicit macros (*.rbm, rbainit, *.lym)")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -s Load files into same view")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -t Don't update the configuration file on exit")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -nt Update the configuration file on exit (default, overrides previous -t option)")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -u <file name> Restore session from given file")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -v Print program version and exit")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -wd <name>=<value> Define a variable within expressions")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -x Synchronous drawing mode")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -zz Non-GUI mode (database only, implies -nc)")) + "\n";
|
|
r += tl::to_string (QObject::tr (" -z Non-GUI mode (hidden views)")) + "\n";
|
|
return r;
|
|
}
|
|
|
|
int
|
|
Application::run ()
|
|
{
|
|
gtf::Player player (0);
|
|
|
|
if (mp_mw) {
|
|
|
|
mp_mw->set_synchronous (m_sync_mode);
|
|
|
|
if (! m_no_gui) {
|
|
mp_mw->setWindowTitle (tl::to_qstring (version ()));
|
|
mp_mw->resize (800, 600);
|
|
mp_mw->show ();
|
|
}
|
|
|
|
if (! m_gtf_replay.empty ()) {
|
|
player.load (m_gtf_replay);
|
|
}
|
|
|
|
if (mp_recorder) {
|
|
mp_recorder->start ();
|
|
}
|
|
|
|
}
|
|
|
|
int result = 0;
|
|
|
|
bool config_failed = false;
|
|
|
|
for (std::vector <std::string>::const_iterator c = m_config_files.begin (); c != m_config_files.end (); ++c) {
|
|
BEGIN_PROTECTED_CLEANUP
|
|
mp_plugin_root->read_config (*c);
|
|
// if the last config was read successfully no reset will happen:
|
|
config_failed = false;
|
|
END_PROTECTED_CLEANUP {
|
|
config_failed = true;
|
|
}
|
|
}
|
|
|
|
if (config_failed) {
|
|
reset_config ();
|
|
}
|
|
|
|
for (std::vector< std::pair<std::string, std::string> >::const_iterator v = m_variables.begin (); v != m_variables.end (); ++v) {
|
|
ruby_interpreter ().define_variable (v->first, v->second);
|
|
python_interpreter ().define_variable (v->first, v->second);
|
|
tl::log << "Variable definition: " << v->first << "='" << v->second << "'";
|
|
}
|
|
|
|
for (std::vector<std::string>::const_iterator m = m_load_macros.begin (); m != m_load_macros.end (); ++m) {
|
|
|
|
BEGIN_PROTECTED
|
|
|
|
std::auto_ptr<lay::Macro> macro (new lay::Macro ());
|
|
macro->load_from (*m);
|
|
macro->set_file_path (*m);
|
|
if (macro->show_in_menu ()) {
|
|
// menu-based macros are just registered so they are shown in the menu
|
|
lay::MacroController *mc = lay::MacroController::instance ();
|
|
if (mc) {
|
|
tl::log << "Registering macro '" << *m << "'";
|
|
mc->add_temp_macro (macro.release ());
|
|
}
|
|
} else {
|
|
// other macros given with -rm are run
|
|
tl::log << "Run macro '" << *m << "'";
|
|
macro->run ();
|
|
}
|
|
|
|
END_PROTECTED
|
|
|
|
}
|
|
|
|
// call "autorun" on all plugins that wish so
|
|
for (std::vector <lay::PluginDescriptor>::const_iterator p = m_native_plugins.begin (); p != m_native_plugins.end (); ++p) {
|
|
if (p->autorun) {
|
|
(*p->autorun) ();
|
|
}
|
|
}
|
|
|
|
// run all autorun macros
|
|
lay::MacroCollection::root ().autorun ();
|
|
|
|
if (mp_mw) {
|
|
|
|
for (std::vector <std::pair<file_type, std::pair<std::string, std::string> > >::const_iterator f = m_files.begin (); f != m_files.end (); ++f) {
|
|
|
|
if (f->first == layout_file || f->first == layout_file_with_tech) {
|
|
|
|
std::string filename = f->second.first;
|
|
|
|
if (f->first != layout_file_with_tech) {
|
|
mp_mw->add_mru (f->second.first);
|
|
mp_mw->load_layout (f->second.first, m_same_view ? 2 /*same view*/ : 1 /*new view*/);
|
|
} else {
|
|
mp_mw->add_mru (f->second.first, f->second.second);
|
|
mp_mw->load_layout (f->second.first, f->second.second, m_same_view ? 2 /*same view*/ : 1 /*new view*/);
|
|
}
|
|
|
|
// Make the first one loaded the active one.
|
|
if (mp_mw->current_view ()) {
|
|
mp_mw->current_view ()->set_active_cellview_index (0);
|
|
}
|
|
|
|
} else {
|
|
|
|
if (mp_mw->current_view () == 0) {
|
|
mp_mw->create_view ();
|
|
}
|
|
|
|
if (mp_mw->current_view () != 0) {
|
|
std::auto_ptr <rdb::Database> db (new rdb::Database ());
|
|
db->load (f->second.first);
|
|
int rdb_index = mp_mw->current_view ()->add_rdb (db.release ());
|
|
mp_mw->current_view ()->open_rdb_browser (rdb_index, mp_mw->current_view ()->active_cellview_index ());
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (! m_layer_props_file.empty ()) {
|
|
|
|
if (m_lyp_map_all_cvs && mp_mw->is_single_cv_layer_properties_file (m_layer_props_file)) {
|
|
mp_mw->load_layer_properties (m_layer_props_file, -1, true /*all views*/, m_lyp_add_default);
|
|
} else {
|
|
mp_mw->load_layer_properties (m_layer_props_file, true /*all views*/, m_lyp_add_default);
|
|
}
|
|
|
|
tl::log << "Layer properties loaded '" << m_layer_props_file << "'";
|
|
|
|
// because the layer may carry transformations, we need to refit the cellviews.
|
|
for (unsigned int v = 0; v != mp_mw->views (); ++v) {
|
|
mp_mw->view (v)->zoom_fit ();
|
|
}
|
|
|
|
}
|
|
|
|
if (! m_session_file.empty ()) {
|
|
mp_mw->restore_session (m_session_file);
|
|
tl::log << "Session restored '" << m_session_file << "'";
|
|
}
|
|
|
|
if (! m_gtf_replay.empty ()) {
|
|
player.replay (m_gtf_replay_rate, m_gtf_replay_stop);
|
|
}
|
|
|
|
// Give the plugins a change to do some last-minute initialisation and checks
|
|
for (tl::Registrar<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::end (); ++cls) {
|
|
lay::PluginDeclaration *pd = const_cast<lay::PluginDeclaration *> (&*cls);
|
|
pd->initialized (mp_mw);
|
|
}
|
|
|
|
if (! m_no_gui && m_gtf_replay.empty () && ! mp_recorder) {
|
|
// Show initial tip window if required
|
|
mp_mw->about_to_exec ();
|
|
}
|
|
|
|
} else if (mp_plugin_root) {
|
|
|
|
// Give the plugins a change to do some last-minute initialisation and checks
|
|
for (tl::Registrar<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::end (); ++cls) {
|
|
lay::PluginDeclaration *pd = const_cast<lay::PluginDeclaration *> (&*cls);
|
|
pd->initialized (mp_plugin_root);
|
|
}
|
|
|
|
}
|
|
|
|
if (! m_run_macro.empty ()) {
|
|
|
|
tl::log << "Run macro '" << m_run_macro << "'";
|
|
lay::Macro macro;
|
|
macro.load_from (m_run_macro);
|
|
macro.set_file_path (m_run_macro);
|
|
result = macro.run ();
|
|
|
|
} else {
|
|
result = exec ();
|
|
}
|
|
|
|
finish ();
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
Application::set_editable (bool e)
|
|
{
|
|
if (m_editable != e) {
|
|
m_editable = e;
|
|
db::set_default_editable_mode (m_editable);
|
|
}
|
|
}
|
|
|
|
static void
|
|
dump_children (QObject *obj, int level = 0)
|
|
{
|
|
QObjectList children = obj->children ();
|
|
if (! children.isEmpty () || ! obj->objectName ().isEmpty ()) {
|
|
std::string info;
|
|
for (int i = 0; i < level; ++i) {
|
|
info += " ";
|
|
}
|
|
if (obj->objectName ().isEmpty ()) {
|
|
info += "<unnamed>";
|
|
} else {
|
|
info += tl::to_string (obj->objectName ());
|
|
}
|
|
tl::info << info;
|
|
for (QObjectList::const_iterator child = children.begin (); child != children.end (); ++child) {
|
|
dump_children (*child, level + 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
Application::exec ()
|
|
{
|
|
if (m_no_gui) {
|
|
return 0;
|
|
} else {
|
|
|
|
// if requested, dump the widgets
|
|
if (tl::verbosity () >= 40) {
|
|
|
|
QWidgetList tl_widgets = QApplication::topLevelWidgets ();
|
|
|
|
tl::info << tl::to_string (QObject::tr ("Widget tree:"));
|
|
for (QWidgetList::const_iterator tl = tl_widgets.begin (); tl != tl_widgets.end (); ++tl) {
|
|
if (! (*tl)->objectName ().isEmpty ()) {
|
|
dump_children (*tl);
|
|
}
|
|
}
|
|
tl::info << "";
|
|
|
|
tl::info << tl::to_string (QObject::tr ("Actions list:"));
|
|
for (QWidgetList::const_iterator tl = tl_widgets.begin (); tl != tl_widgets.end (); ++tl) {
|
|
if (! (*tl)->objectName ().isEmpty ()) {
|
|
QList<QAction *> actions = (*tl)->findChildren<QAction *> ();
|
|
if (! actions.isEmpty ()) {
|
|
tl::info << tl::to_string ((*tl)->objectName ()) << ":";
|
|
for (QList<QAction *>::const_iterator a = actions.begin (); a != actions.end (); ++a) {
|
|
if (! (*a)->objectName ().isEmpty ()) {
|
|
tl::info << " " << tl::to_string ((*a)->objectName ());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
tl::info << "";
|
|
|
|
}
|
|
|
|
return mp_qapp_gui->exec ();
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
Application::process_events (QEventLoop::ProcessEventsFlags flags, bool silent)
|
|
{
|
|
if (mp_mw) {
|
|
|
|
if (silent) {
|
|
mp_dm_scheduler->enable (false);
|
|
}
|
|
|
|
#if QT_VERSION < 0x050000
|
|
QApplication::syncX ();
|
|
#endif
|
|
|
|
mp_mw->enter_busy_mode (true);
|
|
QApplication::processEvents (flags);
|
|
mp_mw->enter_busy_mode (false);
|
|
|
|
if (silent) {
|
|
mp_dm_scheduler->enable (true);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
bool
|
|
Application::write_config (const std::string &config_file)
|
|
{
|
|
return mp_plugin_root ? mp_plugin_root->write_config (config_file) : 0;
|
|
}
|
|
|
|
void
|
|
Application::reset_config ()
|
|
{
|
|
clear_config ();
|
|
for (std::vector <std::string>::const_iterator c = m_initial_config_files.begin (); c != m_initial_config_files.end (); ++c) {
|
|
try {
|
|
read_config (*c);
|
|
} catch (...) { }
|
|
}
|
|
}
|
|
|
|
void
|
|
Application::clear_config ()
|
|
{
|
|
if (mp_plugin_root) {
|
|
mp_plugin_root->clear_config ();
|
|
}
|
|
}
|
|
|
|
bool
|
|
Application::read_config (const std::string &config_file)
|
|
{
|
|
return mp_plugin_root ? mp_plugin_root->read_config (config_file) : true;
|
|
}
|
|
|
|
void
|
|
Application::set_config (const std::string &name, const std::string &value)
|
|
{
|
|
if (mp_plugin_root) {
|
|
mp_plugin_root->config_set (name, value);
|
|
}
|
|
}
|
|
|
|
void
|
|
Application::config_end ()
|
|
{
|
|
if (mp_plugin_root) {
|
|
mp_plugin_root->config_end ();
|
|
}
|
|
}
|
|
|
|
std::string
|
|
Application::get_config (const std::string &name) const
|
|
{
|
|
if (mp_plugin_root) {
|
|
return mp_plugin_root->config_get (name);
|
|
} else {
|
|
return std::string ();
|
|
}
|
|
}
|
|
|
|
std::vector<std::string>
|
|
Application::get_config_names () const
|
|
{
|
|
std::vector<std::string> names;
|
|
if (mp_plugin_root) {
|
|
mp_plugin_root->get_config_names (names);
|
|
}
|
|
return names;
|
|
}
|
|
|
|
bool
|
|
Application::special_app_flag (const std::string &name)
|
|
{
|
|
// TODO: some more elaborate scheme?
|
|
const char *env = getenv (("KLAYOUT_" + name).c_str ());
|
|
return (env && *env);
|
|
}
|
|
|
|
}
|
|
|