From c4e5367b8a4bc27b40de2c4a57918b433229685d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 5 Apr 2020 18:22:45 +0200 Subject: [PATCH 001/113] First setup. --- .../tools/view_25d/lay_plugin/D25View.ui | 59 +++++++++ .../tools/view_25d/lay_plugin/layD25Plugin.cc | 112 ++++++++++++++++ .../tools/view_25d/lay_plugin/layD25View.cc | 65 ++++++++++ .../tools/view_25d/lay_plugin/layD25View.h | 65 ++++++++++ .../view_25d/lay_plugin/layD25ViewWidget.cc | 41 ++++++ .../view_25d/lay_plugin/layD25ViewWidget.h | 44 +++++++ .../tools/view_25d/lay_plugin/layXORPlugin.cc | 122 ++++++++++++++++++ .../tools/view_25d/lay_plugin/lay_plugin.pro | 21 +++ src/plugins/tools/view_25d/view_25d.pro | 6 + 9 files changed, 535 insertions(+) create mode 100644 src/plugins/tools/view_25d/lay_plugin/D25View.ui create mode 100644 src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc create mode 100644 src/plugins/tools/view_25d/lay_plugin/layD25View.cc create mode 100644 src/plugins/tools/view_25d/lay_plugin/layD25View.h create mode 100644 src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc create mode 100644 src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h create mode 100644 src/plugins/tools/view_25d/lay_plugin/layXORPlugin.cc create mode 100644 src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro create mode 100644 src/plugins/tools/view_25d/view_25d.pro diff --git a/src/plugins/tools/view_25d/lay_plugin/D25View.ui b/src/plugins/tools/view_25d/lay_plugin/D25View.ui new file mode 100644 index 000000000..f28b5eddf --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/D25View.ui @@ -0,0 +1,59 @@ + + + D25View + + + + 0 + 0 + 576 + 649 + + + + 2.5d View + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + lay::D25View + QOpenGLWidget +
layD25View.h
+
+
+ + buttonBox + + + +
diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc b/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc new file mode 100644 index 000000000..1883bdf36 --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc @@ -0,0 +1,112 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "layD25View.h" +#include "layDispatcher.h" + +#include "layPlugin.h" + +namespace lay +{ + +class D25Plugin + : public lay::Plugin +{ +public: + D25Plugin (Plugin *parent, lay::LayoutView *view) + : lay::Plugin (parent), mp_view (view) + { + mp_dialog = new lay::D25View (0); + } + + ~D25Plugin () + { + delete mp_dialog; + mp_dialog = 0; + } + + void menu_activated (const std::string &symbol) + { + if (symbol == "lay::d25_view") { + + if (mp_dialog->exec_dialog (mp_view)) { + + // ... implementation is in layD25ToolDialog.cc ... + + } + + } + } + +private: + lay::LayoutView *mp_view; + lay::D25ToolDialog *mp_dialog; +}; + +class D25PluginDeclaration + : public lay::PluginDeclaration +{ +public: + D25PluginDeclaration () + { + // .. nothing yet .. + } + + virtual void get_options (std::vector < std::pair > &options) const + { + // .. nothing yet .. + } + + virtual lay::ConfigPage *config_page (QWidget * /*parent*/, std::string & /*title*/) const + { + // .. nothing yet .. + return 0; + } + + virtual void get_menu_entries (std::vector &menu_entries) const + { + lay::PluginDeclaration::get_menu_entries (menu_entries); + menu_entries.push_back (lay::menu_item ("lay::d25_view", "d25_view:edit", "tools_menu.post_verification_group", tl::to_string (QObject::tr ("2.5d View")))); + } + + virtual bool configure (const std::string & /*name*/, const std::string & /*value*/) + { + return false; + } + + virtual void config_finalize () + { + // .. nothing yet .. + } + + lay::Plugin *create_plugin (db::Manager *, lay::Dispatcher *root, lay::LayoutView *view) const + { + return new D25Plugin (root, view); + } +}; + +static tl::RegisteredClass config_decl (new lay::D25PluginDeclaration (), 3000, "lay::D25Plugin"); + +} + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc new file mode 100644 index 000000000..cd2c5518b --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -0,0 +1,65 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "layD25View.h" + +#include "ui_D25View.h" + +#include + +namespace lay +{ + +D25View::D25View (QWidget *parent) + : QDialog (parent), mp_view (0) +{ + mp_ui = new Ui::D25View (); + mp_ui->setupUi (this); + + // @@@ +} + +D25View::~D25View () +{ + delete mp_ui; + mp_ui = 0; +} + +int +D25View::exec_dialog (lay::LayoutView *view) +{ + mp_view.reset (view); + + // @@@ + + return QDialog::exec (); +} + +void +D25View::accept () +{ + // @@@ +} + +} + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.h b/src/plugins/tools/view_25d/lay_plugin/layD25View.h new file mode 100644 index 000000000..78526bd36 --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.h @@ -0,0 +1,65 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_layD25View +#define HDR_layD25View + +#include + +#include "tlObject.h" + +namespace Ui +{ + class D25View; +} + +namespace lay +{ + class LayoutView; +} + +namespace lay +{ + +class D25View + : public QDialog +{ +Q_OBJECT + +public: + D25View (QWidget *parent); + ~D25View (); + + int exec_dialog (lay::LayoutView *view); + +protected: + void accept (); + +private: + Ui::D25View *mp_ui; + tl::weak_ptr mp_view; +}; + +} + +#endif + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc new file mode 100644 index 000000000..2cfffbebb --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -0,0 +1,41 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "layD25ViewWidget.h" + +namespace lay +{ + +D25ViewWidget::D25ViewWidget (QWidget *parent) + : QOpenGLWidget (parent), mp_view (0) +{ + // @@@ +} + +D25View::~D25View () +{ + // @@@ +} + +} + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h new file mode 100644 index 000000000..428f466f3 --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h @@ -0,0 +1,44 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_layD25ViewWidget +#define HDR_layD25ViewWidget + +#include + +namespace lay +{ + +class D25ViewWidget + : public QOpenGLWidget +{ +Q_OBJECT + +public: + D25ViewWidget (QWidget *parent); + ~D25ViewWidget (); +}; + +} + +#endif + diff --git a/src/plugins/tools/view_25d/lay_plugin/layXORPlugin.cc b/src/plugins/tools/view_25d/lay_plugin/layXORPlugin.cc new file mode 100644 index 000000000..f5bd4895e --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layXORPlugin.cc @@ -0,0 +1,122 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "layXORToolDialog.h" +#include "layDispatcher.h" + +#include "layPlugin.h" + +namespace lay +{ + +class XORPlugin + : public lay::Plugin +{ +public: + XORPlugin (Plugin *parent, lay::LayoutView *view) + : lay::Plugin (parent), mp_view (view) + { + mp_dialog = new lay::XORToolDialog (0); + } + + ~XORPlugin () + { + delete mp_dialog; + mp_dialog = 0; + } + + void menu_activated (const std::string &symbol) + { + if (symbol == "lay::xor_tool") { + + if (mp_dialog->exec_dialog (mp_view)) { + + // ... implementation is in layXORToolDialog.cc ... + + } + + } + } + +private: + lay::LayoutView *mp_view; + lay::XORToolDialog *mp_dialog; +}; + +class XORPluginDeclaration + : public lay::PluginDeclaration +{ +public: + XORPluginDeclaration () + { + // .. nothing yet .. + } + + virtual void get_options (std::vector < std::pair > &options) const + { + options.push_back (std::pair (cfg_xor_input_mode, "all")); + options.push_back (std::pair (cfg_xor_output_mode, "rdb")); + options.push_back (std::pair (cfg_xor_nworkers, "1")); + options.push_back (std::pair (cfg_xor_layer_offset, "")); + options.push_back (std::pair (cfg_xor_axorb, "true")); + options.push_back (std::pair (cfg_xor_anotb, "false")); + options.push_back (std::pair (cfg_xor_bnota, "false")); + options.push_back (std::pair (cfg_xor_summarize, "false")); + options.push_back (std::pair (cfg_xor_tolerances, "")); + options.push_back (std::pair (cfg_xor_tiling, "")); + options.push_back (std::pair (cfg_xor_region_mode, "all")); + } + + virtual lay::ConfigPage *config_page (QWidget * /*parent*/, std::string & /*title*/) const + { + // .. nothing yet .. + return 0; + } + + virtual void get_menu_entries (std::vector &menu_entries) const + { + lay::PluginDeclaration::get_menu_entries (menu_entries); + menu_entries.push_back (lay::menu_item ("lay::xor_tool", "xor_tool:edit", "tools_menu.post_verification_group", tl::to_string (QObject::tr ("XOR Tool")))); + } + + virtual bool configure (const std::string & /*name*/, const std::string & /*value*/) + { + return false; + } + + virtual void config_finalize () + { + // .. nothing yet .. + } + + lay::Plugin *create_plugin (db::Manager *, lay::Dispatcher *root, lay::LayoutView *view) const + { + return new XORPlugin (root, view); + } +}; + +static tl::RegisteredClass config_decl (new lay::XORPluginDeclaration (), 3000, "lay::XORPlugin"); + +} + diff --git a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro new file mode 100644 index 000000000..5b3d29d47 --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro @@ -0,0 +1,21 @@ + +TARGET = xor_ui +DESTDIR = $$OUT_PWD/../../../../lay_plugins + +include($$PWD/../../../lay_plugin.pri) + +INCLUDEPATH += $$RDB_INC $$ANT_INC +DEPENDPATH += $$RDB_INC $$ANT_INC +LIBS += -L$$DESTDIR/.. -lklayout_rdb -lklayout_ant + +HEADERS = \ + layD25View.h \ + layD25ViewWidget.h \ + +SOURCES = \ + layD25View.cc \ + layD25ViewWidget.cc \ + layD25Plugin.cc + +FORMS = \ + D25View.ui \ diff --git a/src/plugins/tools/view_25d/view_25d.pro b/src/plugins/tools/view_25d/view_25d.pro new file mode 100644 index 000000000..f1dd4434b --- /dev/null +++ b/src/plugins/tools/view_25d/view_25d.pro @@ -0,0 +1,6 @@ + +TEMPLATE = subdirs + +!equals(HAVE_QT, "0") { + SUBDIRS = lay_plugin +} From 1e2601d154c4648b056a6202e75ad7c1e60f4f0b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 6 Apr 2020 23:22:22 +0200 Subject: [PATCH 002/113] Fixed some build errors --- src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc | 2 +- src/plugins/tools/view_25d/lay_plugin/layD25View.cc | 3 ++- src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc b/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc index 1883bdf36..d409321b2 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc @@ -61,7 +61,7 @@ public: private: lay::LayoutView *mp_view; - lay::D25ToolDialog *mp_dialog; + lay::D25View *mp_dialog; }; class D25PluginDeclaration diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc index cd2c5518b..18a4f2681 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -22,6 +22,7 @@ #include "layD25View.h" +#include "layLayoutView.h" #include "ui_D25View.h" @@ -31,7 +32,7 @@ namespace lay { D25View::D25View (QWidget *parent) - : QDialog (parent), mp_view (0) + : QDialog (parent) { mp_ui = new Ui::D25View (); mp_ui->setupUi (this); diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index 2cfffbebb..c3fadabb0 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -27,12 +27,12 @@ namespace lay { D25ViewWidget::D25ViewWidget (QWidget *parent) - : QOpenGLWidget (parent), mp_view (0) + : QOpenGLWidget (parent) { // @@@ } -D25View::~D25View () +D25ViewWidget::~D25ViewWidget () { // @@@ } From b7eb150f0320b70029c455822fe38eef6f158c0b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 6 Apr 2020 23:52:51 +0200 Subject: [PATCH 003/113] Fixed some bugs. --- .../tools/view_25d/lay_plugin/D25View.ui | 27 +++++++++++++++---- .../tools/view_25d/lay_plugin/layD25Plugin.cc | 2 +- .../tools/view_25d/lay_plugin/lay_plugin.pro | 2 +- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/D25View.ui b/src/plugins/tools/view_25d/lay_plugin/D25View.ui index f28b5eddf..52ea2c8c4 100644 --- a/src/plugins/tools/view_25d/lay_plugin/D25View.ui +++ b/src/plugins/tools/view_25d/lay_plugin/D25View.ui @@ -30,7 +30,7 @@ 9 - + @@ -38,7 +38,7 @@ Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Close @@ -46,14 +46,31 @@ - lay::D25View + lay::D25ViewWidget QOpenGLWidget -
layD25View.h
+
layD25ViewWidget.h
buttonBox - + + + buttonBox + rejected() + D25View + accept() + + + 530 + 626 + + + 443 + 643 + + + + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc b/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc index d409321b2..12cd9d03c 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc @@ -73,7 +73,7 @@ public: // .. nothing yet .. } - virtual void get_options (std::vector < std::pair > &options) const + virtual void get_options (std::vector < std::pair > & /*options*/) const { // .. nothing yet .. } diff --git a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro index 5b3d29d47..062d2eed4 100644 --- a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro +++ b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro @@ -1,5 +1,5 @@ -TARGET = xor_ui +TARGET = d25_ui DESTDIR = $$OUT_PWD/../../../../lay_plugins include($$PWD/../../../lay_plugin.pri) From a4c2cd34cfeb9085dc7e34f1d2f6d1a5662fd780 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 6 Apr 2020 23:55:30 +0200 Subject: [PATCH 004/113] Fixed some bugs. --- src/plugins/tools/view_25d/lay_plugin/layD25View.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc index 18a4f2681..7a194252c 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -59,6 +59,7 @@ D25View::exec_dialog (lay::LayoutView *view) void D25View::accept () { + QDialog::accept (); // @@@ } From 7472bd4955a68f54f65ba67a1a459b6d2bcc8215 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 10 Apr 2020 22:40:53 +0200 Subject: [PATCH 005/113] WIP: experiments on OpenGL view. --- .../tools/view_25d/lay_plugin/layD25Plugin.cc | 2 + .../view_25d/lay_plugin/layD25ViewWidget.cc | 331 +++++++++++++++++- .../view_25d/lay_plugin/layD25ViewWidget.h | 34 +- 3 files changed, 363 insertions(+), 4 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc b/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc index 12cd9d03c..7f92fcd62 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc @@ -27,6 +27,8 @@ #include "layPlugin.h" +#include + namespace lay { diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index c3fadabb0..e6d572686 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -22,19 +22,344 @@ #include "layD25ViewWidget.h" +#include "tlException.h" + +#include +#include + +#include "math.h" namespace lay { D25ViewWidget::D25ViewWidget (QWidget *parent) - : QOpenGLWidget (parent) + : QOpenGLWidget (parent), + m_program (0), m_dragging (false), m_rotating (false), m_cam_azimuth (0.0), m_cam_elevation (0.0) { - // @@@ + QSurfaceFormat format; + format.setDepthBufferSize (24); + format.setSamples (4); // more -> widget extends beyond boundary! + setFormat (format); + + m_cam_position = QVector3D (0.0, 0.0, 3.0); // @@@ } D25ViewWidget::~D25ViewWidget () { - // @@@ + // Make sure the context is current and then explicitly + // destroy all underlying OpenGL resources. + makeCurrent(); + + delete m_program; + + doneCurrent(); +} + +void +D25ViewWidget::initializeGL () +{ + QOpenGLFunctions::initializeOpenGLFunctions(); + + glEnable (GL_DEPTH_TEST); + glEnable (GL_BLEND); + // @@@ dark background + // glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // @@@ white background + glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA); + + static const char *vertexShaderSource = + "attribute highp vec4 posAttr;\n" + "uniform highp mat4 matrix;\n" + "attribute lowp vec4 colAttr;\n" + "varying lowp vec4 col;\n" + "void main() {\n" + " col = colAttr;\n" + " gl_Position = matrix * posAttr;\n" + "}\n"; + + static const char *fragmentShaderSource = + "varying lowp vec4 col;\n" + "void main() {\n" + " gl_FragColor = col;\n" + "}\n"; + + m_program = new QOpenGLShaderProgram (this); + if (! m_program->addShaderFromSourceCode (QOpenGLShader::Vertex, vertexShaderSource)) { + throw tl::Exception (std::string ("Vertex shader compilation failed:\n") + tl::to_string (m_program->log ())); + } + if (! m_program->addShaderFromSourceCode (QOpenGLShader::Fragment, fragmentShaderSource)) { + throw tl::Exception (std::string ("Fragment shader compilation failed:\n") + tl::to_string (m_program->log ())); + } + if (! m_program->link ()) { + throw tl::Exception (std::string ("Linking failed:\n") + tl::to_string (m_program->log ())); + } + + m_posAttr = m_program->attributeLocation ("posAttr"); + m_colAttr = m_program->attributeLocation ("colAttr"); + m_matrixUniform = m_program->uniformLocation ("matrix"); +} + +static QVector3D cam_direction (double azimuth, double elevation) +{ + // positive azimuth: camera looks left + // positive elevation: camera looks up + double y = sin (elevation * M_PI / 180.0); + double r = cos (elevation * M_PI / 180.0); + double x = r * sin (azimuth * M_PI / 180.0); + double z = r * cos (azimuth * M_PI / 180.0); + return QVector3D (x, y, -z); +} + +void +D25ViewWidget::wheelEvent (QWheelEvent *event) +{ + double cal = 0.6; + + int dx = event->pos ().x () - width () / 2; + int dy = -(event->pos ().y () - height () / 2); + + double da = atan (dx * cal * 2.0 / height ()) * 180 / M_PI; + double de = atan (dy * cal * 2.0 / height ()) * 180 / M_PI; + + m_cam_position += (event->angleDelta ().y () * (1.0 / (45 * 8))) * cam_direction (m_cam_azimuth + da, m_cam_elevation + de); + + update_cam_trans (); +} + +void +D25ViewWidget::mousePressEvent (QMouseEvent *event) +{ + m_dragging = m_rotating = false; + if (event->button () == Qt::MidButton) { + m_dragging = true; + } else if (event->button () == Qt::LeftButton) { + m_rotating = true; + } + + m_start_pos = event->pos (); + m_start_cam_position = m_cam_position; + m_start_cam_azimuth = m_cam_azimuth; + m_start_cam_elevation = m_cam_elevation; +} + +void +D25ViewWidget::mouseReleaseEvent (QMouseEvent *event) +{ + m_dragging = false; +} + +void +D25ViewWidget::mouseMoveEvent (QMouseEvent *event) +{ + double focus_dist = 4.0; // 4 times focal length + + if (m_dragging) { + + // for the chosen perspective transformation: + double cal = 0.6 * focus_dist; + + QPoint d = event->pos () - m_start_pos; + double f = cal * 2.0 / double (height ()); + double dx = d.x () * f; + double dy = -d.y () * f; + + QVector3D xv (cos (m_start_cam_azimuth * M_PI / 180.0), 0.0, -sin (m_start_cam_azimuth * M_PI / 180.0)); + double re = sin (m_start_cam_elevation * M_PI / 180.0); + QVector3D yv (-re * xv.z (), cos (m_start_cam_elevation * M_PI / 180.0), re * xv.x ()); + QVector3D drag = xv * dx + yv * dy; + + // "-drag" because we're not dragging the camera, we're dragging the scene + m_cam_position = m_start_cam_position - drag; + + update_cam_trans (); + + } else if (m_rotating) { + + QPoint d = event->pos () - m_start_pos; + + double ax = atan (d.x () / (0.5 * height ())) * 180 / M_PI; + double ay = atan (-d.y () / (0.5 * height ())) * 180 / M_PI; + + m_cam_elevation = m_start_cam_elevation + ay; + m_cam_azimuth = m_start_cam_azimuth + ax; + + m_cam_position = (cam_direction (m_cam_azimuth, m_cam_elevation) * -focus_dist) + cam_direction (m_start_cam_azimuth, m_start_cam_elevation) * focus_dist + m_start_cam_position; + + update_cam_trans (); + + } +} + +void +D25ViewWidget::update_cam_trans () +{ +printf("@@@ e=%g a=%g x,y,z=%g,%g,%g\n", m_cam_elevation, m_cam_azimuth, m_cam_position.x(), m_cam_position.y(), m_cam_position.z()); fflush(stdout); + QMatrix4x4 t; + + // third: elevation + t.rotate (-m_cam_elevation, 1.0, 0.0, 0.0); + + // second: azimuth + t.rotate (m_cam_azimuth, 0.0, 1.0, 0.0); + + // first: translate the origin into the cam's position + t.translate (-m_cam_position); + + m_cam_trans = t; + + update (); +} + +void +D25ViewWidget::paintGL () +{ + const qreal retinaScale = devicePixelRatio (); + glViewport (0, 0, width () * retinaScale, height () * retinaScale); + + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glClearColor (1.0, 1.0, 1.0, 1.0); + + m_program->bind (); + + QMatrix4x4 matrix; + matrix.perspective (60.0f, float (width ()) / float (height ()), 0.1f, 100.0f); + matrix *= m_cam_trans; + + m_program->setUniformValue (m_matrixUniform, matrix); + + glEnableVertexAttribArray (m_posAttr); + glEnableVertexAttribArray (m_colAttr); + + GLfloat vertices[] = { + 0.0f, 0.707f, -1.0, + -0.5f, -0.5f, -1.0, + 0.5f, -0.5f, -1.0, + -0.6 + 0.0f, 0.0707f, -1.0, + -0.6 + -0.05f, -0.05f, -1.0, + -0.6 + 0.05f, -0.05f, -1.0, + 0.0f, 0.707f, -1.5, + -0.5f, -0.5f, -1.5, + 0.5f, -0.5f, -1.5, + 0.0f, 0.707f, -2.0, + -0.5f, -0.5f, -2.0, + 0.5f, -0.5f, -2.0 + }; + + GLfloat colors[] = { + 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f + }; + + glVertexAttribPointer (m_posAttr, 3, GL_FLOAT, GL_FALSE, 0, vertices); + glVertexAttribPointer (m_colAttr, 3, GL_FLOAT, GL_FALSE, 0, colors); + + glDrawArrays (GL_TRIANGLES, 0, 12); + + GLfloat plane_vertices[] = { + -1.05, 0.0, -2.05, -1.05, 0.0, 0.05, 1.05, 0.0, 0.05, + -1.05, 0.0, -2.05, 1.05, 0.0, 0.05, 1.05, 0.0, -2.05 + }; + + GLfloat plane_colors[] = { + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f + }; + + glVertexAttribPointer (m_posAttr, 3, GL_FLOAT, GL_FALSE, 0, plane_vertices); + glVertexAttribPointer (m_colAttr, 4, GL_FLOAT, GL_FALSE, 0, plane_colors); + + glDrawArrays (GL_TRIANGLES, 0, 6); + + GLfloat gridline_vertices[] = { + -1.0, 0.0, -2.0, -1.0, 0.0, 0.0, + -0.75, 0.0, -2.0, -0.75, 0.0, 0.0, + -0.5, 0.0, -2.0, -0.5, 0.0, 0.0, + -0.25, 0.0, -2.0, -0.25, 0.0, 0.0, + 0.0, 0.0, -2.0, 0.0, 0.0, 0.0, + 0.25, 0.0, -2.0, 0.25, 0.0, 0.0, + 0.5, 0.0, -2.0, 0.5, 0.0, 0.0, + 0.75, 0.0, -2.0, 0.75, 0.0, 0.0, + 1.0, 0.0, -2.0, 1.0, 0.0, 0.0, + 1.0, 0.0, -2.0, -1.0, 0.0, -2.0, + 1.0, 0.0, -1.75, -1.0, 0.0, -1.75, + 1.0, 0.0, -1.5 , -1.0, 0.0, -1.5, + 1.0, 0.0, -1.25, -1.0, 0.0, -1.25, + 1.0, 0.0, -1.0, -1.0, 0.0, -1.0, + 1.0, 0.0, -0.75, -1.0, 0.0, -0.75, + 1.0, 0.0, -0.5 , -1.0, 0.0, -0.5, + 1.0, 0.0, -0.25, -1.0, 0.0, -0.25, + 1.0, 0.0, 0.0, -1.0, 0.0, 0.0 + }; + + GLfloat gridline_colors[] = { + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f, + 1.0f, 1.0f, 1.0f, 0.2f + }; + + glVertexAttribPointer (m_posAttr, 3, GL_FLOAT, GL_FALSE, 0, gridline_vertices); + glVertexAttribPointer (m_colAttr, 4, GL_FLOAT, GL_FALSE, 0, gridline_colors); + + glLineWidth (2.0); + glDrawArrays (GL_LINES, 0, 36); + + glDisableVertexAttribArray (m_posAttr); + glDisableVertexAttribArray (m_colAttr); + + m_program->release (); +} + +void +D25ViewWidget::resizeGL (int /*w*/, int /*h*/) +{ + update_cam_trans (); } } diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h index 428f466f3..aec8bbb33 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h @@ -24,18 +24,50 @@ #define HDR_layD25ViewWidget #include +#include +#include +#include +#include +#include +#include +#include namespace lay { class D25ViewWidget - : public QOpenGLWidget + : public QOpenGLWidget, + private QOpenGLFunctions { Q_OBJECT public: D25ViewWidget (QWidget *parent); ~D25ViewWidget (); + + void wheelEvent (QWheelEvent *event); + void mousePressEvent (QMouseEvent *event); + void mouseReleaseEvent (QMouseEvent *event); + void mouseMoveEvent (QMouseEvent *event); + +private: + QOpenGLShaderProgram *m_program; + GLuint m_posAttr; + GLuint m_colAttr; + GLuint m_matrixUniform; + QMatrix4x4 m_cam_trans; + bool m_dragging, m_rotating; + QVector3D m_cam_position; + double m_cam_azimuth, m_cam_elevation; + QPoint m_start_pos; + QVector3D m_start_cam_position; + double m_start_cam_azimuth, m_start_cam_elevation; + + void initializeGL (); + void paintGL (); + void resizeGL (int w, int h); + + void update_cam_trans (); }; } From 731dfffe1e03bece968086cab003d6c8294b8bb2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Apr 2020 11:21:36 +0200 Subject: [PATCH 006/113] WIP --- .../view_25d/lay_plugin/layD25MemChunks.cc | 0 .../view_25d/lay_plugin/layD25MemChunks.h | 0 .../unit_tests/layD25MemChunksTests.cc | 486 ++++++++++++++++++ .../tools/view_25d/unit_tests/unit_tests.pro | 19 + 4 files changed, 505 insertions(+) create mode 100644 src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.cc create mode 100644 src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.h create mode 100644 src/plugins/tools/view_25d/unit_tests/layD25MemChunksTests.cc create mode 100644 src/plugins/tools/view_25d/unit_tests/unit_tests.pro diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.cc b/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.cc new file mode 100644 index 000000000..e69de29bb diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.h b/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/plugins/tools/view_25d/unit_tests/layD25MemChunksTests.cc b/src/plugins/tools/view_25d/unit_tests/layD25MemChunksTests.cc new file mode 100644 index 000000000..ab34661b6 --- /dev/null +++ b/src/plugins/tools/view_25d/unit_tests/layD25MemChunksTests.cc @@ -0,0 +1,486 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "dbDXFReader.h" +#include "dbTestSupport.h" +#include "tlUnitTest.h" + +#include + +static db::LayerMap string2lm (const char *map) +{ + db::LayerMap lm; + unsigned int ln = 0; + tl::Extractor ex (map); + while (! ex.at_end ()) { + std::string n; + int l; + ex.read_word_or_quoted (n); + ex.test (":"); + ex.read (l); + ex.test (","); + lm.map (n, ln++, db::LayerProperties (l, 0)); + } + return lm; +} + +static void do_run_test (tl::TestBase *_this, const std::string &fn, const std::string &fn_au, const db::DXFReaderOptions &opt, bool as_oas) +{ + db::LoadLayoutOptions options; + options.set_options (new db::DXFReaderOptions (opt)); + + db::Layout layout; + + { + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (layout, options); + } + + db::compare_layouts (_this, layout, fn_au, as_oas ? db::WriteOAS : db::WriteGDS2, 1); +} + +static void run_test (tl::TestBase *_this, const char *file, const char *file_au, const db::DXFReaderOptions &opt = db::DXFReaderOptions (), bool as_oas = false) +{ + std::string fn = tl::testsrc_private () + "/testdata/dxf/" + file; + std::string fn_au = tl::testsrc_private () + std::string ("/testdata/dxf/") + file_au; + + do_run_test (_this, fn, fn_au, opt, as_oas); +} + +static void run_test_public (tl::TestBase *_this, const char *file, const char *file_au, const db::DXFReaderOptions &opt = db::DXFReaderOptions (), bool as_oas = false) +{ + std::string fn = tl::testsrc () + "/testdata/dxf/" + file; + std::string fn_au = tl::testsrc () + std::string ("/testdata/dxf/") + file_au; + + do_run_test (_this, fn, fn_au, opt, as_oas); +} + +TEST(KeepLN1) +{ + db::DXFReaderOptions opt; + run_test_public (_this, "keep_ln.dxf.gz", "keep_ln1_au.oas.gz", opt, true /*because of layer names*/); +} + +TEST(KeepLN2) +{ + db::DXFReaderOptions opt; + opt.keep_layer_names = true; + run_test_public (_this, "keep_ln.dxf.gz", "keep_ln2_au.oas.gz", opt, true /*because of layer names*/); +} + +TEST(1a) +{ + run_test (_this, "t1.dxf.gz", "t1a_au.gds.gz"); +} + +TEST(1b) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.01; + opt.unit = 5.0; + run_test (_this, "t1.dxf.gz", "t1b_au.gds.gz", opt); +} + +TEST(2) +{ + run_test (_this, "t2.dxf.gz", "t2_au.gds.gz"); +} + +TEST(3) +{ + run_test (_this, "t3.dxf.gz", "t3_au.gds.gz"); +} + +TEST(4) +{ + db::DXFReaderOptions opt; + opt.layer_map = string2lm ("Metal:1,Metal2:5"); + opt.create_other_layers = true; + run_test (_this, "t4.dxf.gz", "t4_au.gds.gz", opt); +} + +TEST(5) +{ + run_test (_this, "t5.dxf.gz", "t5_au.gds.gz"); +} + +TEST(6) +{ + run_test (_this, "t6.dxf.gz", "t6_au.gds.gz"); +} + +TEST(7) +{ + run_test (_this, "t7.dxf.gz", "t7_au.gds.gz"); +} + +TEST(8) +{ + db::DXFReaderOptions opt; + opt.layer_map = string2lm ("Metal:4,Kommentare:3,Bemassung:2"); + opt.create_other_layers = true; + run_test (_this, "t8.dxf.gz", "t8_au.gds.gz", opt); +} + +TEST(9) +{ + db::DXFReaderOptions opt; + opt.layer_map = string2lm ("Bemassung:2,Metal:5,Kommentare:4"); + opt.create_other_layers = true; + run_test (_this, "t9.dxf.gz", "t9_au.gds.gz", opt); +} + +TEST(10) +{ + db::DXFReaderOptions opt; + opt.layer_map = string2lm ("METAL:1,KOMMENTARE:4"); + opt.create_other_layers = true; + run_test (_this, "t10.dxf.gz", "t10_au.gds.gz", opt); +} + +TEST(11) +{ + run_test (_this, "t11.dxf.gz", "t11_au.gds.gz"); +} + +TEST(12) +{ + run_test (_this, "t12.dxf.gz", "t12_au.gds.gz"); +} + +TEST(14) +{ + db::DXFReaderOptions opt; + opt.layer_map = string2lm ("'A11-STRUKTUR__E_TYP_':10,A21_NITRID:11,'B11-KONTAKT':9,'B11-STRUKTUR':3,HELLFELD:7,MASKE:5,NORM_MIN_MAX_WAFER:6,RASTER:2,_BEGRENZUNG_A11_A21_A31_B1:8"); + opt.create_other_layers = true; + run_test (_this, "t14.dxf.gz", "t14_au.gds.gz", opt); +} + +TEST(15) +{ + db::DXFReaderOptions opt; + opt.layer_map = string2lm ("TEXT:4,IGBT:5,Wire:7,Ceramic:11,LAYER_1:14,Diode:18,'DBC TOP Plate':19,'Terminal Position':20"); + opt.create_other_layers = true; + run_test (_this, "t15.dxf.gz", "t15_au.gds.gz", opt); +} + +TEST(16) +{ + db::DXFReaderOptions opt; + opt.layer_map = string2lm ("TEXT:4,IGBT:5,Wire:7,Ceramic:11,LAYER_1:14,Diode:18,'DBC TOP Plate':19,'Terminal Position':20"); + opt.create_other_layers = true; + run_test (_this, "t16.dxf.gz", "t16_au.gds.gz", opt); +} + +TEST(17) +{ + db::DXFReaderOptions opt; + opt.layer_map = string2lm ("TEXT:4,IGBT:5,Wire:7,Ceramic:11,LAYER_1:14,Diode:18,'DBC TOP Plate':19,'Terminal Position':20"); + opt.create_other_layers = true; + run_test (_this, "t17.dxf.gz", "t17_au.gds.gz", opt); +} + +TEST(18) +{ + run_test (_this, "t18.dxf.gz", "t18_au.gds.gz"); +} + +TEST(19) +{ + run_test (_this, "t19.dxf.gz", "t19_au.gds.gz"); +} + +TEST(20) +{ + run_test (_this, "t20.dxf.gz", "t20_au.gds.gz"); +} + +TEST(21) +{ + run_test (_this, "t21.dxf.gz", "t21_au.gds.gz"); +} + +TEST(22) +{ + run_test (_this, "t22.dxf.gz", "t22_au.gds.gz"); +} + +TEST(23a) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1; + opt.polyline_mode = 0; + opt.circle_points = 10; + run_test (_this, "t23.dxf.gz", "t23a_au.gds.gz", opt); +} + +TEST(23b) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1; + opt.polyline_mode = 1; + opt.circle_points = 10; + run_test (_this, "t23.dxf.gz", "t23b_au.gds.gz", opt); +} + +TEST(23c) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1; + opt.polyline_mode = 2; + opt.circle_points = 10; + run_test (_this, "t23.dxf.gz", "t23c_au.gds.gz", opt); +} + +TEST(23d) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1; + opt.polyline_mode = 3; + opt.circle_points = 10; + run_test (_this, "t23.dxf.gz", "t23d_au.gds.gz", opt); +} + +TEST(23e) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1; + opt.polyline_mode = 4; + opt.circle_points = 10; + run_test (_this, "t23.dxf.gz", "t23e_au.gds.gz", opt); +} + +TEST(26a) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1; + opt.polyline_mode = 0; + opt.circle_points = 100; + run_test (_this, "t26.dxf.gz", "t26a_au.gds.gz", opt); +} + +TEST(26b) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1; + opt.polyline_mode = 1; + opt.circle_points = 100; + run_test (_this, "t26.dxf.gz", "t26b_au.gds.gz", opt); +} + +TEST(26c) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1; + opt.polyline_mode = 2; + opt.circle_points = 100; + run_test (_this, "t26.dxf.gz", "t26c_au.gds.gz", opt); +} + +TEST(26d) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1; + opt.polyline_mode = 3; + opt.circle_points = 100; + run_test (_this, "t26.dxf.gz", "t26d_au.gds.gz", opt); +} + +TEST(26e) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1; + opt.polyline_mode = 4; + opt.circle_points = 100; + run_test (_this, "t26.dxf.gz", "t26e_au.gds.gz", opt); +} + +TEST(27a) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1; + opt.polyline_mode = 0; + opt.circle_points = 10; + run_test (_this, "t27.dxf.gz", "t27a_au.gds.gz", opt); +} + +TEST(27b) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1; + opt.polyline_mode = 1; + opt.circle_points = 10; + run_test (_this, "t27.dxf.gz", "t27b_au.gds.gz", opt); +} + +TEST(27c) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1; + opt.polyline_mode = 2; + opt.circle_points = 10; + run_test (_this, "t27.dxf.gz", "t27c_au.gds.gz", opt); +} + +TEST(27d) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1; + opt.polyline_mode = 3; + opt.circle_points = 10; + run_test (_this, "t27.dxf.gz", "t27d_au.gds.gz", opt); +} + +TEST(27e) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1; + opt.polyline_mode = 4; + opt.circle_points = 10; + run_test (_this, "t27.dxf.gz", "t27e_au.gds.gz", opt); +} + +TEST(28) +{ + run_test (_this, "t28.dxf.gz", "t28_au.gds.gz"); +} + +TEST(29) +{ + run_test (_this, "t29.dxf.gz", "t29_au.gds.gz"); +} + +TEST(29a) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1; + opt.polyline_mode = 4; + opt.circle_points = 1000; + opt.circle_accuracy = 1; + run_test (_this, "t29.dxf.gz", "t29a_au.gds.gz", opt); +} + +TEST(29b) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1; + opt.polyline_mode = 4; + opt.circle_points = 1000; + opt.circle_accuracy = 0.1; + run_test (_this, "t29.dxf.gz", "t29b_au.gds.gz", opt); +} + +TEST(29c) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1; + opt.polyline_mode = 4; + opt.circle_points = 1000; + opt.circle_accuracy = 0.01; + run_test (_this, "t29.dxf.gz", "t29c_au.gds.gz", opt); +} + +TEST(29d) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1; + opt.polyline_mode = 4; + opt.circle_points = 1000; + opt.circle_accuracy = 0.001; + run_test (_this, "t29.dxf.gz", "t29d_au.gds.gz", opt); +} + +TEST(30) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1000; + opt.polyline_mode = 4; + opt.circle_points = 1000; + opt.circle_accuracy = 0.001; + run_test (_this, "t30.dxf.gz", "t30d_au.gds.gz", opt); +} + +// accuracy +TEST(31) +{ + db::DXFReaderOptions opt; + opt.dbu = 0.001; + opt.unit = 1000; + + opt.contour_accuracy = 0; + run_test (_this, "t31.dxf.gz", "t31a_au.gds.gz", opt); + + opt.contour_accuracy = 0.005; + run_test (_this, "t31.dxf.gz", "t31b_au.gds.gz", opt); + + opt.contour_accuracy = 0.01; + run_test (_this, "t31.dxf.gz", "t31c_au.gds.gz", opt); + + opt.contour_accuracy = 0.02; + run_test (_this, "t31.dxf.gz", "t31d_au.gds.gz", opt); +} + +// issue #198 +TEST(32) +{ + db::DXFReaderOptions opt; + opt.layer_map = string2lm ("L11D0:1,L12D0:2"); + opt.create_other_layers = false; + opt.polyline_mode = 3; + + opt.contour_accuracy = 0.0; + run_test_public (_this, "round_path.dxf.gz", "t32a_au.gds.gz", opt); + + opt.contour_accuracy = 0.1; + run_test_public (_this, "round_path.dxf.gz", "t32b_au.gds.gz", opt); + + opt.contour_accuracy = 1.0; + run_test_public (_this, "round_path.dxf.gz", "t32c_au.gds.gz", opt); + + opt.polyline_mode = 4; + run_test_public (_this, "round_path.dxf.gz", "t32d_au.gds.gz", opt); + + opt.polyline_mode = 2; + run_test_public (_this, "round_path.dxf.gz", "t32e_au.gds.gz", opt); +} diff --git a/src/plugins/tools/view_25d/unit_tests/unit_tests.pro b/src/plugins/tools/view_25d/unit_tests/unit_tests.pro new file mode 100644 index 000000000..22fd790b2 --- /dev/null +++ b/src/plugins/tools/view_25d/unit_tests/unit_tests.pro @@ -0,0 +1,19 @@ + +DESTDIR_UT = $$OUT_PWD/../../../.. + +TARGET = view_25d_tests + +include($$PWD/../../../../lib_ut.pri) + +SOURCES = \ + layD25MemChunksTests.cc \ + +INCLUDEPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../lay_plugin $$PWD/../../../common +DEPENDPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../lay_plugin $$PWD/../../../common + +LIBS += -L$$DESTDIR_UT -lklayout_db -lklayout_tl -lklayout_gsi + +PLUGINPATH = $$OUT_PWD/../../../../lay_plugins +QMAKE_RPATHDIR += $$PLUGINPATH + +LIBS += -L$$PLUGINPATH -ld25_ui From b5a51f15d09e9739e077e639272a4e342cca8dc9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Apr 2020 11:21:59 +0200 Subject: [PATCH 007/113] WIP --- .../tools/view_25d/lay_plugin/D25View.ui | 2 +- .../view_25d/lay_plugin/layD25MemChunks.cc | 23 + .../view_25d/lay_plugin/layD25MemChunks.h | 222 ++++++++ .../tools/view_25d/lay_plugin/layD25View.cc | 9 +- .../view_25d/lay_plugin/layD25ViewWidget.cc | 469 ++++++++++++---- .../view_25d/lay_plugin/layD25ViewWidget.h | 35 +- .../tools/view_25d/lay_plugin/lay_plugin.pro | 4 +- .../unit_tests/layD25MemChunksTests.cc | 510 ++---------------- src/plugins/tools/view_25d/view_25d.pro | 2 +- 9 files changed, 699 insertions(+), 577 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/D25View.ui b/src/plugins/tools/view_25d/lay_plugin/D25View.ui index 52ea2c8c4..3e8f0fc83 100644 --- a/src/plugins/tools/view_25d/lay_plugin/D25View.ui +++ b/src/plugins/tools/view_25d/lay_plugin/D25View.ui @@ -30,7 +30,7 @@ 9 - + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.cc b/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.cc index e69de29bb..c80a2d5a7 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.cc @@ -0,0 +1,23 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "layD25MemChunks.h" diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.h b/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.h index e69de29bb..034aa5377 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.h @@ -0,0 +1,222 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_layD25MemChunks +#define HDR_layD25MemChunks + +#include + +#include "tlObject.h" +#include // for memcpy + +namespace lay +{ + +/** + * @brief Provides a semi-contiguous array of objects + * + * The objects are kept in chunks of ChunkLen items. + * The blocks can be accessed individually. The array can be + * cleared and new items can be added. No insert or delete. + * + * This object is intended to be used for keeping vertex, + * color or point data for OpenGL. + */ +template +class mem_chunks + : public QDialog +{ +public: + + struct chunk { + public: + chunk () + : m_len (0), m_next (0) + { + // .. nothing yet .. + } + + chunk (const chunk &other) + : m_len (0), m_next (0) + { + operator= (other); + } + + chunk &operator= (const chunk &other) + { + if (this != &other) { + memcpy (&m_objects, &other.m_objects, sizeof (m_objects)); + m_len = other.m_len; + } + return *this; + } + + const Obj *front () const { return m_objects; } + size_t size () const { return m_len; } + const chunk *next () const { return m_next; } + + private: + friend class mem_chunks; + + Obj m_objects [ChunkLen]; + size_t m_len; + chunk *m_next; + }; + + class iterator + { + public: + iterator (chunk *ch = 0) + : mp_chunk (ch) + { + // .. nothing yet .. + } + + bool operator== (iterator other) const + { + return mp_chunk == other.mp_chunk; + } + + bool operator!= (iterator other) const + { + return mp_chunk != other.mp_chunk; + } + + const chunk &operator* () const + { + return *mp_chunk; + } + + const chunk *operator-> () const + { + return mp_chunk; + } + + void operator++ () + { + mp_chunk = mp_chunk->next (); + } + + private: + const chunk *mp_chunk; + }; + + /** + * @brief Default constructor + * Creates an empty array + */ + mem_chunks () + : mp_chunks (0), mp_last_chunk (0) + { + // .. nothing yet .. + } + + /** + * @brief Destructor + */ + ~mem_chunks () + { + clear (); + } + + /** + * @brief Copy constructor + */ + mem_chunks (const mem_chunks &other) + : mp_chunks (0), mp_last_chunk (0) + { + operator= (other); + } + + /** + * @brief Assignment + */ + mem_chunks &operator= (const mem_chunks &other) + { + if (this != &other) { + clear (); + const chunk *ch = other.mp_chunks; + while (ch) { + if (! mp_chunks) { + mp_last_chunk = mp_chunks = new chunk (*ch); + } else { + mp_last_chunk->m_next = new chunk (*ch); + mp_last_chunk = mp_last_chunk->m_next; + } + ch = ch->next (); + } + } + + return *this; + } + + /** + * @brief Clears the array + */ + void clear () + { + chunk *ch = mp_chunks; + mp_chunks = 0; + mp_last_chunk = 0; + while (ch) { + chunk *del = ch; + ch = ch->m_next; + delete del; + } + } + + /** + * @brief Adds an element to the array + */ + void add (const Obj &element) + { + if (! mp_last_chunk) { + mp_chunks = mp_last_chunk = new chunk (); + } + + if (mp_last_chunk->m_len >= ChunkLen) { + mp_last_chunk->m_next = new chunk (); + mp_last_chunk = mp_last_chunk->m_next; + } + + mp_last_chunk->m_objects [mp_last_chunk->m_len++] = element; + } + + /** + * @brief begin iterator + */ + iterator begin () const { return iterator (mp_chunks); } + + /** + * @brief end iterator + */ + iterator end () const { return iterator (); } + +private: + chunk *mp_chunks; + chunk *mp_last_chunk; +}; + +} + +#endif + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc index 7a194252c..44f905517 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -50,10 +50,13 @@ int D25View::exec_dialog (lay::LayoutView *view) { mp_view.reset (view); + mp_ui->d25_view->attach_view (view); - // @@@ - - return QDialog::exec (); + int ret = QDialog::exec (); + + mp_ui->d25_view->attach_view (0); + + return ret; } void diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index e6d572686..b204800c0 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -22,6 +22,13 @@ #include "layD25ViewWidget.h" +#include "layLayoutView.h" + +#include "dbRecursiveShapeIterator.h" +#include "dbEdgeProcessor.h" +#include "dbPolygonGenerators.h" +#include "dbPolygonTools.h" + #include "tlException.h" #include @@ -32,9 +39,11 @@ namespace lay { +// ------------------------------------------------------------------------------ + D25ViewWidget::D25ViewWidget (QWidget *parent) : QOpenGLWidget (parent), - m_program (0), m_dragging (false), m_rotating (false), m_cam_azimuth (0.0), m_cam_elevation (0.0) + m_shapes_program (0), m_dragging (false), m_rotating (false), m_cam_azimuth (0.0), m_cam_elevation (0.0) { QSurfaceFormat format; format.setDepthBufferSize (24); @@ -50,7 +59,7 @@ D25ViewWidget::~D25ViewWidget () // destroy all underlying OpenGL resources. makeCurrent(); - delete m_program; + delete m_shapes_program; doneCurrent(); } @@ -63,40 +72,110 @@ D25ViewWidget::initializeGL () glEnable (GL_DEPTH_TEST); glEnable (GL_BLEND); // @@@ dark background - // glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // @@@ white background - glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA); + // @@@ glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA); - static const char *vertexShaderSource = - "attribute highp vec4 posAttr;\n" - "uniform highp mat4 matrix;\n" - "attribute lowp vec4 colAttr;\n" - "varying lowp vec4 col;\n" + static const char *shapes_vertex_shader_source = + "#version 320 es\n" + "#undef lowp\n" + "#undef highp\n" + "#undef mediump\n" + "layout (location = 0) in vec4 posAttr;\n" + "\n" + "void main() {\n" + " gl_Position = posAttr;\n" + "}\n"; + + static const char *shapes_geometry_shader_source = + "#version 320 es\n" + "#undef lowp\n" + "#undef highp\n" + "#undef mediump\n" + "\n" + "uniform vec4 color;\n" + "uniform vec3 illum;\n" + "out lowp vec4 vertexColor;\n" + "uniform mat4 matrix;\n" + "layout (triangles) in;\n" + "layout (triangle_strip, max_vertices = 3) out;\n" + "\n" + "void main() {\n" + " vec4 p0 = gl_in[0].gl_Position;\n" + " vec4 p1 = gl_in[1].gl_Position;\n" + " vec4 p2 = gl_in[2].gl_Position;\n" + " vec3 n = cross(p1.xyz - p0.xyz, p2.xyz - p0.xyz);\n" + " vertexColor.rgb = color.rgb * (max(0.0, dot(normalize(n), illum)) * 0.5 + 0.5);\n" + " vertexColor.a = 1.0;\n" + " gl_Position = matrix * p0;\n" + " EmitVertex();\n" + " gl_Position = matrix * p1;\n" + " EmitVertex();\n" + " gl_Position = matrix * p2;\n" + " EmitVertex();\n" + " EndPrimitive();\n" + "}\n"; + + static const char *shapes_fragment_shader_source = + "#version 320 es\n" + "#undef lowp\n" + "#undef highp\n" + "#undef mediump\n" + "in lowp vec4 vertexColor;\n" + "out lowp vec4 fragColor;\n" + "void main() {\n" + " fragColor = vertexColor;\n" + "}\n"; + + m_shapes_program = new QOpenGLShaderProgram (this); + if (! m_shapes_program->addShaderFromSourceCode (QOpenGLShader::Vertex, shapes_vertex_shader_source)) { + throw tl::Exception (std::string ("Shapes vertex shader compilation failed:\n") + tl::to_string (m_shapes_program->log ())); + } + if (! m_shapes_program->addShaderFromSourceCode (QOpenGLShader::Geometry, shapes_geometry_shader_source)) { + throw tl::Exception (std::string ("Shapes geometry shader compilation failed:\n") + tl::to_string (m_shapes_program->log ())); + } + if (! m_shapes_program->addShaderFromSourceCode (QOpenGLShader::Fragment, shapes_fragment_shader_source)) { + throw tl::Exception (std::string ("Shapes fragment shader compilation failed:\n") + tl::to_string (m_shapes_program->log ())); + } + if (! m_shapes_program->link ()) { + throw tl::Exception (std::string ("Shapes shader program linking failed failed:\n") + tl::to_string (m_shapes_program->log ())); + } + + // grid plane shader source + + static const char *gridplan_vertex_shader_source = + "#version 320 es\n" + "#undef lowp\n" + "#undef highp\n" + "#undef mediump\n" + "layout (location = 0) in vec4 posAttr;\n" + "uniform mat4 matrix;\n" + "\n" "void main() {\n" - " col = colAttr;\n" " gl_Position = matrix * posAttr;\n" "}\n"; - static const char *fragmentShaderSource = - "varying lowp vec4 col;\n" + static const char *gridplan_fragment_shader_source = + "#version 320 es\n" + "#undef lowp\n" + "#undef highp\n" + "#undef mediump\n" + "uniform lowp vec4 color;\n" + "out lowp vec4 fragColor;\n" "void main() {\n" - " gl_FragColor = col;\n" + " fragColor = color;\n" "}\n"; - m_program = new QOpenGLShaderProgram (this); - if (! m_program->addShaderFromSourceCode (QOpenGLShader::Vertex, vertexShaderSource)) { - throw tl::Exception (std::string ("Vertex shader compilation failed:\n") + tl::to_string (m_program->log ())); + m_gridplane_program = new QOpenGLShaderProgram (this); + if (! m_gridplane_program->addShaderFromSourceCode (QOpenGLShader::Vertex, gridplan_vertex_shader_source)) { + throw tl::Exception (std::string ("Grid plane vertex shader compilation failed:\n") + tl::to_string (m_gridplane_program->log ())); } - if (! m_program->addShaderFromSourceCode (QOpenGLShader::Fragment, fragmentShaderSource)) { - throw tl::Exception (std::string ("Fragment shader compilation failed:\n") + tl::to_string (m_program->log ())); + if (! m_gridplane_program->addShaderFromSourceCode (QOpenGLShader::Fragment, gridplan_fragment_shader_source)) { + throw tl::Exception (std::string ("Grid plane fragment shader compilation failed:\n") + tl::to_string (m_gridplane_program->log ())); } - if (! m_program->link ()) { - throw tl::Exception (std::string ("Linking failed:\n") + tl::to_string (m_program->log ())); + if (! m_gridplane_program->link ()) { + throw tl::Exception (std::string ("Grid plane shader program linking failed:\n") + tl::to_string (m_gridplane_program->log ())); } - - m_posAttr = m_program->attributeLocation ("posAttr"); - m_colAttr = m_program->attributeLocation ("colAttr"); - m_matrixUniform = m_program->uniformLocation ("matrix"); } static QVector3D cam_direction (double azimuth, double elevation) @@ -143,7 +222,7 @@ D25ViewWidget::mousePressEvent (QMouseEvent *event) } void -D25ViewWidget::mouseReleaseEvent (QMouseEvent *event) +D25ViewWidget::mouseReleaseEvent (QMouseEvent * /*event*/) { m_dragging = false; } @@ -210,6 +289,225 @@ printf("@@@ e=%g a=%g x,y,z=%g,%g,%g\n", m_cam_elevation, m_cam_azimuth, m update (); } +void +D25ViewWidget::attach_view (LayoutView *view) +{ + if (mp_view != view) { + + mp_view = view; + m_layers.clear (); + m_vertex_chunks.clear (); + + if (mp_view) { + prepare_view (); + } + + } +} + +void +D25ViewWidget::prepare_view () +{ + double z = 0.0, dz = 0.2; // @@@ + + for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) { + + if (! lp->has_children () && lp->visible (true) && lp->cellview_index () >= 0 && lp->cellview_index () < int (mp_view->cellviews ())) { + + lay::color_t color = lp->fill_color (true); + + m_vertex_chunks.push_back (chunks_type ()); + + LayerInfo info; + // @@@ use alpha? + info.color[0] = (color & 0xff) / 255.0f; + info.color[1] = ((color >> 8) & 0xff) / 255.0f; + info.color[2] = ((color >> 16) & 0xff) / 255.0f; + info.color[3] = 1.0; + info.vertex_chunk = &m_vertex_chunks.back (); + + m_layers.push_back (info); + + const lay::CellView &cv = mp_view->cellview ((unsigned int) lp->cellview_index ()); + render_layout (m_vertex_chunks.back (), cv->layout (), *cv.cell (), (unsigned int) lp->layer_index (), z, z + dz); + + z += dz; // @@@ + + } + + } +} + +void +D25ViewWidget::render_polygon (D25ViewWidget::chunks_type &chunks, const db::Polygon &poly, double dbu, double zstart, double zstop) +{ + if (poly.hull ().size () > 4) { + + std::vector poly_heap; + + db::split_polygon (poly, poly_heap); + for (std::vector::const_iterator p = poly_heap.begin (); p != poly_heap.end (); ++p) { + render_polygon (chunks, *p, dbu, zstart, zstop); + } + + } else if (poly.hull ().size () >= 3) { + + db::Point pts [4]; + std::copy (poly.hull ().begin (), poly.hull ().end (), &pts [0]); + + // triangle bottom + + chunks.add (pts[0].x () * dbu); + chunks.add (zstart); + chunks.add (pts[0].y () * dbu); + + chunks.add (pts[2].x () * dbu); + chunks.add (zstart); + chunks.add (pts[2].y () * dbu); + + chunks.add (pts[1].x () * dbu); + chunks.add (zstart); + chunks.add (pts[1].y () * dbu); + + // triangle top + + chunks.add (pts[0].x () * dbu); + chunks.add (zstop); + chunks.add (pts[0].y () * dbu); + + chunks.add (pts[1].x () * dbu); + chunks.add (zstop); + chunks.add (pts[1].y () * dbu); + + chunks.add (pts[2].x () * dbu); + chunks.add (zstop); + chunks.add (pts[2].y () * dbu); + + if (poly.hull ().size () == 4) { + + // triangle bottom + + chunks.add (pts[0].x () * dbu); + chunks.add (zstart); + chunks.add (pts[0].y () * dbu); + + chunks.add (pts[3].x () * dbu); + chunks.add (zstart); + chunks.add (pts[3].y () * dbu); + + chunks.add (pts[2].x () * dbu); + chunks.add (zstart); + chunks.add (pts[2].y () * dbu); + + // triangle top + + chunks.add (pts[0].x () * dbu); + chunks.add (zstop); + chunks.add (pts[0].y () * dbu); + + chunks.add (pts[2].x () * dbu); + chunks.add (zstop); + chunks.add (pts[2].y () * dbu); + + chunks.add (pts[3].x () * dbu); + chunks.add (zstop); + chunks.add (pts[3].y () * dbu); + + } + + } +} + +void +D25ViewWidget::render_wall (D25ViewWidget::chunks_type &chunks, const db::Edge &edge, double dbu, double zstart, double zstop) +{ + chunks.add (edge.p1 ().x () * dbu); + chunks.add (zstart); + chunks.add (edge.p1 ().y () * dbu); + + chunks.add (edge.p2 ().x () * dbu); + chunks.add (zstop); + chunks.add (edge.p2 ().y () * dbu); + + chunks.add (edge.p1 ().x () * dbu); + chunks.add (zstop); + chunks.add (edge.p1 ().y () * dbu); + + chunks.add (edge.p1 ().x () * dbu); + chunks.add (zstart); + chunks.add (edge.p1 ().y () * dbu); + + chunks.add (edge.p2 ().x () * dbu); + chunks.add (zstart); + chunks.add (edge.p2 ().y () * dbu); + + chunks.add (edge.p2 ().x () * dbu); + chunks.add (zstop); + chunks.add (edge.p2 ().y () * dbu); +} + +void +D25ViewWidget::render_layout (D25ViewWidget::chunks_type &chunks, const db::Layout &layout, const db::Cell &cell, unsigned int layer, double zstart, double zstop) +{ + db::EdgeProcessor ep; + std::vector poly_heap; + + // @@@ hidden cells, hierarchy depth ... + db::RecursiveShapeIterator s (layout, cell, layer); + s.shape_flags (db::ShapeIterator::Polygons | db::ShapeIterator::Paths | db::ShapeIterator::Boxes); + for ( ; ! s.at_end (); ++s) { + + db::Polygon polygon; + s->polygon (polygon); + polygon.transform (s.trans ()); + + if (polygon.holes () == 0 && polygon.hull ().size () <= 4) { + + render_polygon (chunks, polygon, layout.dbu (), zstart, zstop); + + for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) { + render_wall (chunks, *e, layout.dbu (), zstart, zstop); + } + + } else { + + poly_heap.clear (); + ep.clear (); + + ep.insert_sequence (polygon.begin_edge ()); + { + db::PolygonContainer pc (poly_heap); + db::PolygonGenerator out (pc, true /*resolve holes*/, false /*min coherence for splitting*/); + db::SimpleMerge op; + ep.process (out, op); + } + + for (std::vector::const_iterator p = poly_heap.begin (); p != poly_heap.end (); ++p) { + render_polygon (chunks, *p, layout.dbu (), zstart, zstop); + } + + poly_heap.clear (); + ep.clear (); + + ep.insert_sequence (polygon.begin_edge ()); + { + db::PolygonContainer pc (poly_heap); + db::PolygonGenerator out (pc, false /*don't resolve holes*/, false /*min coherence for splitting*/); + db::SimpleMerge op; + ep.process (out, op); + } + + for (std::vector::const_iterator p = poly_heap.begin (); p != poly_heap.end (); ++p) { + for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { + render_wall (chunks, *e, layout.dbu (), zstart, zstop); + } + } + + } + + } +} + void D25ViewWidget::paintGL () { @@ -217,70 +515,52 @@ D25ViewWidget::paintGL () glViewport (0, 0, width () * retinaScale, height () * retinaScale); glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glClearColor (1.0, 1.0, 1.0, 1.0); + // @@@ white background: glClearColor (1.0, 1.0, 1.0, 1.0); - m_program->bind (); + const int positions = 0; + + m_shapes_program->bind (); QMatrix4x4 matrix; matrix.perspective (60.0f, float (width ()) / float (height ()), 0.1f, 100.0f); matrix *= m_cam_trans; - m_program->setUniformValue (m_matrixUniform, matrix); + m_shapes_program->setUniformValue ("matrix", matrix); + m_shapes_program->setUniformValue ("illum", QVector3D (-3.0, -4.0, 2.0).normalized ()); - glEnableVertexAttribArray (m_posAttr); - glEnableVertexAttribArray (m_colAttr); + glEnableVertexAttribArray (positions); - GLfloat vertices[] = { - 0.0f, 0.707f, -1.0, - -0.5f, -0.5f, -1.0, - 0.5f, -0.5f, -1.0, - -0.6 + 0.0f, 0.0707f, -1.0, - -0.6 + -0.05f, -0.05f, -1.0, - -0.6 + 0.05f, -0.05f, -1.0, - 0.0f, 0.707f, -1.5, - -0.5f, -0.5f, -1.5, - 0.5f, -0.5f, -1.5, - 0.0f, 0.707f, -2.0, - -0.5f, -0.5f, -2.0, - 0.5f, -0.5f, -2.0 - }; + for (std::list::const_iterator l = m_layers.begin (); l != m_layers.end (); ++l) { - GLfloat colors[] = { - 1.0f, 0.0f, 0.0f, - 1.0f, 0.0f, 0.0f, - 1.0f, 0.0f, 0.0f, - 1.0f, 0.0f, 0.0f, - 1.0f, 0.0f, 0.0f, - 1.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, - 0.0f, 1.0f, 0.0f, - 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 1.0f, - 0.0f, 0.0f, 1.0f, - 0.0f, 0.0f, 1.0f - }; + m_shapes_program->setUniformValue ("color", l->color [0], l->color [1], l->color [2], l->color [3]); - glVertexAttribPointer (m_posAttr, 3, GL_FLOAT, GL_FALSE, 0, vertices); - glVertexAttribPointer (m_colAttr, 3, GL_FLOAT, GL_FALSE, 0, colors); + for (chunks_type::iterator c = l->vertex_chunk->begin (); c != l->vertex_chunk->end (); ++c) { + glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, c->front ()); + glDrawArrays (GL_TRIANGLES, 0, c->size () / 3); + } - glDrawArrays (GL_TRIANGLES, 0, 12); + } + + glDisableVertexAttribArray (positions); + + m_shapes_program->release (); + + m_gridplane_program->bind (); + + glEnableVertexAttribArray (positions); + + m_gridplane_program->setUniformValue ("matrix", matrix); + + // @@@ GLfloat plane_vertices[] = { -1.05, 0.0, -2.05, -1.05, 0.0, 0.05, 1.05, 0.0, 0.05, -1.05, 0.0, -2.05, 1.05, 0.0, 0.05, 1.05, 0.0, -2.05 }; - GLfloat plane_colors[] = { - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f - }; + m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, 0.2f); - glVertexAttribPointer (m_posAttr, 3, GL_FLOAT, GL_FALSE, 0, plane_vertices); - glVertexAttribPointer (m_colAttr, 4, GL_FLOAT, GL_FALSE, 0, plane_colors); + glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, plane_vertices); glDrawArrays (GL_TRIANGLES, 0, 6); @@ -305,55 +585,16 @@ D25ViewWidget::paintGL () 1.0, 0.0, 0.0, -1.0, 0.0, 0.0 }; - GLfloat gridline_colors[] = { - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f, - 1.0f, 1.0f, 1.0f, 0.2f - }; + m_shapes_program->setUniformValue ("vertexColor", 1.0, 1.0, 1.0, 0.2f); - glVertexAttribPointer (m_posAttr, 3, GL_FLOAT, GL_FALSE, 0, gridline_vertices); - glVertexAttribPointer (m_colAttr, 4, GL_FLOAT, GL_FALSE, 0, gridline_colors); + glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, gridline_vertices); glLineWidth (2.0); glDrawArrays (GL_LINES, 0, 36); - glDisableVertexAttribArray (m_posAttr); - glDisableVertexAttribArray (m_colAttr); + glDisableVertexAttribArray (positions); - m_program->release (); + m_shapes_program->release (); } void diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h index aec8bbb33..63fb4fad5 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h @@ -32,9 +32,21 @@ #include #include +#include "dbPolygon.h" + +#include "layD25MemChunks.h" + +namespace db +{ + class Layout; + class Cell; +} + namespace lay { +class LayoutView; + class D25ViewWidget : public QOpenGLWidget, private QOpenGLFunctions @@ -50,11 +62,12 @@ public: void mouseReleaseEvent (QMouseEvent *event); void mouseMoveEvent (QMouseEvent *event); + void attach_view (lay::LayoutView *view); + private: - QOpenGLShaderProgram *m_program; - GLuint m_posAttr; - GLuint m_colAttr; - GLuint m_matrixUniform; + typedef lay::mem_chunks chunks_type; + + QOpenGLShaderProgram *m_shapes_program, *m_gridplane_program; QMatrix4x4 m_cam_trans; bool m_dragging, m_rotating; QVector3D m_cam_position; @@ -62,12 +75,26 @@ private: QPoint m_start_pos; QVector3D m_start_cam_position; double m_start_cam_azimuth, m_start_cam_elevation; + lay::LayoutView *mp_view; + + std::list m_vertex_chunks; + + struct LayerInfo { + const chunks_type *vertex_chunk; + GLfloat color [4]; + }; + + std::list m_layers; void initializeGL (); void paintGL (); void resizeGL (int w, int h); void update_cam_trans (); + void prepare_view (); + void render_layout (D25ViewWidget::chunks_type &chunks, const db::Layout &layout, const db::Cell &cell, unsigned int layer, double zstart, double zstop); + void render_polygon (D25ViewWidget::chunks_type &chunks, const db::Polygon &poly, double dbu, double zstart, double zstop); + void render_wall (D25ViewWidget::chunks_type &chunks, const db::Edge &poly, double dbu, double zstart, double zstop); }; } diff --git a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro index 062d2eed4..1de5fe5b5 100644 --- a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro +++ b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro @@ -11,11 +11,13 @@ LIBS += -L$$DESTDIR/.. -lklayout_rdb -lklayout_ant HEADERS = \ layD25View.h \ layD25ViewWidget.h \ + layD25MemChunks.h SOURCES = \ layD25View.cc \ layD25ViewWidget.cc \ - layD25Plugin.cc + layD25Plugin.cc \ + layD25MemChunks.cc FORMS = \ D25View.ui \ diff --git a/src/plugins/tools/view_25d/unit_tests/layD25MemChunksTests.cc b/src/plugins/tools/view_25d/unit_tests/layD25MemChunksTests.cc index ab34661b6..5482762a0 100644 --- a/src/plugins/tools/view_25d/unit_tests/layD25MemChunksTests.cc +++ b/src/plugins/tools/view_25d/unit_tests/layD25MemChunksTests.cc @@ -21,466 +21,70 @@ */ -#include "dbDXFReader.h" -#include "dbTestSupport.h" +#include "layD25MemChunks.h" #include "tlUnitTest.h" -#include - -static db::LayerMap string2lm (const char *map) +TEST(1_Basic) { - db::LayerMap lm; - unsigned int ln = 0; - tl::Extractor ex (map); - while (! ex.at_end ()) { - std::string n; - int l; - ex.read_word_or_quoted (n); - ex.test (":"); - ex.read (l); - ex.test (","); - lm.map (n, ln++, db::LayerProperties (l, 0)); - } - return lm; + lay::mem_chunks ch; + EXPECT_EQ (ch.begin () == ch.end (), true); + + ch.add (1); + EXPECT_EQ (ch.begin () == ch.end (), false); + EXPECT_EQ (ch.begin ()->size (), size_t (1)); + EXPECT_EQ (ch.begin ()->front () [0], 1); + + ch.add (17); + EXPECT_EQ (ch.begin () == ch.end (), false); + EXPECT_EQ (ch.begin ()->size (), size_t (2)); + EXPECT_EQ (ch.begin ()->front () [0], 1); + EXPECT_EQ (ch.begin ()->front () [1], 17); + + lay::mem_chunks::iterator c = ch.begin (); + EXPECT_EQ (c == ch.end (), false); + ++c; + EXPECT_EQ (c == ch.end (), true); + + ch.add (42); + c = ch.begin (); + EXPECT_EQ (c == ch.end (), false); + EXPECT_EQ (c->size (), size_t (2)); + EXPECT_EQ (c->front () [0], 1); + EXPECT_EQ (c->front () [1], 17); + ++c; + EXPECT_EQ (c == ch.end (), false); + EXPECT_EQ (c->size (), size_t (1)); + EXPECT_EQ (c->front () [0], 42); + ++c; + EXPECT_EQ (c == ch.end (), true); + + ch.clear (); + EXPECT_EQ (ch.begin () == ch.end (), true); } -static void do_run_test (tl::TestBase *_this, const std::string &fn, const std::string &fn_au, const db::DXFReaderOptions &opt, bool as_oas) +TEST(2_Copy) { - db::LoadLayoutOptions options; - options.set_options (new db::DXFReaderOptions (opt)); + lay::mem_chunks ch1; + ch1.add (1); + ch1.add (17); + ch1.add (42); - db::Layout layout; + lay::mem_chunks ch (ch1); - { - tl::InputStream stream (fn); - db::Reader reader (stream); - reader.read (layout, options); - } + lay::mem_chunks::iterator c = ch.begin (); - db::compare_layouts (_this, layout, fn_au, as_oas ? db::WriteOAS : db::WriteGDS2, 1); -} - -static void run_test (tl::TestBase *_this, const char *file, const char *file_au, const db::DXFReaderOptions &opt = db::DXFReaderOptions (), bool as_oas = false) -{ - std::string fn = tl::testsrc_private () + "/testdata/dxf/" + file; - std::string fn_au = tl::testsrc_private () + std::string ("/testdata/dxf/") + file_au; - - do_run_test (_this, fn, fn_au, opt, as_oas); -} - -static void run_test_public (tl::TestBase *_this, const char *file, const char *file_au, const db::DXFReaderOptions &opt = db::DXFReaderOptions (), bool as_oas = false) -{ - std::string fn = tl::testsrc () + "/testdata/dxf/" + file; - std::string fn_au = tl::testsrc () + std::string ("/testdata/dxf/") + file_au; - - do_run_test (_this, fn, fn_au, opt, as_oas); -} - -TEST(KeepLN1) -{ - db::DXFReaderOptions opt; - run_test_public (_this, "keep_ln.dxf.gz", "keep_ln1_au.oas.gz", opt, true /*because of layer names*/); -} - -TEST(KeepLN2) -{ - db::DXFReaderOptions opt; - opt.keep_layer_names = true; - run_test_public (_this, "keep_ln.dxf.gz", "keep_ln2_au.oas.gz", opt, true /*because of layer names*/); -} - -TEST(1a) -{ - run_test (_this, "t1.dxf.gz", "t1a_au.gds.gz"); -} - -TEST(1b) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.01; - opt.unit = 5.0; - run_test (_this, "t1.dxf.gz", "t1b_au.gds.gz", opt); -} - -TEST(2) -{ - run_test (_this, "t2.dxf.gz", "t2_au.gds.gz"); -} - -TEST(3) -{ - run_test (_this, "t3.dxf.gz", "t3_au.gds.gz"); -} - -TEST(4) -{ - db::DXFReaderOptions opt; - opt.layer_map = string2lm ("Metal:1,Metal2:5"); - opt.create_other_layers = true; - run_test (_this, "t4.dxf.gz", "t4_au.gds.gz", opt); -} - -TEST(5) -{ - run_test (_this, "t5.dxf.gz", "t5_au.gds.gz"); -} - -TEST(6) -{ - run_test (_this, "t6.dxf.gz", "t6_au.gds.gz"); -} - -TEST(7) -{ - run_test (_this, "t7.dxf.gz", "t7_au.gds.gz"); -} - -TEST(8) -{ - db::DXFReaderOptions opt; - opt.layer_map = string2lm ("Metal:4,Kommentare:3,Bemassung:2"); - opt.create_other_layers = true; - run_test (_this, "t8.dxf.gz", "t8_au.gds.gz", opt); -} - -TEST(9) -{ - db::DXFReaderOptions opt; - opt.layer_map = string2lm ("Bemassung:2,Metal:5,Kommentare:4"); - opt.create_other_layers = true; - run_test (_this, "t9.dxf.gz", "t9_au.gds.gz", opt); -} - -TEST(10) -{ - db::DXFReaderOptions opt; - opt.layer_map = string2lm ("METAL:1,KOMMENTARE:4"); - opt.create_other_layers = true; - run_test (_this, "t10.dxf.gz", "t10_au.gds.gz", opt); -} - -TEST(11) -{ - run_test (_this, "t11.dxf.gz", "t11_au.gds.gz"); -} - -TEST(12) -{ - run_test (_this, "t12.dxf.gz", "t12_au.gds.gz"); -} - -TEST(14) -{ - db::DXFReaderOptions opt; - opt.layer_map = string2lm ("'A11-STRUKTUR__E_TYP_':10,A21_NITRID:11,'B11-KONTAKT':9,'B11-STRUKTUR':3,HELLFELD:7,MASKE:5,NORM_MIN_MAX_WAFER:6,RASTER:2,_BEGRENZUNG_A11_A21_A31_B1:8"); - opt.create_other_layers = true; - run_test (_this, "t14.dxf.gz", "t14_au.gds.gz", opt); -} - -TEST(15) -{ - db::DXFReaderOptions opt; - opt.layer_map = string2lm ("TEXT:4,IGBT:5,Wire:7,Ceramic:11,LAYER_1:14,Diode:18,'DBC TOP Plate':19,'Terminal Position':20"); - opt.create_other_layers = true; - run_test (_this, "t15.dxf.gz", "t15_au.gds.gz", opt); -} - -TEST(16) -{ - db::DXFReaderOptions opt; - opt.layer_map = string2lm ("TEXT:4,IGBT:5,Wire:7,Ceramic:11,LAYER_1:14,Diode:18,'DBC TOP Plate':19,'Terminal Position':20"); - opt.create_other_layers = true; - run_test (_this, "t16.dxf.gz", "t16_au.gds.gz", opt); -} - -TEST(17) -{ - db::DXFReaderOptions opt; - opt.layer_map = string2lm ("TEXT:4,IGBT:5,Wire:7,Ceramic:11,LAYER_1:14,Diode:18,'DBC TOP Plate':19,'Terminal Position':20"); - opt.create_other_layers = true; - run_test (_this, "t17.dxf.gz", "t17_au.gds.gz", opt); -} - -TEST(18) -{ - run_test (_this, "t18.dxf.gz", "t18_au.gds.gz"); -} - -TEST(19) -{ - run_test (_this, "t19.dxf.gz", "t19_au.gds.gz"); -} - -TEST(20) -{ - run_test (_this, "t20.dxf.gz", "t20_au.gds.gz"); -} - -TEST(21) -{ - run_test (_this, "t21.dxf.gz", "t21_au.gds.gz"); -} - -TEST(22) -{ - run_test (_this, "t22.dxf.gz", "t22_au.gds.gz"); -} - -TEST(23a) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1; - opt.polyline_mode = 0; - opt.circle_points = 10; - run_test (_this, "t23.dxf.gz", "t23a_au.gds.gz", opt); -} - -TEST(23b) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1; - opt.polyline_mode = 1; - opt.circle_points = 10; - run_test (_this, "t23.dxf.gz", "t23b_au.gds.gz", opt); -} - -TEST(23c) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1; - opt.polyline_mode = 2; - opt.circle_points = 10; - run_test (_this, "t23.dxf.gz", "t23c_au.gds.gz", opt); -} - -TEST(23d) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1; - opt.polyline_mode = 3; - opt.circle_points = 10; - run_test (_this, "t23.dxf.gz", "t23d_au.gds.gz", opt); -} - -TEST(23e) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1; - opt.polyline_mode = 4; - opt.circle_points = 10; - run_test (_this, "t23.dxf.gz", "t23e_au.gds.gz", opt); -} - -TEST(26a) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1; - opt.polyline_mode = 0; - opt.circle_points = 100; - run_test (_this, "t26.dxf.gz", "t26a_au.gds.gz", opt); -} - -TEST(26b) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1; - opt.polyline_mode = 1; - opt.circle_points = 100; - run_test (_this, "t26.dxf.gz", "t26b_au.gds.gz", opt); -} - -TEST(26c) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1; - opt.polyline_mode = 2; - opt.circle_points = 100; - run_test (_this, "t26.dxf.gz", "t26c_au.gds.gz", opt); -} - -TEST(26d) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1; - opt.polyline_mode = 3; - opt.circle_points = 100; - run_test (_this, "t26.dxf.gz", "t26d_au.gds.gz", opt); -} - -TEST(26e) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1; - opt.polyline_mode = 4; - opt.circle_points = 100; - run_test (_this, "t26.dxf.gz", "t26e_au.gds.gz", opt); -} - -TEST(27a) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1; - opt.polyline_mode = 0; - opt.circle_points = 10; - run_test (_this, "t27.dxf.gz", "t27a_au.gds.gz", opt); -} - -TEST(27b) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1; - opt.polyline_mode = 1; - opt.circle_points = 10; - run_test (_this, "t27.dxf.gz", "t27b_au.gds.gz", opt); -} - -TEST(27c) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1; - opt.polyline_mode = 2; - opt.circle_points = 10; - run_test (_this, "t27.dxf.gz", "t27c_au.gds.gz", opt); -} - -TEST(27d) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1; - opt.polyline_mode = 3; - opt.circle_points = 10; - run_test (_this, "t27.dxf.gz", "t27d_au.gds.gz", opt); -} - -TEST(27e) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1; - opt.polyline_mode = 4; - opt.circle_points = 10; - run_test (_this, "t27.dxf.gz", "t27e_au.gds.gz", opt); -} - -TEST(28) -{ - run_test (_this, "t28.dxf.gz", "t28_au.gds.gz"); -} - -TEST(29) -{ - run_test (_this, "t29.dxf.gz", "t29_au.gds.gz"); -} - -TEST(29a) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1; - opt.polyline_mode = 4; - opt.circle_points = 1000; - opt.circle_accuracy = 1; - run_test (_this, "t29.dxf.gz", "t29a_au.gds.gz", opt); -} - -TEST(29b) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1; - opt.polyline_mode = 4; - opt.circle_points = 1000; - opt.circle_accuracy = 0.1; - run_test (_this, "t29.dxf.gz", "t29b_au.gds.gz", opt); -} - -TEST(29c) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1; - opt.polyline_mode = 4; - opt.circle_points = 1000; - opt.circle_accuracy = 0.01; - run_test (_this, "t29.dxf.gz", "t29c_au.gds.gz", opt); -} - -TEST(29d) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1; - opt.polyline_mode = 4; - opt.circle_points = 1000; - opt.circle_accuracy = 0.001; - run_test (_this, "t29.dxf.gz", "t29d_au.gds.gz", opt); -} - -TEST(30) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1000; - opt.polyline_mode = 4; - opt.circle_points = 1000; - opt.circle_accuracy = 0.001; - run_test (_this, "t30.dxf.gz", "t30d_au.gds.gz", opt); -} - -// accuracy -TEST(31) -{ - db::DXFReaderOptions opt; - opt.dbu = 0.001; - opt.unit = 1000; - - opt.contour_accuracy = 0; - run_test (_this, "t31.dxf.gz", "t31a_au.gds.gz", opt); - - opt.contour_accuracy = 0.005; - run_test (_this, "t31.dxf.gz", "t31b_au.gds.gz", opt); - - opt.contour_accuracy = 0.01; - run_test (_this, "t31.dxf.gz", "t31c_au.gds.gz", opt); - - opt.contour_accuracy = 0.02; - run_test (_this, "t31.dxf.gz", "t31d_au.gds.gz", opt); -} - -// issue #198 -TEST(32) -{ - db::DXFReaderOptions opt; - opt.layer_map = string2lm ("L11D0:1,L12D0:2"); - opt.create_other_layers = false; - opt.polyline_mode = 3; - - opt.contour_accuracy = 0.0; - run_test_public (_this, "round_path.dxf.gz", "t32a_au.gds.gz", opt); - - opt.contour_accuracy = 0.1; - run_test_public (_this, "round_path.dxf.gz", "t32b_au.gds.gz", opt); - - opt.contour_accuracy = 1.0; - run_test_public (_this, "round_path.dxf.gz", "t32c_au.gds.gz", opt); - - opt.polyline_mode = 4; - run_test_public (_this, "round_path.dxf.gz", "t32d_au.gds.gz", opt); - - opt.polyline_mode = 2; - run_test_public (_this, "round_path.dxf.gz", "t32e_au.gds.gz", opt); + EXPECT_EQ (c == ch.end (), false); + EXPECT_EQ (c->size (), size_t (2)); + EXPECT_EQ (c->front () [0], 1); + EXPECT_EQ (c->front () [1], 17); + ++c; + EXPECT_EQ (c == ch.end (), false); + EXPECT_EQ (c->size (), size_t (1)); + EXPECT_EQ (c->front () [0], 42); + ++c; + EXPECT_EQ (c == ch.end (), true); + + ch1.clear (); + ch = ch1; + EXPECT_EQ (ch.begin () == ch.end (), true); } diff --git a/src/plugins/tools/view_25d/view_25d.pro b/src/plugins/tools/view_25d/view_25d.pro index f1dd4434b..1f10c85ca 100644 --- a/src/plugins/tools/view_25d/view_25d.pro +++ b/src/plugins/tools/view_25d/view_25d.pro @@ -2,5 +2,5 @@ TEMPLATE = subdirs !equals(HAVE_QT, "0") { - SUBDIRS = lay_plugin + SUBDIRS = lay_plugin unit_tests } From 56d406aaad231a773bb670147fe6dadb0351908d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Apr 2020 12:02:30 +0200 Subject: [PATCH 008/113] WIP --- .../tools/view_25d/lay_plugin/layD25ViewWidget.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index b204800c0..e27129f2f 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -94,6 +94,7 @@ D25ViewWidget::initializeGL () "#undef mediump\n" "\n" "uniform vec4 color;\n" + "uniform vec4 ambient;\n" "uniform vec3 illum;\n" "out lowp vec4 vertexColor;\n" "uniform mat4 matrix;\n" @@ -104,8 +105,9 @@ D25ViewWidget::initializeGL () " vec4 p0 = gl_in[0].gl_Position;\n" " vec4 p1 = gl_in[1].gl_Position;\n" " vec4 p2 = gl_in[2].gl_Position;\n" - " vec3 n = cross(p1.xyz - p0.xyz, p2.xyz - p0.xyz);\n" - " vertexColor.rgb = color.rgb * (max(0.0, dot(normalize(n), illum)) * 0.5 + 0.5);\n" + " vec3 n = cross(p2.xyz - p0.xyz, p1.xyz - p0.xyz);\n" + " float dp = dot(normalize(n), illum);\n" + " vertexColor.rgb = color.rgb * (dp * 0.5 + 0.5) - (min(0.0, dp) * 0.5 * ambient.rgb);\n" " vertexColor.a = 1.0;\n" " gl_Position = matrix * p0;\n" " EmitVertex();\n" @@ -526,7 +528,8 @@ D25ViewWidget::paintGL () matrix *= m_cam_trans; m_shapes_program->setUniformValue ("matrix", matrix); - m_shapes_program->setUniformValue ("illum", QVector3D (-3.0, -4.0, 2.0).normalized ()); + m_shapes_program->setUniformValue ("illum", QVector3D (-3.0, -4.0, -2.0).normalized ()); + m_shapes_program->setUniformValue ("ambient", QVector4D (0.5, 0.5, 0.5, 0.5)); glEnableVertexAttribArray (positions); From 7077bac647dfa49f15f80a9a3bddec9dd9ba3d30 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Apr 2020 22:46:35 +0200 Subject: [PATCH 009/113] WIP. --- .../view_25d/lay_plugin/layD25ViewUtils.cc | 164 +++++++++++++++++ .../view_25d/lay_plugin/layD25ViewUtils.h | 80 +++++++++ .../view_25d/lay_plugin/layD25ViewWidget.cc | 124 ++++++++++--- .../view_25d/lay_plugin/layD25ViewWidget.h | 7 + .../tools/view_25d/lay_plugin/lay_plugin.pro | 6 +- .../unit_tests/layD25ViewUtilsTests.cc | 168 ++++++++++++++++++ .../tools/view_25d/unit_tests/unit_tests.pro | 1 + 7 files changed, 527 insertions(+), 23 deletions(-) create mode 100644 src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc create mode 100644 src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.h create mode 100644 src/plugins/tools/view_25d/unit_tests/layD25ViewUtilsTests.cc diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc new file mode 100644 index 000000000..b194e55fa --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc @@ -0,0 +1,164 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "layD25ViewUtils.h" + +#include +#include + +#include + +namespace lay +{ + +const double epsilon = 1e-10; + +std::pair +cutpoint_line_with_plane (const QVector3D &line, const QVector3D &dir, const QVector3D &plane, const QVector3D &plane_normal) +{ + double dn = QVector3D::dotProduct (dir, plane_normal); + if (fabs (dn) < epsilon) { + return std::make_pair (false, QVector3D ()); + } else { + return std::make_pair (true, line + dir * QVector3D::dotProduct (plane - line, plane_normal) / dn); + } +} + +std::pair +cutpoint_line_with_face (const QVector3D &line, const QVector3D &dir, const QVector3D &plane, const QVector3D &u, const QVector3D &v) +{ + QVector3D n = QVector3D::crossProduct (u, v); + std::pair r = cutpoint_line_with_plane (line, dir, plane, n); + if (! r.first) { + return r; + } + + double pu = QVector3D::dotProduct (r.second - plane, u); + double pv = QVector3D::dotProduct (r.second - plane, v); + + // test whether the cut point is inside the face + if (pu < -epsilon || pu > u.lengthSquared () + epsilon || pv < -epsilon || pv > v.lengthSquared () + epsilon) { + return std::make_pair (false, QVector3D ()); + } else { + return r; + } +} + +static std::pair plane_or_face (const QVector3D &line, const QVector3D &line_dir, const QVector3D &corner, const QVector3D &u, const QVector3D &v, bool face) +{ + if (face) { + return cutpoint_line_with_face (line, line_dir, corner, u, v); + } else { + return cutpoint_line_with_plane (line, line_dir, corner, QVector3D::crossProduct (u, v)); + } +} + +std::pair +hit_point_with_cuboid (const QVector3D &line, const QVector3D &line_dir, const QVector3D &corner, const QVector3D &dim) +{ + std::vector > cutpoints; + cutpoints.reserve (6); // 6 faces + + for (int pass = 0; pass < 2; ++pass) { + + bool face = (pass == 0); + + if (face) { + bool in_x = (line.x () > corner.x () - epsilon) && (line.x () < corner.x () + dim.x () + epsilon); + bool in_y = (line.y () > corner.y () - epsilon) && (line.y () < corner.y () + dim.y () + epsilon); + bool in_z = (line.z () > corner.z () - epsilon) && (line.z () < corner.z () + dim.z () + epsilon); + if (in_x && in_y && in_z) { + // inside cuboid + return std::make_pair (true, line); + } + } + + cutpoints.clear (); + + // front + cutpoints.push_back (plane_or_face (line, line_dir, corner, QVector3D (dim.x (), 0, 0), QVector3D (0, dim.y (), 0), face)); + // back + cutpoints.push_back (plane_or_face (line, line_dir, corner + QVector3D (0, 0, dim.z ()), QVector3D (dim.x (), 0, 0), QVector3D (0, dim.y (), 0), face)); + + if (face) { + // bottom + cutpoints.push_back (plane_or_face (line, line_dir, corner, QVector3D (dim.x (), 0, 0), QVector3D (0, 0, dim.z ()), face)); + // top + cutpoints.push_back (plane_or_face (line, line_dir, corner + QVector3D (0, dim.y (), 0), QVector3D (dim.x (), 0, 0), QVector3D (0, 0, dim.z ()), face)); + // left + cutpoints.push_back (plane_or_face (line, line_dir, corner, QVector3D (0, 0, dim.z ()), QVector3D (0, dim.y (), 0), face)); + // right + cutpoints.push_back (plane_or_face (line, line_dir, corner + QVector3D (dim.x (), 0, 0), QVector3D (0, 0, dim.z ()), QVector3D (0, dim.y (), 0), face)); + } + + double min_dist = 0.0; + int min_dist_index = -1; + QVector3D ld_norm = line_dir.normalized (); + + for (std::vector >::const_iterator i = cutpoints.begin (); i != cutpoints.end (); ++i) { + if (i->first) { + double dist = QVector3D::dotProduct (i->second - line, ld_norm); + if (dist < -epsilon) { + // ignore all cutpoints behind us + } else if (min_dist_index < 0) { + min_dist = dist; + min_dist_index = int (i - cutpoints.begin ()); + } else if (dist < min_dist) { + min_dist = dist; + min_dist_index = int (i - cutpoints.begin ()); + } + } + } + + if (min_dist_index >= 0) { + return cutpoints [min_dist_index]; + } + + } + + return std::make_pair (false, QVector3D ()); +} + +std::pair +camera_normal (const QMatrix4x4 &camera_trans, double x, double y) +{ + QVector3D p = camera_trans.inverted ().map (QVector3D (x, y, 1.0)); + + QVector4D pv = camera_trans.row (3); + + QMatrix4x4 m (camera_trans); + + float values[] = { + float (x * pv.x ()), float (x * pv.y ()), float (x * pv.z ()), 0.0f, + float (y * pv.x ()), float (y * pv.y ()), float (y * pv.z ()), 0.0f, + float (pv.x ()), float (pv.y ()), float (pv.z ()), 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f + }; + m -= QMatrix4x4 (values); + + QMatrix3x3 nm = m.normalMatrix (); + + QVector3D u (nm (2, 0), nm (2, 1), nm (2, 2)); + return (std::make_pair (p, u.normalized ())); +} + +} diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.h new file mode 100644 index 000000000..c875c771d --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.h @@ -0,0 +1,80 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_layD25ViewUtils +#define HDR_layD25ViewUtils + +#include "layPluginCommon.h" + +#include +#include +#include + +namespace lay +{ + +/** + * @brief Computes the cutpoint between a line and a plane + * + * The line is given by a point and a direction (line, line_dir). + * The plane is given by a point and a normal vector (plane, plane_normal) + * The "first" component of the returned pair is false if not hit is present. + */ + +LAY_PLUGIN_PUBLIC std::pair +cutpoint_line_with_plane (const QVector3D &line, const QVector3D &line_dir, const QVector3D &plane, const QVector3D &plane_normal); + +/** + * @brief Computes the cutpoint between a line and a face + * + * The line is given by a point and a direction (line, line_dir). + * The face is given by a plane point and two vectors spanning the face. + * The "first" component of the returned pair is false if not hit is present. + */ + +LAY_PLUGIN_PUBLIC std::pair +cutpoint_line_with_face (const QVector3D &line, const QVector3D &dir, const QVector3D &plane, const QVector3D &u, const QVector3D &v); + +/** + * @brief Determines a good hit point of a view line and a cuboid + * + * "corner, dim" are the coordinates for the cuboid (corner is the bottom, left, foremost corner, dim + * is (width, height, depth) + * "line, line_dir" is the view line where "line_dir" is pointing from the camera to the object. + * The returned point is a suitable hit point. + * The "first" component of the returned pair is false if no hit is present. + */ +LAY_PLUGIN_PUBLIC std::pair +hit_point_with_cuboid (const QVector3D &line, const QVector3D &line_dir, const QVector3D &corner, const QVector3D &dim); + +/** + * @brief For a given pixel coordinate and camera transformation matrix compute a line containing all points corresponding to this pixel + * + * The returned pair contains a point and a direction vector describing the line. + */ +LAY_PLUGIN_PUBLIC std::pair +camera_normal (const QMatrix4x4 &camera_trans, double x, double y); + +} + +#endif + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index e27129f2f..dadf2d297 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -22,6 +22,7 @@ #include "layD25ViewWidget.h" +#include "layD25ViewUtils.h" #include "layLayoutView.h" #include "dbRecursiveShapeIterator.h" @@ -50,7 +51,9 @@ D25ViewWidget::D25ViewWidget (QWidget *parent) format.setSamples (4); // more -> widget extends beyond boundary! setFormat (format); - m_cam_position = QVector3D (0.0, 0.0, 3.0); // @@@ + m_cam_position = QVector3D (0.0, 0.0, 4.0); // @@@ + m_scale_factor = 1.0; + m_focus_dist = 0.0; } D25ViewWidget::~D25ViewWidget () @@ -194,19 +197,36 @@ static QVector3D cam_direction (double azimuth, double elevation) void D25ViewWidget::wheelEvent (QWheelEvent *event) { - double cal = 0.6; + double px = (event->pos ().x () - width () / 2) * 2.0 / width (); + double py = -(event->pos ().y () - height () / 2) * 2.0 / height (); - int dx = event->pos ().x () - width () / 2; - int dy = -(event->pos ().y () - height () / 2); + double f = exp (event->angleDelta ().y () * (1.0 / (90 * 8))); - double da = atan (dx * cal * 2.0 / height ()) * 180 / M_PI; - double de = atan (dy * cal * 2.0 / height ()) * 180 / M_PI; + // compute vector of line of sight + std::pair ray = camera_normal (m_cam_trans, px, py); - m_cam_position += (event->angleDelta ().y () * (1.0 / (45 * 8))) * cam_direction (m_cam_azimuth + da, m_cam_elevation + de); + // by definition the ray goes through the camera position + std::pair hp = hit_point_with_scene (m_cam_position, ray.second); + + QVector3D pm = m_cam_trans.map(hp.second); + printf("@@@ mouse=%g,%g back=%g,%g\n", px, py, pm.x(), pm.y()); + printf("@@@ before: hp=%g,%g,%g d=%g,%g,%g s=%g\n", hp.second.x(), hp.second.y(), hp.second.z(), m_displacement.x(), m_displacement.y(), m_displacement.z(), m_scale_factor); fflush(stdout); + + m_displacement = hp.second * (1.0 - f) + m_displacement * f; + m_scale_factor *= f; update_cam_trans (); } +std::pair +D25ViewWidget::hit_point_with_scene (const QVector3D &line, const QVector3D &line_dir) +{ + QVector3D corner = QVector3D (m_bbox.left (), m_zmin, -(m_bbox.bottom () + m_bbox.height ())) * m_scale_factor + m_displacement; + QVector3D dim = QVector3D (m_bbox.width (), m_zmax - m_zmin, m_bbox.height ()) * m_scale_factor; + + return lay::hit_point_with_cuboid (line, line_dir, corner, dim); +} + void D25ViewWidget::mousePressEvent (QMouseEvent *event) { @@ -221,6 +241,19 @@ D25ViewWidget::mousePressEvent (QMouseEvent *event) m_start_cam_position = m_cam_position; m_start_cam_azimuth = m_cam_azimuth; m_start_cam_elevation = m_cam_elevation; + m_start_displacement = m_displacement; + + m_focus_dist = 2.0; + + if (m_dragging || m_rotating) { + + QVector3D cd = cam_direction (m_start_cam_azimuth, m_start_cam_elevation); + std::pair hp = hit_point_with_scene (m_cam_position, cd); + if (hp.first) { + m_focus_dist = std::max (m_focus_dist, double ((m_cam_position - hp.second).length ())); + } + + } } void @@ -232,12 +265,10 @@ D25ViewWidget::mouseReleaseEvent (QMouseEvent * /*event*/) void D25ViewWidget::mouseMoveEvent (QMouseEvent *event) { - double focus_dist = 4.0; // 4 times focal length - if (m_dragging) { // for the chosen perspective transformation: - double cal = 0.6 * focus_dist; + double cal = 0.6 * m_focus_dist; QPoint d = event->pos () - m_start_pos; double f = cal * 2.0 / double (height ()); @@ -249,13 +280,14 @@ D25ViewWidget::mouseMoveEvent (QMouseEvent *event) QVector3D yv (-re * xv.z (), cos (m_start_cam_elevation * M_PI / 180.0), re * xv.x ()); QVector3D drag = xv * dx + yv * dy; - // "-drag" because we're not dragging the camera, we're dragging the scene - m_cam_position = m_start_cam_position - drag; + m_displacement = m_start_displacement + drag; update_cam_trans (); } else if (m_rotating) { + double focus_dist = 4.0; // @@@ + QPoint d = event->pos () - m_start_pos; double ax = atan (d.x () / (0.5 * height ())) * 180 / M_PI; @@ -274,9 +306,12 @@ D25ViewWidget::mouseMoveEvent (QMouseEvent *event) void D25ViewWidget::update_cam_trans () { -printf("@@@ e=%g a=%g x,y,z=%g,%g,%g\n", m_cam_elevation, m_cam_azimuth, m_cam_position.x(), m_cam_position.y(), m_cam_position.z()); fflush(stdout); +printf("@@@ e=%g a=%g x,y,z=%g,%g,%g d=%g,%g,%g s=%g f=%g\n", m_cam_elevation, m_cam_azimuth, m_cam_position.x(), m_cam_position.y(), m_cam_position.z(), m_displacement.x(), m_displacement.y(), m_displacement.z(), m_scale_factor, m_focus_dist); fflush(stdout); QMatrix4x4 t; + // finally add perspective + t.perspective (60.0f, float (width ()) / float (height ()), 0.1f, 100.0f); + // third: elevation t.rotate (-m_cam_elevation, 1.0, 0.0, 0.0); @@ -312,6 +347,10 @@ D25ViewWidget::prepare_view () { double z = 0.0, dz = 0.2; // @@@ + m_bbox = db::DBox (); + bool zset = false; + m_zmin = m_zmax = 0.0; + for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) { if (! lp->has_children () && lp->visible (true) && lp->cellview_index () >= 0 && lp->cellview_index () < int (mp_view->cellviews ())) { @@ -322,9 +361,9 @@ D25ViewWidget::prepare_view () LayerInfo info; // @@@ use alpha? - info.color[0] = (color & 0xff) / 255.0f; + info.color[0] = ((color >> 16) & 0xff) / 255.0f; info.color[1] = ((color >> 8) & 0xff) / 255.0f; - info.color[2] = ((color >> 16) & 0xff) / 255.0f; + info.color[2] = (color & 0xff) / 255.0f; info.color[3] = 1.0; info.vertex_chunk = &m_vertex_chunks.back (); @@ -333,6 +372,17 @@ D25ViewWidget::prepare_view () const lay::CellView &cv = mp_view->cellview ((unsigned int) lp->cellview_index ()); render_layout (m_vertex_chunks.back (), cv->layout (), *cv.cell (), (unsigned int) lp->layer_index (), z, z + dz); + m_bbox += db::DBox (cv.cell ()->bbox ((unsigned int) lp->layer_index ())) * cv->layout ().dbu (); + + if (! zset) { + m_zmin = z; + m_zmax = z + dz; + zset = true; + } else { + m_zmin = std::min (z, m_zmin); + m_zmax = std::max (z + dz, m_zmax); + } + z += dz; // @@@ } @@ -513,6 +563,7 @@ D25ViewWidget::render_layout (D25ViewWidget::chunks_type &chunks, const db::Layo void D25ViewWidget::paintGL () { + printf("@@@ width=%d,height=%d\n", width(),height()); // @@@ const qreal retinaScale = devicePixelRatio (); glViewport (0, 0, width () * retinaScale, height () * retinaScale); @@ -523,12 +574,19 @@ D25ViewWidget::paintGL () m_shapes_program->bind (); - QMatrix4x4 matrix; - matrix.perspective (60.0f, float (width ()) / float (height ()), 0.1f, 100.0f); - matrix *= m_cam_trans; + QMatrix4x4 scene_trans; + + // provide the displacement and scaling + scene_trans.translate (m_displacement); + scene_trans.scale (m_scale_factor); + // this way we can use y as z coordinate when drawing + scene_trans.scale (1.0, 1.0, -1.0); + + m_shapes_program->setUniformValue ("matrix", m_cam_trans * scene_trans); + + // NOTE: z axis of illum points towards the scene because we include the z inversion in the scene transformation matrix + m_shapes_program->setUniformValue ("illum", QVector3D (-3.0, -4.0, 2.0).normalized ()); - m_shapes_program->setUniformValue ("matrix", matrix); - m_shapes_program->setUniformValue ("illum", QVector3D (-3.0, -4.0, -2.0).normalized ()); m_shapes_program->setUniformValue ("ambient", QVector4D (0.5, 0.5, 0.5, 0.5)); glEnableVertexAttribArray (positions); @@ -552,7 +610,8 @@ D25ViewWidget::paintGL () glEnableVertexAttribArray (positions); - m_gridplane_program->setUniformValue ("matrix", matrix); + // @@@ m_gridplane_program->setUniformValue ("matrix", m_cam_trans * m_scene_trans); + m_gridplane_program->setUniformValue ("matrix", QMatrix4x4 ()); // @@@ // @@@ @@ -567,6 +626,7 @@ D25ViewWidget::paintGL () glDrawArrays (GL_TRIANGLES, 0, 6); +#if 0 GLfloat gridline_vertices[] = { -1.0, 0.0, -2.0, -1.0, 0.0, 0.0, -0.75, 0.0, -2.0, -0.75, 0.0, 0.0, @@ -587,6 +647,28 @@ D25ViewWidget::paintGL () 1.0, 0.0, -0.25, -1.0, 0.0, -0.25, 1.0, 0.0, 0.0, -1.0, 0.0, 0.0 }; +#else + GLfloat gridline_vertices[] = { + -1.0, -1.0, 0.0, -1.0, 1.0, 0.0, + -0.75, -1.0, 0.0, -0.75, 1.0, 0.0, + -0.5, -1.0, 0.0, -0.5, 1.0, 0.0, + -0.25, -1.0, 0.0, -0.25, 1.0, 0.0, + 0.0, -1.0, 0.0, 0.0, 1.0, 0.0, + 0.25, -1.0, 0.0, 0.25, 1.0, 0.0, + 0.5, -1.0, 0.0, 0.5, 1.0, 0.0, + 0.75, -1.0, 0.0, 0.75, 1.0, 0.0, + 1.0, -1.0, 0.0, 1.0, 1.0, 0.0, + 1.0, -1.0, 0.0, -1.0, -1.0, 0.0, + 1.0, -0.75, 0.0, -1.0, -0.75, 0.0, + 1.0, -0.5, 0.0, -1.0, -0.5, 0.0, + 1.0, -0.25, 0.0, -1.0, -0.25, 0.0, + 1.0, 0.0, 0.0, -1.0, 0.0, 0.0, + 1.0, 0.25, 0.0, -1.0, 0.25, 0.0, + 1.0, 0.5, 0.0, -1.0, 0.5, 0.0, + 1.0, 0.75, 0.0, -1.0, 0.75, 0.0, + 1.0, 1.0, 0.0, -1.0, 1.0, 0.0 + }; +#endif m_shapes_program->setUniformValue ("vertexColor", 1.0, 1.0, 1.0, 0.2f); diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h index 63fb4fad5..26a0653ba 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h @@ -71,11 +71,17 @@ private: QMatrix4x4 m_cam_trans; bool m_dragging, m_rotating; QVector3D m_cam_position; + double m_scale_factor; double m_cam_azimuth, m_cam_elevation; + QVector3D m_displacement; + double m_focus_dist; QPoint m_start_pos; QVector3D m_start_cam_position; double m_start_cam_azimuth, m_start_cam_elevation; + QVector3D m_start_displacement; lay::LayoutView *mp_view; + db::DBox m_bbox; + double m_zmin, m_zmax; std::list m_vertex_chunks; @@ -95,6 +101,7 @@ private: void render_layout (D25ViewWidget::chunks_type &chunks, const db::Layout &layout, const db::Cell &cell, unsigned int layer, double zstart, double zstop); void render_polygon (D25ViewWidget::chunks_type &chunks, const db::Polygon &poly, double dbu, double zstart, double zstop); void render_wall (D25ViewWidget::chunks_type &chunks, const db::Edge &poly, double dbu, double zstart, double zstop); + std::pair hit_point_with_scene (const QVector3D &line, const QVector3D &line_dir); }; } diff --git a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro index 1de5fe5b5..6afb49ac2 100644 --- a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro +++ b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro @@ -11,13 +11,15 @@ LIBS += -L$$DESTDIR/.. -lklayout_rdb -lklayout_ant HEADERS = \ layD25View.h \ layD25ViewWidget.h \ - layD25MemChunks.h + layD25MemChunks.h \ + layD25ViewUtils.h SOURCES = \ layD25View.cc \ layD25ViewWidget.cc \ layD25Plugin.cc \ - layD25MemChunks.cc + layD25MemChunks.cc \ + layD25ViewUtils.cc FORMS = \ D25View.ui \ diff --git a/src/plugins/tools/view_25d/unit_tests/layD25ViewUtilsTests.cc b/src/plugins/tools/view_25d/unit_tests/layD25ViewUtilsTests.cc new file mode 100644 index 000000000..e5cf71c65 --- /dev/null +++ b/src/plugins/tools/view_25d/unit_tests/layD25ViewUtilsTests.cc @@ -0,0 +1,168 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "layD25ViewUtils.h" +#include "tlUnitTest.h" + +static std::string v2s (const QVector3D &v) +{ + return tl::to_string (v.x ()) + "," + tl::to_string (v.y ()) + "," + tl::to_string (v.z ()); +} + +static std::string v2s_2d (const QVector3D &v) +{ + return tl::to_string (v.x ()) + "," + tl::to_string (v.y ()); +} + +TEST(1_CutPoint) +{ + std::pair r; + + r = lay::cutpoint_line_with_plane (QVector3D (0, 0, 0), QVector3D (0, 0, 1), QVector3D (0, 0, 0), QVector3D (1, 0, 0)); + EXPECT_EQ (r.first, false); + + r = lay::cutpoint_line_with_plane (QVector3D (1, 2, 3), QVector3D (0, 0, 2), QVector3D (4, 5, 6), QVector3D (0, 0, 1)); + EXPECT_EQ (r.first, true); + EXPECT_EQ (v2s (r.second), "1,2,6"); + + r = lay::cutpoint_line_with_plane (QVector3D (1, 2, 3), QVector3D (0, 0, -1), QVector3D (4, 5, 6), QVector3D (1, 1, 1)); + EXPECT_EQ (r.first, true); + EXPECT_EQ (v2s (r.second), "1,2,12"); +} + +TEST(2_Face) +{ + std::pair r; + + r = lay::cutpoint_line_with_face (QVector3D (0, 0, 0), QVector3D (0, 0, 1), QVector3D (0, 0, 0), QVector3D (0, 1, 0), QVector3D (0, 0, 1)); + EXPECT_EQ (r.first, false); + + r = lay::cutpoint_line_with_face (QVector3D (1, 2, 3), QVector3D (0, 0, 2), QVector3D (4, 5, 6), QVector3D (0, 1, 0), QVector3D (1, 0, 0)); + EXPECT_EQ (r.first, false); + + r = lay::cutpoint_line_with_face (QVector3D (4, 5, 3), QVector3D (0, 0, 3), QVector3D (4, 5, 6), QVector3D (0, 1, 0), QVector3D (1, 0, 0)); + EXPECT_EQ (r.first, true); + EXPECT_EQ (v2s (r.second), "4,5,6"); + + r = lay::cutpoint_line_with_face (QVector3D (4, 7, 3), QVector3D (0, 0, 1), QVector3D (4, 5, 6), QVector3D (0, 1, 0), QVector3D (1, 0, 0)); + EXPECT_EQ (r.first, false); + + r = lay::cutpoint_line_with_face (QVector3D (4, 6, 3), QVector3D (0, 0, 2), QVector3D (4, 5, 6), QVector3D (0, 1, 0), QVector3D (1, 0, 0)); + EXPECT_EQ (r.first, true); + EXPECT_EQ (v2s (r.second), "4,6,6"); + + r = lay::cutpoint_line_with_face (QVector3D (5, 6, 3), QVector3D (0, 0, -1), QVector3D (4, 5, 6), QVector3D (0, 1, 0), QVector3D (1, 0, 0)); + EXPECT_EQ (r.first, true); + EXPECT_EQ (v2s (r.second), "5,6,6"); + + r = lay::cutpoint_line_with_face (QVector3D (6, 6, 3), QVector3D (0, 0, 1), QVector3D (4, 5, 6), QVector3D (0, 1, 0), QVector3D (1, 0, 0)); + EXPECT_EQ (r.first, false); +} + +TEST(3_HitWithCuboid) +{ + std::pair r; + + r = lay::hit_point_with_cuboid (QVector3D (0, 0, 0), QVector3D (0, 0, 1), QVector3D (-1, -1, 3), QVector3D (2, 2, 2)); + EXPECT_EQ (r.first, true); + EXPECT_EQ (v2s (r.second), "0,0,3"); + + r = lay::hit_point_with_cuboid (QVector3D (1, 1, 4), QVector3D (0, 0, 1), QVector3D (-1, -1, 3), QVector3D (2, 2, 2)); + EXPECT_EQ (r.first, true); + EXPECT_EQ (v2s (r.second), "1,1,4"); + + r = lay::hit_point_with_cuboid (QVector3D (1, 1, 6), QVector3D (0, 0, 1), QVector3D (-1, -1, 3), QVector3D (2, 2, 2)); + EXPECT_EQ (r.first, false); + + r = lay::hit_point_with_cuboid (QVector3D (5, -6, 0), QVector3D (0, 0, 1), QVector3D (-1, -1, 3), QVector3D (2, 2, 2)); + EXPECT_EQ (r.first, true); + EXPECT_EQ (v2s (r.second), "5,-6,3"); + + r = lay::hit_point_with_cuboid (QVector3D (5, -6, 4), QVector3D (0, 0, 1), QVector3D (-1, -1, 3), QVector3D (2, 2, 2)); + EXPECT_EQ (r.first, true); + EXPECT_EQ (v2s (r.second), "5,-6,5"); + + r = lay::hit_point_with_cuboid (QVector3D (5, -6, 6), QVector3D (0, 0, 1), QVector3D (-1, -1, 3), QVector3D (2, 2, 2)); + EXPECT_EQ (r.first, false); +} + +TEST(4_CameraNormal) +{ + QMatrix4x4 matrix; + matrix.perspective (60.0f, 1.5, 0.1f, 100.0f); + + std::pair ray; + QVector3D p; + + ray = lay::camera_normal (matrix, 0.0, 0.0); + EXPECT_EQ (v2s (ray.second.normalized ()), "0,0,-1"); + + ray = lay::camera_normal (matrix, 1.0, 0.0); + EXPECT_EQ (v2s (ray.second), "0.654654,0,-0.755929"); + + p = matrix.map (ray.first); + EXPECT_EQ (v2s_2d (p), "1,0"); + + p = matrix.map (ray.first + ray.second); + EXPECT_EQ (v2s_2d (p), "1,0"); + + p = matrix.map (ray.first + ray.second * 1000.0); + EXPECT_EQ (v2s_2d (p), "1,0"); + + ray = lay::camera_normal (matrix, 0.0, -1.0); + EXPECT_EQ (v2s (ray.second), "0,-0.5,-0.866025"); + + p = matrix.map (ray.first); + EXPECT_EQ (v2s_2d (p), "0,-1"); + + p = matrix.map (ray.first + ray.second); + EXPECT_EQ (v2s_2d (p), "0,-1"); + + p = matrix.map (ray.first + ray.second * 1000.0); + EXPECT_EQ (v2s_2d (p), "0,-1"); +} + +TEST(5_CameraNormal) +{ + QMatrix4x4 matrix; + QVector3D p; + + matrix.perspective (60.0f, 1.5, 0.1f, 100.0f); + matrix.rotate (22.0, 1.0, 0.0, 0.0); + matrix.rotate (-15.0, 0.0, 1.0, 0.0); + matrix.translate (QVector3D (0.0, 0.0, 4.0)); + + std::pair ray; + + ray = lay::camera_normal (matrix, 0.0, 1.0); + EXPECT_EQ (v2s (ray.second), "-0.2563,0.139173,-0.956526"); + + p = matrix.map (ray.first); + EXPECT_EQ (v2s_2d (p), "0,1"); + + p = matrix.map (ray.first + ray.second); + EXPECT_EQ (v2s_2d (p), "0,1"); + + p = matrix.map (ray.first + ray.second * 1000.0); + EXPECT_EQ (v2s_2d (p), "0,1"); +} diff --git a/src/plugins/tools/view_25d/unit_tests/unit_tests.pro b/src/plugins/tools/view_25d/unit_tests/unit_tests.pro index 22fd790b2..729ddc42e 100644 --- a/src/plugins/tools/view_25d/unit_tests/unit_tests.pro +++ b/src/plugins/tools/view_25d/unit_tests/unit_tests.pro @@ -7,6 +7,7 @@ include($$PWD/../../../../lib_ut.pri) SOURCES = \ layD25MemChunksTests.cc \ + layD25ViewUtilsTests.cc INCLUDEPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../lay_plugin $$PWD/../../../common DEPENDPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../lay_plugin $$PWD/../../../common From d37608dac1783d9a15cbdc3d6d49173afde0835d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 12 Apr 2020 10:32:23 +0200 Subject: [PATCH 010/113] WIP. --- .../tools/view_25d/lay_plugin/layD25View.cc | 3 + .../view_25d/lay_plugin/layD25ViewUtils.cc | 61 +++++++ .../view_25d/lay_plugin/layD25ViewUtils.h | 12 ++ .../view_25d/lay_plugin/layD25ViewWidget.cc | 154 +++++++++++++----- .../view_25d/lay_plugin/layD25ViewWidget.h | 10 +- .../unit_tests/layD25ViewUtilsTests.cc | 62 +++++++ 6 files changed, 260 insertions(+), 42 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc index 44f905517..fdb5230d9 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -37,6 +37,9 @@ D25View::D25View (QWidget *parent) mp_ui = new Ui::D25View (); mp_ui->setupUi (this); + // @@@ should be an event filter? + mp_ui->d25_view->setFocusPolicy (Qt::StrongFocus); + mp_ui->d25_view->setFocus (); // @@@ } diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc index b194e55fa..e63a75969 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc @@ -161,4 +161,65 @@ camera_normal (const QMatrix4x4 &camera_trans, double x, double y) return (std::make_pair (p, u.normalized ())); } +void +normalize_scene_trans (const QMatrix4x4 &cam_trans, QVector3D &displacement, double &scale, double ztarget) +{ + QMatrix4x4 m; + + // Here is the theory: + // Let: + // cam = ( M t ) M = 3x3 matrix, t = 3x1 translation vector, z = scalar, p = 1x3 perspective + // ( p z ) + // and: + // scene = ( S d ) S = s*U1 (s = scale factor, U1 = 3x3 unit matrix), d = 3x1 displacement vector + // ( 0 1 ) + // then: + // cam * scene = ( M*s M*d+t ) + // ( p*s p*d+z ) (p*d = dot product) + // + // this is image invariant (only x,y results are considered) against changes of s (s->s') if + // + // 1.) (p*d+z)/s = (p*d'+z)/s' + // 2.) (M*d+t)/s = (M*d'+t)/s' for [x] and [y] + // + // Ff we seek a solution with d'[z] == b (b = ztarget), we get these equations (f:=s'/s) + // + // 2.) M[xx] * d'[x] + M[xy] * d'[y] - ((M*d)[x] + t[x]) * f = -t[x] - M[xz] * b + // M[yx] * d'[x] + M[yy] * d'[y] - ((M*d)[y] + t[y]) * f = -t[y] - M[yz] * b + // 1.) p[x] * d'[x] + p[y] * d'[y] - (p*d+z) * f = -z - p[z] * b + // + // we can solve these equations for d'[x], d'[y] and f. + // With p[x]=M[wx], p[y]=M[wy] and z=t[w], the above equation system can be written as + // + // M[ix] * d'[x] + M[iy] * d'[y] - ((M*d)[i] + t[i]) * f = -t[i] - M[iz]*b i = x,y,w + // + // and with M,t->M (4x4 matrix) and d->(d,1) (4d vector) + // + // M[ix] * d'[x] + M[iy] * d'[y] - (M*d)[i] * f = -t[i] - M[iz]*b i = x,y,w + // + + QVector4D d4 (displacement); + d4.setW (1.0); + + for (int i = 0; i < 4; ++i) { + if (i != 2) { + m (i, 0) = cam_trans (i, 0); + m (i, 1) = cam_trans (i, 1); + m (i, 3) = -QVector4D::dotProduct (cam_trans.row (i), d4); + } + } + + bool invertable = false; + m = m.inverted (&invertable); + if (! invertable) { + return; + } + + QVector4D sol = m.map (-cam_trans.column (3) - cam_trans.column (2) * ztarget); + if (sol.w () > 0.01 /*skip weird solutions*/) { + scale *= sol.w (); + displacement = QVector3D (sol.x (), sol.y (), ztarget); + } +} + } diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.h index c875c771d..b4bc140ea 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.h @@ -74,6 +74,18 @@ hit_point_with_cuboid (const QVector3D &line, const QVector3D &line_dir, const Q LAY_PLUGIN_PUBLIC std::pair camera_normal (const QMatrix4x4 &camera_trans, double x, double y); +/** + * @brief Normalizes a scene transformation + * + * Scene transformations consist of a scaling and displacement. Both are + * interchangeable to some extent under the presence of a perspective + * transformation (further away makes the scene smaller). This normalization + * tries to find a displacement which has "ztarget" target value for z. Without normalization + * the scene tends to "move away" with respect to z. + */ +LAY_PLUGIN_PUBLIC void +normalize_scene_trans (const QMatrix4x4 &cam_trans, QVector3D &displacement, double &scale, double ztarget = 0.0); + } #endif diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index dadf2d297..04f965992 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -34,6 +34,7 @@ #include #include +#include #include "math.h" @@ -44,14 +45,13 @@ namespace lay D25ViewWidget::D25ViewWidget (QWidget *parent) : QOpenGLWidget (parent), - m_shapes_program (0), m_dragging (false), m_rotating (false), m_cam_azimuth (0.0), m_cam_elevation (0.0) + m_shapes_program (0), m_dragging (false), m_rotating (false), m_cam_azimuth (0.0), m_cam_elevation (0.0), m_top_view (false) { QSurfaceFormat format; format.setDepthBufferSize (24); format.setSamples (4); // more -> widget extends beyond boundary! setFormat (format); - m_cam_position = QVector3D (0.0, 0.0, 4.0); // @@@ m_scale_factor = 1.0; m_focus_dist = 0.0; } @@ -183,48 +183,87 @@ D25ViewWidget::initializeGL () } } -static QVector3D cam_direction (double azimuth, double elevation) -{ - // positive azimuth: camera looks left - // positive elevation: camera looks up - double y = sin (elevation * M_PI / 180.0); - double r = cos (elevation * M_PI / 180.0); - double x = r * sin (azimuth * M_PI / 180.0); - double z = r * cos (azimuth * M_PI / 180.0); - return QVector3D (x, y, -z); -} - void D25ViewWidget::wheelEvent (QWheelEvent *event) { double px = (event->pos ().x () - width () / 2) * 2.0 / width (); double py = -(event->pos ().y () - height () / 2) * 2.0 / height (); - double f = exp (event->angleDelta ().y () * (1.0 / (90 * 8))); - // compute vector of line of sight std::pair ray = camera_normal (m_cam_trans, px, py); // by definition the ray goes through the camera position - std::pair hp = hit_point_with_scene (m_cam_position, ray.second); + float focal_length = 2.0; + QVector3D hp = hit_point_with_scene (cam_position () + focal_length * ray.second, ray.second); - QVector3D pm = m_cam_trans.map(hp.second); - printf("@@@ mouse=%g,%g back=%g,%g\n", px, py, pm.x(), pm.y()); - printf("@@@ before: hp=%g,%g,%g d=%g,%g,%g s=%g\n", hp.second.x(), hp.second.y(), hp.second.z(), m_displacement.x(), m_displacement.y(), m_displacement.z(), m_scale_factor); fflush(stdout); + if (false /*@@@*/ && (event->modifiers () & Qt::ShiftModifier)) { - m_displacement = hp.second * (1.0 - f) + m_displacement * f; - m_scale_factor *= f; + // "Shift" is closeup + + double f = event->angleDelta ().y () * (1.0 / (90 * 8)); + m_displacement += -f * cam_position ().length () * ray.second; + + } else { + + // No shift is zoom + + double f = exp (event->angleDelta ().y () * (1.0 / (90 * 8))); + + QVector3D initial_displacement = m_displacement; + QVector3D displacement = m_displacement; + + displacement = hp * (1.0 - f) + displacement * f; + m_scale_factor *= f; + + // normalize the scene translation so the scene does not "flee" + + QMatrix4x4 ct; + ct.rotate (-cam_elevation (), 1.0, 0.0, 0.0); + ct.rotate (cam_azimuth (), 0.0, 1.0, 0.0); + ct.translate (-cam_position ()); + + initial_displacement = ct.map (initial_displacement); + displacement = ct.map (displacement); + + lay::normalize_scene_trans (m_cam_trans, displacement, m_scale_factor, initial_displacement.z ()); + + m_displacement = ct.inverted ().map (displacement); + + } update_cam_trans (); } -std::pair +void +D25ViewWidget::keyPressEvent (QKeyEvent *event) +{ + if (event->key () == Qt::Key_Shift) { + m_top_view = true; + update_cam_trans (); + } +} + +void +D25ViewWidget::keyReleaseEvent (QKeyEvent *event) +{ + if (event->key () == Qt::Key_Shift) { + m_top_view = false; + update_cam_trans (); + } +} + +QVector3D D25ViewWidget::hit_point_with_scene (const QVector3D &line, const QVector3D &line_dir) { QVector3D corner = QVector3D (m_bbox.left (), m_zmin, -(m_bbox.bottom () + m_bbox.height ())) * m_scale_factor + m_displacement; QVector3D dim = QVector3D (m_bbox.width (), m_zmax - m_zmin, m_bbox.height ()) * m_scale_factor; - return lay::hit_point_with_cuboid (line, line_dir, corner, dim); + std::pair hp = lay::hit_point_with_cuboid (line, line_dir, corner, dim); + if (! hp.first) { + return line; + } else { + return hp.second; + } } void @@ -238,20 +277,19 @@ D25ViewWidget::mousePressEvent (QMouseEvent *event) } m_start_pos = event->pos (); - m_start_cam_position = m_cam_position; - m_start_cam_azimuth = m_cam_azimuth; - m_start_cam_elevation = m_cam_elevation; + m_start_cam_position = cam_position (); + m_start_cam_azimuth = cam_azimuth (); + m_start_cam_elevation = cam_elevation (); m_start_displacement = m_displacement; m_focus_dist = 2.0; if (m_dragging || m_rotating) { - QVector3D cd = cam_direction (m_start_cam_azimuth, m_start_cam_elevation); - std::pair hp = hit_point_with_scene (m_cam_position, cd); - if (hp.first) { - m_focus_dist = std::max (m_focus_dist, double ((m_cam_position - hp.second).length ())); - } + QVector3D cd = cam_direction (); + QVector3D cp = cam_position (); + QVector3D hp = hit_point_with_scene (cp + m_focus_dist * cd, cd); + m_focus_dist = std::max (m_focus_dist, double ((cp - hp).length ())); } } @@ -275,9 +313,9 @@ D25ViewWidget::mouseMoveEvent (QMouseEvent *event) double dx = d.x () * f; double dy = -d.y () * f; - QVector3D xv (cos (m_start_cam_azimuth * M_PI / 180.0), 0.0, -sin (m_start_cam_azimuth * M_PI / 180.0)); - double re = sin (m_start_cam_elevation * M_PI / 180.0); - QVector3D yv (-re * xv.z (), cos (m_start_cam_elevation * M_PI / 180.0), re * xv.x ()); + QVector3D xv (cos (cam_azimuth () * M_PI / 180.0), 0.0, -sin (cam_azimuth () * M_PI / 180.0)); + double re = sin (cam_elevation () * M_PI / 180.0); + QVector3D yv (-re * xv.z (), cos (cam_elevation () * M_PI / 180.0), re * xv.x ()); QVector3D drag = xv * dx + yv * dy; m_displacement = m_start_displacement + drag; @@ -286,6 +324,8 @@ D25ViewWidget::mouseMoveEvent (QMouseEvent *event) } else if (m_rotating) { + // @@@ needs redo ... + // @@@ consider m_top_view double focus_dist = 4.0; // @@@ QPoint d = event->pos () - m_start_pos; @@ -296,30 +336,64 @@ D25ViewWidget::mouseMoveEvent (QMouseEvent *event) m_cam_elevation = m_start_cam_elevation + ay; m_cam_azimuth = m_start_cam_azimuth + ax; - m_cam_position = (cam_direction (m_cam_azimuth, m_cam_elevation) * -focus_dist) + cam_direction (m_start_cam_azimuth, m_start_cam_elevation) * focus_dist + m_start_cam_position; - update_cam_trans (); } } +QVector3D +D25ViewWidget::cam_direction () const +{ + double azimuth = cam_azimuth (); + double elevation = cam_elevation (); + + // positive azimuth: camera looks left + // positive elevation: camera looks up + double y = sin (elevation * M_PI / 180.0); + double r = cos (elevation * M_PI / 180.0); + double x = r * sin (azimuth * M_PI / 180.0); + double z = r * cos (azimuth * M_PI / 180.0); + return QVector3D (x, y, -z); +} + +QVector3D +D25ViewWidget::cam_position () const +{ + double focus_dist = 4.0; + return cam_direction () * -focus_dist; +} + +double +D25ViewWidget::cam_azimuth () const +{ + return m_cam_azimuth; +} + +double +D25ViewWidget::cam_elevation () const +{ + return m_top_view ? -90.0 : m_cam_elevation; +} + void D25ViewWidget::update_cam_trans () { -printf("@@@ e=%g a=%g x,y,z=%g,%g,%g d=%g,%g,%g s=%g f=%g\n", m_cam_elevation, m_cam_azimuth, m_cam_position.x(), m_cam_position.y(), m_cam_position.z(), m_displacement.x(), m_displacement.y(), m_displacement.z(), m_scale_factor, m_focus_dist); fflush(stdout); + QVector3D cp = cam_position (); + +printf("@@@ e=%g a=%g x,y,z=%g,%g,%g d=%g,%g,%g s=%g f=%g\n", cam_elevation (), cam_azimuth (), cp.x(), cp.y(), cp.z(), m_displacement.x(), m_displacement.y(), m_displacement.z(), m_scale_factor, m_focus_dist); fflush(stdout); QMatrix4x4 t; // finally add perspective t.perspective (60.0f, float (width ()) / float (height ()), 0.1f, 100.0f); // third: elevation - t.rotate (-m_cam_elevation, 1.0, 0.0, 0.0); + t.rotate (-cam_elevation (), 1.0, 0.0, 0.0); // second: azimuth - t.rotate (m_cam_azimuth, 0.0, 1.0, 0.0); + t.rotate (cam_azimuth (), 0.0, 1.0, 0.0); // first: translate the origin into the cam's position - t.translate (-m_cam_position); + t.translate (-cam_position ()); m_cam_trans = t; diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h index 26a0653ba..68bc1a468 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h @@ -57,6 +57,8 @@ public: D25ViewWidget (QWidget *parent); ~D25ViewWidget (); + void keyPressEvent (QKeyEvent *event); + void keyReleaseEvent (QKeyEvent *event); void wheelEvent (QWheelEvent *event); void mousePressEvent (QMouseEvent *event); void mouseReleaseEvent (QMouseEvent *event); @@ -70,9 +72,9 @@ private: QOpenGLShaderProgram *m_shapes_program, *m_gridplane_program; QMatrix4x4 m_cam_trans; bool m_dragging, m_rotating; - QVector3D m_cam_position; double m_scale_factor; double m_cam_azimuth, m_cam_elevation; + bool m_top_view; QVector3D m_displacement; double m_focus_dist; QPoint m_start_pos; @@ -101,7 +103,11 @@ private: void render_layout (D25ViewWidget::chunks_type &chunks, const db::Layout &layout, const db::Cell &cell, unsigned int layer, double zstart, double zstop); void render_polygon (D25ViewWidget::chunks_type &chunks, const db::Polygon &poly, double dbu, double zstart, double zstop); void render_wall (D25ViewWidget::chunks_type &chunks, const db::Edge &poly, double dbu, double zstart, double zstop); - std::pair hit_point_with_scene (const QVector3D &line, const QVector3D &line_dir); + QVector3D hit_point_with_scene(const QVector3D &line, const QVector3D &line_dir); + double cam_elevation () const; + double cam_azimuth () const; + QVector3D cam_position () const; + QVector3D cam_direction () const; }; } diff --git a/src/plugins/tools/view_25d/unit_tests/layD25ViewUtilsTests.cc b/src/plugins/tools/view_25d/unit_tests/layD25ViewUtilsTests.cc index e5cf71c65..4d9daa5b6 100644 --- a/src/plugins/tools/view_25d/unit_tests/layD25ViewUtilsTests.cc +++ b/src/plugins/tools/view_25d/unit_tests/layD25ViewUtilsTests.cc @@ -166,3 +166,65 @@ TEST(5_CameraNormal) p = matrix.map (ray.first + ray.second * 1000.0); EXPECT_EQ (v2s_2d (p), "0,1"); } + +TEST(6_NormalizeSceneTrans) +{ + QMatrix4x4 cam; + cam.perspective (60.0f, 1.5, 0.1f, 100.0f); + cam.rotate (22.0, 1.0, 0.0, 0.0); + cam.rotate (-15.0, 0.0, 1.0, 0.0); + cam.translate (QVector3D (0.0, 0.0, 4.0)); + + double scale = 0.1; + QVector3D displacement (-0.5, 0.2, 2.0); + + QMatrix4x4 scene1; + scene1.translate (displacement); + scene1.scale (scale); + + QVector3D v1 = (cam * scene1).map (QVector3D (1.0, -1.0, 2.0)); + v1.setZ (0); + QVector3D v2 = (cam * scene1).map (QVector3D (0.0, 0.0, 5.0)); + v2.setZ (0); + QVector3D v3 = (cam * scene1).map (QVector3D (-1.0, 0.0, 1.0)); + v3.setZ (0); + + lay::normalize_scene_trans (cam, displacement, scale); + + QMatrix4x4 scene2; + scene2.translate (displacement); + scene2.scale (scale); + + EXPECT_EQ (tl::sprintf ("%.4f", scale), "0.0667"); + + QVector3D u1 = (cam * scene2).map (QVector3D (1.0, -1.0, 2.0)); + u1.setZ (0); + QVector3D u2 = (cam * scene2).map (QVector3D (0.0, 0.0, 5.0)); + u2.setZ (0); + QVector3D u3 = (cam * scene2).map (QVector3D (-1.0, 0.0, 1.0)); + u3.setZ (0); + + EXPECT_EQ ((u1 - v1).length () < 1e-4, true); + EXPECT_EQ ((u2 - v2).length () < 1e-4, true); + EXPECT_EQ ((u3 - v3).length () < 1e-4, true); + + lay::normalize_scene_trans (cam, displacement, scale, 1.0); + + QMatrix4x4 scene3; + scene3.translate (displacement); + scene3.scale (scale); + + EXPECT_EQ (tl::sprintf ("%.4f", scale), "0.0833"); + EXPECT_EQ (tl::to_string (displacement.z ()), "1"); + + QVector3D uu1 = (cam * scene2).map (QVector3D (1.0, -1.0, 2.0)); + uu1.setZ (0); + QVector3D uu2 = (cam * scene2).map (QVector3D (0.0, 0.0, 5.0)); + uu2.setZ (0); + QVector3D uu3 = (cam * scene2).map (QVector3D (-1.0, 0.0, 1.0)); + uu3.setZ (0); + + EXPECT_EQ ((uu1 - v1).length () < 1e-4, true); + EXPECT_EQ ((uu2 - v2).length () < 1e-4, true); + EXPECT_EQ ((uu3 - v3).length () < 1e-4, true); +} From 55e79ef78fbfc976ea0cb49bf84ae1fb70a09d30 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 12 Apr 2020 16:56:30 +0200 Subject: [PATCH 011/113] WIP. --- .../view_25d/lay_plugin/layD25ViewWidget.cc | 45 +++++++++---------- .../view_25d/lay_plugin/layD25ViewWidget.h | 3 +- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index 04f965992..ee173e776 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -190,7 +190,7 @@ D25ViewWidget::wheelEvent (QWheelEvent *event) double py = -(event->pos ().y () - height () / 2) * 2.0 / height (); // compute vector of line of sight - std::pair ray = camera_normal (m_cam_trans, px, py); + std::pair ray = camera_normal (cam_perspective () * cam_trans (), px, py); // by definition the ray goes through the camera position float focal_length = 2.0; @@ -217,15 +217,11 @@ D25ViewWidget::wheelEvent (QWheelEvent *event) // normalize the scene translation so the scene does not "flee" - QMatrix4x4 ct; - ct.rotate (-cam_elevation (), 1.0, 0.0, 0.0); - ct.rotate (cam_azimuth (), 0.0, 1.0, 0.0); - ct.translate (-cam_position ()); - + QMatrix4x4 ct = cam_trans (); initial_displacement = ct.map (initial_displacement); displacement = ct.map (displacement); - lay::normalize_scene_trans (m_cam_trans, displacement, m_scale_factor, initial_displacement.z ()); + lay::normalize_scene_trans (cam_perspective (), displacement, m_scale_factor, initial_displacement.z ()); m_displacement = ct.inverted ().map (displacement); @@ -375,27 +371,30 @@ D25ViewWidget::cam_elevation () const return m_top_view ? -90.0 : m_cam_elevation; } +QMatrix4x4 +D25ViewWidget::cam_perspective () const +{ + QMatrix4x4 t; + t.perspective (60.0f, float (width ()) / float (height ()), 0.1f, 100.0f); + return t; +} + +QMatrix4x4 +D25ViewWidget::cam_trans () const +{ + QMatrix4x4 t; + t.rotate (-cam_elevation (), 1.0, 0.0, 0.0); + t.rotate (cam_azimuth (), 0.0, 1.0, 0.0); + t.translate (-cam_position ()); + return t; +} + void D25ViewWidget::update_cam_trans () { QVector3D cp = cam_position (); printf("@@@ e=%g a=%g x,y,z=%g,%g,%g d=%g,%g,%g s=%g f=%g\n", cam_elevation (), cam_azimuth (), cp.x(), cp.y(), cp.z(), m_displacement.x(), m_displacement.y(), m_displacement.z(), m_scale_factor, m_focus_dist); fflush(stdout); - QMatrix4x4 t; - - // finally add perspective - t.perspective (60.0f, float (width ()) / float (height ()), 0.1f, 100.0f); - - // third: elevation - t.rotate (-cam_elevation (), 1.0, 0.0, 0.0); - - // second: azimuth - t.rotate (cam_azimuth (), 0.0, 1.0, 0.0); - - // first: translate the origin into the cam's position - t.translate (-cam_position ()); - - m_cam_trans = t; update (); } @@ -656,7 +655,7 @@ D25ViewWidget::paintGL () // this way we can use y as z coordinate when drawing scene_trans.scale (1.0, 1.0, -1.0); - m_shapes_program->setUniformValue ("matrix", m_cam_trans * scene_trans); + m_shapes_program->setUniformValue ("matrix", cam_perspective () * cam_trans () * scene_trans); // NOTE: z axis of illum points towards the scene because we include the z inversion in the scene transformation matrix m_shapes_program->setUniformValue ("illum", QVector3D (-3.0, -4.0, 2.0).normalized ()); diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h index 68bc1a468..c467510d8 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h @@ -70,7 +70,6 @@ private: typedef lay::mem_chunks chunks_type; QOpenGLShaderProgram *m_shapes_program, *m_gridplane_program; - QMatrix4x4 m_cam_trans; bool m_dragging, m_rotating; double m_scale_factor; double m_cam_azimuth, m_cam_elevation; @@ -108,6 +107,8 @@ private: double cam_azimuth () const; QVector3D cam_position () const; QVector3D cam_direction () const; + QMatrix4x4 cam_perspective () const; + QMatrix4x4 cam_trans () const; }; } From 75b1b4dc54499e67833a692174633a161d418fae Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 12 Apr 2020 18:38:39 +0200 Subject: [PATCH 012/113] WIP. --- .../view_25d/lay_plugin/layD25ViewUtils.cc | 48 +++++++++---------- .../view_25d/lay_plugin/layD25ViewWidget.cc | 17 +++---- .../unit_tests/layD25ViewUtilsTests.cc | 14 +++--- 3 files changed, 40 insertions(+), 39 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc index e63a75969..ba690ee99 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc @@ -164,60 +164,60 @@ camera_normal (const QMatrix4x4 &camera_trans, double x, double y) void normalize_scene_trans (const QMatrix4x4 &cam_trans, QVector3D &displacement, double &scale, double ztarget) { - QMatrix4x4 m; - // Here is the theory: // Let: // cam = ( M t ) M = 3x3 matrix, t = 3x1 translation vector, z = scalar, p = 1x3 perspective // ( p z ) // and: - // scene = ( S d ) S = s*U1 (s = scale factor, U1 = 3x3 unit matrix), d = 3x1 displacement vector - // ( 0 1 ) + // scene = ( S d*s ) S = s*U1 (s = scale factor, U1 = 3x3 unit matrix), d = 3x1 displacement vector + // ( 0 1 ) // then: - // cam * scene = ( M*s M*d+t ) - // ( p*s p*d+z ) (p*d = dot product) + // cam * scene = ( M*s M*d*s+t ) + // ( p*s p*d*s+z ) (p*d = dot product) // // this is image invariant (only x,y results are considered) against changes of s (s->s') if // - // 1.) (p*d+z)/s = (p*d'+z)/s' - // 2.) (M*d+t)/s = (M*d'+t)/s' for [x] and [y] + // 1.) (p*d*s+z)/s = (p*d'*s'+z)/s' (because x and y will be devided by this value) + // 2.) (M*d*s+t)/s = (M*d'*s'+t)/s' for [x] and [y] // - // Ff we seek a solution with d'[z] == b (b = ztarget), we get these equations (f:=s'/s) + // or // - // 2.) M[xx] * d'[x] + M[xy] * d'[y] - ((M*d)[x] + t[x]) * f = -t[x] - M[xz] * b - // M[yx] * d'[x] + M[yy] * d'[y] - ((M*d)[y] + t[y]) * f = -t[y] - M[yz] * b - // 1.) p[x] * d'[x] + p[y] * d'[y] - (p*d+z) * f = -z - p[z] * b + // 1.) p*d+z/s = p*d'+z/s' + // 2.) M*d+t/s = M*d'+t/s' + // + // If we seek a solution with d'[z] == b (b = ztarget), we get these equations (f:=1/s') + // + // 2.) M[xx] * d'[x] + M[xy] * d'[y] + t[x] * f = (M*d)[x] + t[x]/s - M[xz]*b + // M[yx] * d'[x] + M[yy] * d'[y] + t[y] * f = (M*d)[y] + t[y]/s - M[yz]*b + // 1.) p[x] * d'[x] + p[y] * d'[y] + z * f = p*d + z/s - p[z]*b // // we can solve these equations for d'[x], d'[y] and f. // With p[x]=M[wx], p[y]=M[wy] and z=t[w], the above equation system can be written as // - // M[ix] * d'[x] + M[iy] * d'[y] - ((M*d)[i] + t[i]) * f = -t[i] - M[iz]*b i = x,y,w - // - // and with M,t->M (4x4 matrix) and d->(d,1) (4d vector) - // - // M[ix] * d'[x] + M[iy] * d'[y] - (M*d)[i] * f = -t[i] - M[iz]*b i = x,y,w + // M[ix] * d'[x] + M[iy] * d'[y] + t[i] * f = (M*d)[i] - M[iz]*b + t[i]/s i = x,y,w // - QVector4D d4 (displacement); - d4.setW (1.0); + QMatrix4x4 m; for (int i = 0; i < 4; ++i) { if (i != 2) { m (i, 0) = cam_trans (i, 0); m (i, 1) = cam_trans (i, 1); - m (i, 3) = -QVector4D::dotProduct (cam_trans.row (i), d4); + m (i, 3) = cam_trans (i, 3); } } bool invertable = false; - m = m.inverted (&invertable); + QMatrix4x4 minv = m.inverted (&invertable); if (! invertable) { return; } - QVector4D sol = m.map (-cam_trans.column (3) - cam_trans.column (2) * ztarget); - if (sol.w () > 0.01 /*skip weird solutions*/) { - scale *= sol.w (); + QVector4D rhs = cam_trans.map (QVector4D (displacement.x (), displacement.y (), displacement.z () - ztarget, 1.0 / scale)); + QVector4D sol = minv.map (rhs); + double f = sol.w (); + if (f > 1e-6 /*skip weird solutions*/) { + scale = 1.0 / f; displacement = QVector3D (sol.x (), sol.y (), ztarget); } } diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index ee173e776..5892e329a 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -201,7 +201,7 @@ D25ViewWidget::wheelEvent (QWheelEvent *event) // "Shift" is closeup double f = event->angleDelta ().y () * (1.0 / (90 * 8)); - m_displacement += -f * cam_position ().length () * ray.second; + m_displacement += -(f / m_scale_factor) * cam_position ().length () * ray.second; } else { @@ -212,7 +212,7 @@ D25ViewWidget::wheelEvent (QWheelEvent *event) QVector3D initial_displacement = m_displacement; QVector3D displacement = m_displacement; - displacement = hp * (1.0 - f) + displacement * f; + displacement += hp * (1.0 - f) / (f * m_scale_factor); m_scale_factor *= f; // normalize the scene translation so the scene does not "flee" @@ -251,7 +251,7 @@ D25ViewWidget::keyReleaseEvent (QKeyEvent *event) QVector3D D25ViewWidget::hit_point_with_scene (const QVector3D &line, const QVector3D &line_dir) { - QVector3D corner = QVector3D (m_bbox.left (), m_zmin, -(m_bbox.bottom () + m_bbox.height ())) * m_scale_factor + m_displacement; + QVector3D corner = (QVector3D (m_bbox.left (), m_zmin, -(m_bbox.bottom () + m_bbox.height ())) + m_displacement) * m_scale_factor; QVector3D dim = QVector3D (m_bbox.width (), m_zmax - m_zmin, m_bbox.height ()) * m_scale_factor; std::pair hp = lay::hit_point_with_cuboid (line, line_dir, corner, dim); @@ -314,7 +314,7 @@ D25ViewWidget::mouseMoveEvent (QMouseEvent *event) QVector3D yv (-re * xv.z (), cos (cam_elevation () * M_PI / 180.0), re * xv.x ()); QVector3D drag = xv * dx + yv * dy; - m_displacement = m_start_displacement + drag; + m_displacement = m_start_displacement + drag / m_scale_factor; update_cam_trans (); @@ -355,7 +355,7 @@ D25ViewWidget::cam_direction () const QVector3D D25ViewWidget::cam_position () const { - double focus_dist = 4.0; + double focus_dist = 4.0; // @@@ return cam_direction () * -focus_dist; } @@ -374,8 +374,10 @@ D25ViewWidget::cam_elevation () const QMatrix4x4 D25ViewWidget::cam_perspective () const { + double focus_dist = 4.0; // @@@ QMatrix4x4 t; t.perspective (60.0f, float (width ()) / float (height ()), 0.1f, 100.0f); + t.translate (QVector3D (0.0, 0.0, -focus_dist)); return t; } @@ -385,7 +387,6 @@ D25ViewWidget::cam_trans () const QMatrix4x4 t; t.rotate (-cam_elevation (), 1.0, 0.0, 0.0); t.rotate (cam_azimuth (), 0.0, 1.0, 0.0); - t.translate (-cam_position ()); return t; } @@ -649,9 +650,9 @@ D25ViewWidget::paintGL () QMatrix4x4 scene_trans; - // provide the displacement and scaling - scene_trans.translate (m_displacement); + // provide the displacement and scaling (in this order!) scene_trans.scale (m_scale_factor); + scene_trans.translate (m_displacement); // this way we can use y as z coordinate when drawing scene_trans.scale (1.0, 1.0, -1.0); diff --git a/src/plugins/tools/view_25d/unit_tests/layD25ViewUtilsTests.cc b/src/plugins/tools/view_25d/unit_tests/layD25ViewUtilsTests.cc index 4d9daa5b6..b24265b22 100644 --- a/src/plugins/tools/view_25d/unit_tests/layD25ViewUtilsTests.cc +++ b/src/plugins/tools/view_25d/unit_tests/layD25ViewUtilsTests.cc @@ -176,11 +176,11 @@ TEST(6_NormalizeSceneTrans) cam.translate (QVector3D (0.0, 0.0, 4.0)); double scale = 0.1; - QVector3D displacement (-0.5, 0.2, 2.0); + QVector3D displacement (-5.0, 2.0, 20.0); QMatrix4x4 scene1; - scene1.translate (displacement); scene1.scale (scale); + scene1.translate (displacement); QVector3D v1 = (cam * scene1).map (QVector3D (1.0, -1.0, 2.0)); v1.setZ (0); @@ -192,8 +192,8 @@ TEST(6_NormalizeSceneTrans) lay::normalize_scene_trans (cam, displacement, scale); QMatrix4x4 scene2; - scene2.translate (displacement); scene2.scale (scale); + scene2.translate (displacement); EXPECT_EQ (tl::sprintf ("%.4f", scale), "0.0667"); @@ -208,14 +208,14 @@ TEST(6_NormalizeSceneTrans) EXPECT_EQ ((u2 - v2).length () < 1e-4, true); EXPECT_EQ ((u3 - v3).length () < 1e-4, true); - lay::normalize_scene_trans (cam, displacement, scale, 1.0); + lay::normalize_scene_trans (cam, displacement, scale, 10.0); QMatrix4x4 scene3; - scene3.translate (displacement); scene3.scale (scale); + scene3.translate (displacement); - EXPECT_EQ (tl::sprintf ("%.4f", scale), "0.0833"); - EXPECT_EQ (tl::to_string (displacement.z ()), "1"); + EXPECT_EQ (tl::sprintf ("%.4f", scale), "0.0800"); + EXPECT_EQ (tl::to_string (displacement.z ()), "10"); QVector3D uu1 = (cam * scene2).map (QVector3D (1.0, -1.0, 2.0)); uu1.setZ (0); From 194a2747e68b2e54b424bf15640927e9211426c4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 12 Apr 2020 19:15:11 +0200 Subject: [PATCH 013/113] WIP: better hit point detection --- .../view_25d/lay_plugin/layD25ViewUtils.cc | 29 +++++++++++-------- .../view_25d/lay_plugin/layD25ViewWidget.cc | 13 ++------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc index ba690ee99..9d2bfc359 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc @@ -63,12 +63,20 @@ cutpoint_line_with_face (const QVector3D &line, const QVector3D &dir, const QVec } } +static bool somewhat_perpendicular (const QVector3D &a, const QVector3D &b) +{ + // returns true if a and b are perpendicular within 30 degree + return fabs (QVector3D::dotProduct (a, b) < 0.5 * a.length () * b.length ()); +} + static std::pair plane_or_face (const QVector3D &line, const QVector3D &line_dir, const QVector3D &corner, const QVector3D &u, const QVector3D &v, bool face) { if (face) { return cutpoint_line_with_face (line, line_dir, corner, u, v); - } else { + } else if (somewhat_perpendicular (u, line_dir) && somewhat_perpendicular (v, line_dir)) { return cutpoint_line_with_plane (line, line_dir, corner, QVector3D::crossProduct (u, v)); + } else { + return std::make_pair (false, QVector3D ()); } } @@ -98,17 +106,14 @@ hit_point_with_cuboid (const QVector3D &line, const QVector3D &line_dir, const Q cutpoints.push_back (plane_or_face (line, line_dir, corner, QVector3D (dim.x (), 0, 0), QVector3D (0, dim.y (), 0), face)); // back cutpoints.push_back (plane_or_face (line, line_dir, corner + QVector3D (0, 0, dim.z ()), QVector3D (dim.x (), 0, 0), QVector3D (0, dim.y (), 0), face)); - - if (face) { - // bottom - cutpoints.push_back (plane_or_face (line, line_dir, corner, QVector3D (dim.x (), 0, 0), QVector3D (0, 0, dim.z ()), face)); - // top - cutpoints.push_back (plane_or_face (line, line_dir, corner + QVector3D (0, dim.y (), 0), QVector3D (dim.x (), 0, 0), QVector3D (0, 0, dim.z ()), face)); - // left - cutpoints.push_back (plane_or_face (line, line_dir, corner, QVector3D (0, 0, dim.z ()), QVector3D (0, dim.y (), 0), face)); - // right - cutpoints.push_back (plane_or_face (line, line_dir, corner + QVector3D (dim.x (), 0, 0), QVector3D (0, 0, dim.z ()), QVector3D (0, dim.y (), 0), face)); - } + // bottom + cutpoints.push_back (plane_or_face (line, line_dir, corner, QVector3D (dim.x (), 0, 0), QVector3D (0, 0, dim.z ()), face)); + // top + cutpoints.push_back (plane_or_face (line, line_dir, corner + QVector3D (0, dim.y (), 0), QVector3D (dim.x (), 0, 0), QVector3D (0, 0, dim.z ()), face)); + // left + cutpoints.push_back (plane_or_face (line, line_dir, corner, QVector3D (0, 0, dim.z ()), QVector3D (0, dim.y (), 0), face)); + // right + cutpoints.push_back (plane_or_face (line, line_dir, corner + QVector3D (dim.x (), 0, 0), QVector3D (0, 0, dim.z ()), QVector3D (0, dim.y (), 0), face)); double min_dist = 0.0; int min_dist_index = -1; diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index 5892e329a..ef5a708a9 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -340,16 +340,9 @@ D25ViewWidget::mouseMoveEvent (QMouseEvent *event) QVector3D D25ViewWidget::cam_direction () const { - double azimuth = cam_azimuth (); - double elevation = cam_elevation (); - - // positive azimuth: camera looks left - // positive elevation: camera looks up - double y = sin (elevation * M_PI / 180.0); - double r = cos (elevation * M_PI / 180.0); - double x = r * sin (azimuth * M_PI / 180.0); - double z = r * cos (azimuth * M_PI / 180.0); - return QVector3D (x, y, -z); + QVector3D cd = cam_trans ().map (QVector3D (0, 0, 1)); + cd.setZ (-cd.z ()); + return cd; } QVector3D From fd2749895c4be525de683a2b188153ab6cd5f5b6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 13 Apr 2020 18:13:17 +0200 Subject: [PATCH 014/113] WIP: foreground light, enhanced navigation ... --- .../view_25d/lay_plugin/layD25ViewUtils.cc | 4 +- .../view_25d/lay_plugin/layD25ViewWidget.cc | 524 ++++++++++++------ .../view_25d/lay_plugin/layD25ViewWidget.h | 9 +- 3 files changed, 355 insertions(+), 182 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc index 9d2bfc359..b2fceddba 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc @@ -122,9 +122,7 @@ hit_point_with_cuboid (const QVector3D &line, const QVector3D &line_dir, const Q for (std::vector >::const_iterator i = cutpoints.begin (); i != cutpoints.end (); ++i) { if (i->first) { double dist = QVector3D::dotProduct (i->second - line, ld_norm); - if (dist < -epsilon) { - // ignore all cutpoints behind us - } else if (min_dist_index < 0) { + if (min_dist_index < 0) { min_dist = dist; min_dist_index = int (i - cutpoints.begin ()); } else if (dist < min_dist) { diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index ef5a708a9..0c3437017 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -45,147 +45,51 @@ namespace lay D25ViewWidget::D25ViewWidget (QWidget *parent) : QOpenGLWidget (parent), - m_shapes_program (0), m_dragging (false), m_rotating (false), m_cam_azimuth (0.0), m_cam_elevation (0.0), m_top_view (false) + m_shapes_program (0) { QSurfaceFormat format; format.setDepthBufferSize (24); format.setSamples (4); // more -> widget extends beyond boundary! + format.setStencilBufferSize (8); + // @@@? format.setVersion (3, 2); + format.setProfile (QSurfaceFormat::CoreProfile); setFormat (format); - m_scale_factor = 1.0; - m_focus_dist = 0.0; + m_zmin = m_zmax = 0.0; + mp_view = 0; } D25ViewWidget::~D25ViewWidget () { // Make sure the context is current and then explicitly // destroy all underlying OpenGL resources. - makeCurrent(); + makeCurrent (); delete m_shapes_program; - doneCurrent(); + doneCurrent (); } void -D25ViewWidget::initializeGL () +D25ViewWidget::reset () { - QOpenGLFunctions::initializeOpenGLFunctions(); + m_scale_factor = 1.0; + m_focus_dist = 0.0; + m_fov = 60.0; + m_cam_azimuth = m_cam_elevation = 0.0; + m_top_view = false; + m_dragging = m_rotating = false; - glEnable (GL_DEPTH_TEST); - glEnable (GL_BLEND); - // @@@ dark background - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - // @@@ white background - // @@@ glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA); - - static const char *shapes_vertex_shader_source = - "#version 320 es\n" - "#undef lowp\n" - "#undef highp\n" - "#undef mediump\n" - "layout (location = 0) in vec4 posAttr;\n" - "\n" - "void main() {\n" - " gl_Position = posAttr;\n" - "}\n"; - - static const char *shapes_geometry_shader_source = - "#version 320 es\n" - "#undef lowp\n" - "#undef highp\n" - "#undef mediump\n" - "\n" - "uniform vec4 color;\n" - "uniform vec4 ambient;\n" - "uniform vec3 illum;\n" - "out lowp vec4 vertexColor;\n" - "uniform mat4 matrix;\n" - "layout (triangles) in;\n" - "layout (triangle_strip, max_vertices = 3) out;\n" - "\n" - "void main() {\n" - " vec4 p0 = gl_in[0].gl_Position;\n" - " vec4 p1 = gl_in[1].gl_Position;\n" - " vec4 p2 = gl_in[2].gl_Position;\n" - " vec3 n = cross(p2.xyz - p0.xyz, p1.xyz - p0.xyz);\n" - " float dp = dot(normalize(n), illum);\n" - " vertexColor.rgb = color.rgb * (dp * 0.5 + 0.5) - (min(0.0, dp) * 0.5 * ambient.rgb);\n" - " vertexColor.a = 1.0;\n" - " gl_Position = matrix * p0;\n" - " EmitVertex();\n" - " gl_Position = matrix * p1;\n" - " EmitVertex();\n" - " gl_Position = matrix * p2;\n" - " EmitVertex();\n" - " EndPrimitive();\n" - "}\n"; - - static const char *shapes_fragment_shader_source = - "#version 320 es\n" - "#undef lowp\n" - "#undef highp\n" - "#undef mediump\n" - "in lowp vec4 vertexColor;\n" - "out lowp vec4 fragColor;\n" - "void main() {\n" - " fragColor = vertexColor;\n" - "}\n"; - - m_shapes_program = new QOpenGLShaderProgram (this); - if (! m_shapes_program->addShaderFromSourceCode (QOpenGLShader::Vertex, shapes_vertex_shader_source)) { - throw tl::Exception (std::string ("Shapes vertex shader compilation failed:\n") + tl::to_string (m_shapes_program->log ())); - } - if (! m_shapes_program->addShaderFromSourceCode (QOpenGLShader::Geometry, shapes_geometry_shader_source)) { - throw tl::Exception (std::string ("Shapes geometry shader compilation failed:\n") + tl::to_string (m_shapes_program->log ())); - } - if (! m_shapes_program->addShaderFromSourceCode (QOpenGLShader::Fragment, shapes_fragment_shader_source)) { - throw tl::Exception (std::string ("Shapes fragment shader compilation failed:\n") + tl::to_string (m_shapes_program->log ())); - } - if (! m_shapes_program->link ()) { - throw tl::Exception (std::string ("Shapes shader program linking failed failed:\n") + tl::to_string (m_shapes_program->log ())); - } - - // grid plane shader source - - static const char *gridplan_vertex_shader_source = - "#version 320 es\n" - "#undef lowp\n" - "#undef highp\n" - "#undef mediump\n" - "layout (location = 0) in vec4 posAttr;\n" - "uniform mat4 matrix;\n" - "\n" - "void main() {\n" - " gl_Position = matrix * posAttr;\n" - "}\n"; - - static const char *gridplan_fragment_shader_source = - "#version 320 es\n" - "#undef lowp\n" - "#undef highp\n" - "#undef mediump\n" - "uniform lowp vec4 color;\n" - "out lowp vec4 fragColor;\n" - "void main() {\n" - " fragColor = color;\n" - "}\n"; - - m_gridplane_program = new QOpenGLShaderProgram (this); - if (! m_gridplane_program->addShaderFromSourceCode (QOpenGLShader::Vertex, gridplan_vertex_shader_source)) { - throw tl::Exception (std::string ("Grid plane vertex shader compilation failed:\n") + tl::to_string (m_gridplane_program->log ())); - } - if (! m_gridplane_program->addShaderFromSourceCode (QOpenGLShader::Fragment, gridplan_fragment_shader_source)) { - throw tl::Exception (std::string ("Grid plane fragment shader compilation failed:\n") + tl::to_string (m_gridplane_program->log ())); - } - if (! m_gridplane_program->link ()) { - throw tl::Exception (std::string ("Grid plane shader program linking failed:\n") + tl::to_string (m_gridplane_program->log ())); - } + refresh (); } void D25ViewWidget::wheelEvent (QWheelEvent *event) { + if (event->angleDelta ().y () == 0) { + return; + } + double px = (event->pos ().x () - width () / 2) * 2.0 / width (); double py = -(event->pos ().y () - height () / 2) * 2.0 / height (); @@ -193,15 +97,14 @@ D25ViewWidget::wheelEvent (QWheelEvent *event) std::pair ray = camera_normal (cam_perspective () * cam_trans (), px, py); // by definition the ray goes through the camera position - float focal_length = 2.0; - QVector3D hp = hit_point_with_scene (cam_position () + focal_length * ray.second, ray.second); + QVector3D hp = hit_point_with_scene (ray.second); - if (false /*@@@*/ && (event->modifiers () & Qt::ShiftModifier)) { + if (event->modifiers () & Qt::ControlModifier) { - // "Shift" is closeup + // "Ctrl" is closeup double f = event->angleDelta ().y () * (1.0 / (90 * 8)); - m_displacement += -(f / m_scale_factor) * cam_position ().length () * ray.second; + m_displacement += -((f / m_scale_factor) * cam_dist ()) * ray.second; } else { @@ -212,8 +115,8 @@ D25ViewWidget::wheelEvent (QWheelEvent *event) QVector3D initial_displacement = m_displacement; QVector3D displacement = m_displacement; - displacement += hp * (1.0 - f) / (f * m_scale_factor); m_scale_factor *= f; + displacement += hp * (1.0 - f) / m_scale_factor; // normalize the scene translation so the scene does not "flee" @@ -227,7 +130,7 @@ D25ViewWidget::wheelEvent (QWheelEvent *event) } - update_cam_trans (); + refresh (); } void @@ -235,7 +138,7 @@ D25ViewWidget::keyPressEvent (QKeyEvent *event) { if (event->key () == Qt::Key_Shift) { m_top_view = true; - update_cam_trans (); + refresh (); } } @@ -244,19 +147,25 @@ D25ViewWidget::keyReleaseEvent (QKeyEvent *event) { if (event->key () == Qt::Key_Shift) { m_top_view = false; - update_cam_trans (); + refresh (); } } QVector3D -D25ViewWidget::hit_point_with_scene (const QVector3D &line, const QVector3D &line_dir) +D25ViewWidget::hit_point_with_scene (const QVector3D &line_dir) { + double min_focus_dist = 0.5; + QVector3D corner = (QVector3D (m_bbox.left (), m_zmin, -(m_bbox.bottom () + m_bbox.height ())) + m_displacement) * m_scale_factor; QVector3D dim = QVector3D (m_bbox.width (), m_zmax - m_zmin, m_bbox.height ()) * m_scale_factor; + QVector3D line = cam_position (); std::pair hp = lay::hit_point_with_cuboid (line, line_dir, corner, dim); if (! hp.first) { - return line; + return line + line_dir * min_focus_dist; + } else if (QVector3D::dotProduct (line_dir, hp.second - line) < min_focus_dist) { + // limit to min focus distance (not behind) + return line + line_dir * min_focus_dist; } else { return hp.second; } @@ -279,13 +188,27 @@ D25ViewWidget::mousePressEvent (QMouseEvent *event) m_start_displacement = m_displacement; m_focus_dist = 2.0; + m_hit_point = QVector3D (); - if (m_dragging || m_rotating) { + if (m_dragging) { - QVector3D cd = cam_direction (); - QVector3D cp = cam_position (); - QVector3D hp = hit_point_with_scene (cp + m_focus_dist * cd, cd); - m_focus_dist = std::max (m_focus_dist, double ((cp - hp).length ())); + // by definition the ray goes through the camera position + QVector3D hp = hit_point_with_scene (cam_direction ()); + + m_focus_dist = std::max (m_focus_dist, double ((m_start_cam_position - hp).length ())); + + } else if (m_rotating) { + + double px = (event->pos ().x () - width () / 2) * 2.0 / width (); + double py = -(event->pos ().y () - height () / 2) * 2.0 / height (); + + // compute vector of line of sight + std::pair ray = camera_normal (cam_perspective () * cam_trans (), px, py); + + // by definition the ray goes through the camera position + m_hit_point = hit_point_with_scene (ray.second); + + m_focus_dist = std::max (m_focus_dist, double ((m_start_cam_position - m_hit_point).length ())); } } @@ -299,42 +222,65 @@ D25ViewWidget::mouseReleaseEvent (QMouseEvent * /*event*/) void D25ViewWidget::mouseMoveEvent (QMouseEvent *event) { + if (! m_dragging && ! m_rotating) { + return; + } + if (m_dragging) { - // for the chosen perspective transformation: - double cal = 0.6 * m_focus_dist; - QPoint d = event->pos () - m_start_pos; - double f = cal * 2.0 / double (height ()); + double f = tan ((cam_fov () / 2) / 180.0 * M_PI) * m_focus_dist * 2.0 / double (height ()); double dx = d.x () * f; double dy = -d.y () * f; - QVector3D xv (cos (cam_azimuth () * M_PI / 180.0), 0.0, -sin (cam_azimuth () * M_PI / 180.0)); - double re = sin (cam_elevation () * M_PI / 180.0); - QVector3D yv (-re * xv.z (), cos (cam_elevation () * M_PI / 180.0), re * xv.x ()); + QVector3D xv (cos (m_start_cam_azimuth * M_PI / 180.0), 0.0, sin (m_start_cam_azimuth * M_PI / 180.0)); + double re = sin (m_start_cam_elevation * M_PI / 180.0); + QVector3D yv (-re * xv.z (), cos (m_start_cam_elevation * M_PI / 180.0), re * xv.x ()); QVector3D drag = xv * dx + yv * dy; m_displacement = m_start_displacement + drag / m_scale_factor; - update_cam_trans (); - - } else if (m_rotating) { - - // @@@ needs redo ... - // @@@ consider m_top_view - double focus_dist = 4.0; // @@@ + } else { QPoint d = event->pos () - m_start_pos; + double f = tan ((cam_fov () / 2) / 180.0 * M_PI) * m_focus_dist * 2.0 / double (height ()); + double dx = d.x () * f; + double dy = -d.y () * f; - double ax = atan (d.x () / (0.5 * height ())) * 180 / M_PI; - double ay = atan (-d.y () / (0.5 * height ())) * 180 / M_PI; + if (m_hit_point.x () * dx < 0 && (dx + 2.0 * m_hit_point.x ()) * m_hit_point.x () > 0) { - m_cam_elevation = m_start_cam_elevation + ay; - m_cam_azimuth = m_start_cam_azimuth + ax; + double da = asin (dx / m_hit_point.x () - 1.0) * 180.0 / M_PI; +// @@@ printf("@@@ hp=%g,%g,%g drag=%g,%g -> da=%g\n", m_hit_point.x(), m_hit_point.y(), m_hit_point.z(), drag.x(), drag.z(), da); fflush(stdout); + m_cam_azimuth = m_start_cam_azimuth + da; - update_cam_trans (); + } + +#if 0 + // elevation change + if (! m_top_view) { + + dp = m_hit_point + QVector3D (0.0, float (drag.y ()), 0.0); + double de = QVector3D::crossProduct (dp, m_hit_point).length () * (drag.y () > 0 ? 1.0 : -1.0) / (dp.length () * m_hit_point.length ()) * 180.0 / M_PI; + m_cam_elevation = m_start_cam_elevation + de; + + } +#endif } + + refresh (); +} + +double +D25ViewWidget::cam_fov () const +{ + return m_fov; // @@@ +} + +double +D25ViewWidget::cam_dist () const +{ + return 4.0; // @@@ } QVector3D @@ -348,8 +294,7 @@ D25ViewWidget::cam_direction () const QVector3D D25ViewWidget::cam_position () const { - double focus_dist = 4.0; // @@@ - return cam_direction () * -focus_dist; + return cam_direction () * -cam_dist (); } double @@ -367,10 +312,9 @@ D25ViewWidget::cam_elevation () const QMatrix4x4 D25ViewWidget::cam_perspective () const { - double focus_dist = 4.0; // @@@ QMatrix4x4 t; - t.perspective (60.0f, float (width ()) / float (height ()), 0.1f, 100.0f); - t.translate (QVector3D (0.0, 0.0, -focus_dist)); + t.perspective (cam_fov (), float (width ()) / float (height ()), 0.1f, 10000.0f); + t.translate (QVector3D (0.0, 0.0, -cam_dist ())); return t; } @@ -384,7 +328,7 @@ D25ViewWidget::cam_trans () const } void -D25ViewWidget::update_cam_trans () +D25ViewWidget::refresh () { QVector3D cp = cam_position (); @@ -399,12 +343,9 @@ D25ViewWidget::attach_view (LayoutView *view) if (mp_view != view) { mp_view = view; - m_layers.clear (); - m_vertex_chunks.clear (); - if (mp_view) { - prepare_view (); - } + prepare_view (); + reset (); } } @@ -412,12 +353,20 @@ D25ViewWidget::attach_view (LayoutView *view) void D25ViewWidget::prepare_view () { - double z = 0.0, dz = 0.2; // @@@ + m_layers.clear (); + m_vertex_chunks.clear (); m_bbox = db::DBox (); bool zset = false; m_zmin = m_zmax = 0.0; + if (! mp_view) { + m_bbox = db::DBox (-1.0, -1.0, 1.0, 1.0); + return; + } + + double z = 0.0, dz = 0.2; // @@@ + for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) { if (! lp->has_children () && lp->visible (true) && lp->cellview_index () >= 0 && lp->cellview_index () < int (mp_view->cellviews ())) { @@ -427,11 +376,9 @@ D25ViewWidget::prepare_view () m_vertex_chunks.push_back (chunks_type ()); LayerInfo info; - // @@@ use alpha? info.color[0] = ((color >> 16) & 0xff) / 255.0f; info.color[1] = ((color >> 8) & 0xff) / 255.0f; info.color[2] = (color & 0xff) / 255.0f; - info.color[3] = 1.0; info.vertex_chunk = &m_vertex_chunks.back (); m_layers.push_back (info); @@ -627,20 +574,178 @@ D25ViewWidget::render_layout (D25ViewWidget::chunks_type &chunks, const db::Layo } } +static std::pair find_grid (double v) +{ + for (int p = -12; p < 12; ++p) { + double g10 = pow (10, double (p)); + if (v > 100 * g10) { + continue; + } else if (v < 10 * g10) { + return std::make_pair (g10, g10); + } else if (v < 20 * g10) { + return std::make_pair (g10, g10 * 0.1); + } else if (v < 50 * g10) { + return std::make_pair (2.0 * g10, g10); + } else { + return std::make_pair (5.0 * g10, g10); + } + } + + return std::make_pair (v, v); +} + +void +D25ViewWidget::initializeGL () +{ + QOpenGLFunctions::initializeOpenGLFunctions(); + + glEnable (GL_BLEND); + // @@@ dark background + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // @@@ white background + // @@@ glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA); + + static const char *shapes_vertex_shader_source = + "#version 320 es\n" + "#undef lowp\n" + "#undef highp\n" + "#undef mediump\n" + "layout (location = 0) in vec4 posAttr;\n" + "\n" + "void main() {\n" + " gl_Position = posAttr;\n" + "}\n"; + + static const char *shapes_geometry_shader_source = + "#version 320 es\n" + "#undef lowp\n" + "#undef highp\n" + "#undef mediump\n" + "\n" + "uniform vec4 color;\n" + "uniform vec4 ambient;\n" + "uniform vec3 illum;\n" + "out lowp vec4 vertexColor;\n" + "uniform mat4 geo_matrix;\n" + "uniform mat4 cam_matrix;\n" + "layout (triangles) in;\n" + "layout (triangle_strip, max_vertices = 3) out;\n" + "\n" + "void main() {\n" + " vec4 p0 = gl_in[0].gl_Position;\n" + " vec4 p1 = gl_in[1].gl_Position;\n" + " vec4 p2 = gl_in[2].gl_Position;\n" + " vec3 n = cross(p2.xyz - p0.xyz, p1.xyz - p0.xyz);\n" + " float dp = dot(normalize(n), illum);\n" + " vertexColor = color * (dp * 0.5 + 0.5) - (min(0.0, dp) * 0.5 * ambient);\n" + " vertexColor.a = 1.0;\n" + " gl_Position = cam_matrix * geo_matrix * p0;\n" + " EmitVertex();\n" + " gl_Position = cam_matrix * geo_matrix * p1;\n" + " EmitVertex();\n" + " gl_Position = cam_matrix * geo_matrix * p2;\n" + " EmitVertex();\n" + " EndPrimitive();\n" + "}\n"; + + static const char *shapes_fragment_shader_source = + "#version 320 es\n" + "#undef lowp\n" + "#undef highp\n" + "#undef mediump\n" + "in lowp vec4 vertexColor;\n" + "out lowp vec4 fragColor;\n" + "\n" + "vec4 color_by_z(lowp vec4 c, highp float z) {\n" + " lowp vec4 mist_color = vec4(c.g * 0.4, c.g * 0.4, c.g * 0.4, 1.0);\n" + " highp float d = 0.12;\n" // d + dd/2 = 0.15 = 1/? + " highp float dd = 0.06;\n" + " highp float f = 1.0;\n" + " if (z < d - dd) {\n" + " f = 0.0;\n" + " } else if (z < d + dd) {\n" + " f = (z - (d - dd)) / (2.0 * dd);\n" + " }\n" + " return (1.0 - f) * mist_color + f * c;\n" + "};\n" + "\n" + "void main() {\n" + " fragColor = color_by_z(vertexColor, gl_FragCoord.w);\n" + "}\n"; + + m_shapes_program = new QOpenGLShaderProgram (this); + if (! m_shapes_program->addShaderFromSourceCode (QOpenGLShader::Vertex, shapes_vertex_shader_source)) { + throw tl::Exception (std::string ("Shapes vertex shader compilation failed:\n") + tl::to_string (m_shapes_program->log ())); + } + if (! m_shapes_program->addShaderFromSourceCode (QOpenGLShader::Geometry, shapes_geometry_shader_source)) { + throw tl::Exception (std::string ("Shapes geometry shader compilation failed:\n") + tl::to_string (m_shapes_program->log ())); + } + if (! m_shapes_program->addShaderFromSourceCode (QOpenGLShader::Fragment, shapes_fragment_shader_source)) { + throw tl::Exception (std::string ("Shapes fragment shader compilation failed:\n") + tl::to_string (m_shapes_program->log ())); + } + if (! m_shapes_program->link ()) { + throw tl::Exception (std::string ("Shapes shader program linking failed failed:\n") + tl::to_string (m_shapes_program->log ())); + } + + // grid plane shader source + + static const char *gridplan_vertex_shader_source = + "#version 320 es\n" + "#undef lowp\n" + "#undef highp\n" + "#undef mediump\n" + "layout (location = 0) in vec4 posAttr;\n" + "uniform mat4 matrix;\n" + "\n" + "vec4 color_by_z(vec4 face_color, highp float z) {\n" + " vec4 mist_color = vec4(1.0, 1.0, 1.0, 1.0);\n" + " highp float d = -2.309;\n" // tan(camera_fov/2)*camera_dist + " highp float dd = 0.0;\n" + " highp float f = 1.0;\n" + " if (z < d - dd) {\n" + " f = 0.0;\n" + " } else if (z < d + dd) {\n" + " f = (z - (d - dd)) / (2.0 * dd);\n" + " }\n" + " return (1.0 - f) * mist_color + f * face_color;\n" + "};\n" + "\n" + "void main() {\n" + " gl_Position = matrix * posAttr;\n" + "}\n"; + + static const char *gridplan_fragment_shader_source = + "#version 320 es\n" + "#undef lowp\n" + "#undef highp\n" + "#undef mediump\n" + "uniform lowp vec4 color;\n" + "out lowp vec4 fragColor;\n" + "void main() {\n" + " fragColor = color;\n" + "}\n"; + + m_gridplane_program = new QOpenGLShaderProgram (this); + if (! m_gridplane_program->addShaderFromSourceCode (QOpenGLShader::Vertex, gridplan_vertex_shader_source)) { + throw tl::Exception (std::string ("Grid plane vertex shader compilation failed:\n") + tl::to_string (m_gridplane_program->log ())); + } + if (! m_gridplane_program->addShaderFromSourceCode (QOpenGLShader::Fragment, gridplan_fragment_shader_source)) { + throw tl::Exception (std::string ("Grid plane fragment shader compilation failed:\n") + tl::to_string (m_gridplane_program->log ())); + } + if (! m_gridplane_program->link ()) { + throw tl::Exception (std::string ("Grid plane shader program linking failed:\n") + tl::to_string (m_gridplane_program->log ())); + } +} + void D25ViewWidget::paintGL () { - printf("@@@ width=%d,height=%d\n", width(),height()); // @@@ const qreal retinaScale = devicePixelRatio (); glViewport (0, 0, width () * retinaScale, height () * retinaScale); glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // @@@ white background: glClearColor (1.0, 1.0, 1.0, 1.0); - const int positions = 0; - - m_shapes_program->bind (); - QMatrix4x4 scene_trans; // provide the displacement and scaling (in this order!) @@ -649,13 +754,19 @@ D25ViewWidget::paintGL () // this way we can use y as z coordinate when drawing scene_trans.scale (1.0, 1.0, -1.0); - m_shapes_program->setUniformValue ("matrix", cam_perspective () * cam_trans () * scene_trans); + const int positions = 0; + + m_shapes_program->bind (); + + m_shapes_program->setUniformValue ("geo_matrix", cam_trans () * scene_trans); + m_shapes_program->setUniformValue ("cam_matrix", cam_perspective ()); // NOTE: z axis of illum points towards the scene because we include the z inversion in the scene transformation matrix m_shapes_program->setUniformValue ("illum", QVector3D (-3.0, -4.0, 2.0).normalized ()); m_shapes_program->setUniformValue ("ambient", QVector4D (0.5, 0.5, 0.5, 0.5)); + glEnable (GL_DEPTH_TEST); glEnableVertexAttribArray (positions); for (std::list::const_iterator l = m_layers.begin (); l != m_layers.end (); ++l) { @@ -675,24 +786,82 @@ D25ViewWidget::paintGL () m_gridplane_program->bind (); + glEnable (GL_DEPTH_TEST); glEnableVertexAttribArray (positions); - // @@@ m_gridplane_program->setUniformValue ("matrix", m_cam_trans * m_scene_trans); - m_gridplane_program->setUniformValue ("matrix", QMatrix4x4 ()); // @@@ + m_gridplane_program->setUniformValue ("matrix", cam_perspective () * cam_trans () * scene_trans); + + std::pair gg = find_grid (std::max (m_bbox.width (), m_bbox.height ())); + double gminor = gg.second, gmajor = gg.first; + + double margin = std::max (m_bbox.width (), m_bbox.height ()) * 0.02; + double l = m_bbox.left () - margin; + double r = m_bbox.right () + margin; + double b = m_bbox.bottom () - margin; + double t = m_bbox.top () + margin; // @@@ + // major and minor grid lines + + GLfloat gridline_vertices[6000]; + size_t nmax = sizeof (gridline_vertices) / sizeof (GLfloat); + + const double epsilon = 1e-6; + + for (int major = 0; major < 2; ++major) { + + m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, major ? 0.25f : 0.15f); + + size_t index = 0; + double x, y; + double step = (major ? gmajor : gminor); + + x = ceil (l / step) * step; + for ( ; index < nmax && x < r - step * epsilon; x += step) { + if ((fabs (floor (x / gmajor + 0.5) * gmajor - x) < epsilon) == (major != 0)) { + gridline_vertices [index++] = x; + gridline_vertices [index++] = 0.0; + gridline_vertices [index++] = b; + gridline_vertices [index++] = x; + gridline_vertices [index++] = 0.0; + gridline_vertices [index++] = t; + } + } + + y = ceil (b / step) * step; + for ( ; index < nmax && y < t - step * epsilon; y += step) { + if ((fabs (floor (y / gmajor + 0.5) * gmajor - y) < epsilon) == (major != 0)) { + gridline_vertices [index++] = l; + gridline_vertices [index++] = 0.0; + gridline_vertices [index++] = y; + gridline_vertices [index++] = r; + gridline_vertices [index++] = 0.0; + gridline_vertices [index++] = y; + } + } + + glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, gridline_vertices); + + glLineWidth (2.0); + glDrawArrays (GL_LINES, 0, index / 3); + + } + + // base plane + GLfloat plane_vertices[] = { - -1.05, 0.0, -2.05, -1.05, 0.0, 0.05, 1.05, 0.0, 0.05, - -1.05, 0.0, -2.05, 1.05, 0.0, 0.05, 1.05, 0.0, -2.05 + float (l), 0.0f, float (b), float (l), 0.0f, float (t), float (r), 0.0f, float (t), + float (l), 0.0f, float (b), float (r), 0.0f, float (t), float (r), 0.0f, float (b) }; - m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, 0.2f); + m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, 0.1f); glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, plane_vertices); glDrawArrays (GL_TRIANGLES, 0, 6); +#if 0 #if 0 GLfloat gridline_vertices[] = { -1.0, 0.0, -2.0, -1.0, 0.0, 0.0, @@ -743,6 +912,7 @@ D25ViewWidget::paintGL () glLineWidth (2.0); glDrawArrays (GL_LINES, 0, 36); +#endif glDisableVertexAttribArray (positions); @@ -752,7 +922,7 @@ D25ViewWidget::paintGL () void D25ViewWidget::resizeGL (int /*w*/, int /*h*/) { - update_cam_trans (); + refresh (); } } diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h index c467510d8..e89404360 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h @@ -76,6 +76,8 @@ private: bool m_top_view; QVector3D m_displacement; double m_focus_dist; + double m_fov; + QVector3D m_hit_point; QPoint m_start_pos; QVector3D m_start_cam_position; double m_start_cam_azimuth, m_start_cam_elevation; @@ -97,18 +99,21 @@ private: void paintGL (); void resizeGL (int w, int h); - void update_cam_trans (); + void refresh (); + void reset (); void prepare_view (); void render_layout (D25ViewWidget::chunks_type &chunks, const db::Layout &layout, const db::Cell &cell, unsigned int layer, double zstart, double zstop); void render_polygon (D25ViewWidget::chunks_type &chunks, const db::Polygon &poly, double dbu, double zstart, double zstop); void render_wall (D25ViewWidget::chunks_type &chunks, const db::Edge &poly, double dbu, double zstart, double zstop); - QVector3D hit_point_with_scene(const QVector3D &line, const QVector3D &line_dir); + QVector3D hit_point_with_scene(const QVector3D &line_dir); double cam_elevation () const; double cam_azimuth () const; QVector3D cam_position () const; QVector3D cam_direction () const; QMatrix4x4 cam_perspective () const; QMatrix4x4 cam_trans () const; + double cam_dist () const; + double cam_fov () const; }; } From 9c005683014149778d80a90ca2e9d7dcb3b87b9d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 13 Apr 2020 21:34:32 +0200 Subject: [PATCH 015/113] WIP: compass, navigation, bug fixes. --- .../view_25d/lay_plugin/layD25ViewUtils.cc | 2 +- .../view_25d/lay_plugin/layD25ViewWidget.cc | 198 +++++++++++------- 2 files changed, 121 insertions(+), 79 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc index b2fceddba..23cb6c951 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc @@ -66,7 +66,7 @@ cutpoint_line_with_face (const QVector3D &line, const QVector3D &dir, const QVec static bool somewhat_perpendicular (const QVector3D &a, const QVector3D &b) { // returns true if a and b are perpendicular within 30 degree - return fabs (QVector3D::dotProduct (a, b) < 0.5 * a.length () * b.length ()); + return fabs (QVector3D::dotProduct (a, b)) < 0.5 * a.length () * b.length (); } static std::pair plane_or_face (const QVector3D &line, const QVector3D &line_dir, const QVector3D &corner, const QVector3D &u, const QVector3D &v, bool face) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index 0c3437017..d8170443e 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -104,7 +104,7 @@ D25ViewWidget::wheelEvent (QWheelEvent *event) // "Ctrl" is closeup double f = event->angleDelta ().y () * (1.0 / (90 * 8)); - m_displacement += -((f / m_scale_factor) * cam_dist ()) * ray.second; + m_displacement += -((f / m_scale_factor) * std::min (cam_dist (), double ((cam_position () - hp).length ()))) * ray.second; } else { @@ -195,7 +195,8 @@ D25ViewWidget::mousePressEvent (QMouseEvent *event) // by definition the ray goes through the camera position QVector3D hp = hit_point_with_scene (cam_direction ()); - m_focus_dist = std::max (m_focus_dist, double ((m_start_cam_position - hp).length ())); + m_focus_dist = std::max (m_focus_dist, double ((cam_position () - hp).length ())); + m_hit_point = cam_position () + cam_direction () * m_focus_dist; } else if (m_rotating) { @@ -206,9 +207,10 @@ D25ViewWidget::mousePressEvent (QMouseEvent *event) std::pair ray = camera_normal (cam_perspective () * cam_trans (), px, py); // by definition the ray goes through the camera position - m_hit_point = hit_point_with_scene (ray.second); + QVector3D hp = hit_point_with_scene (ray.second); - m_focus_dist = std::max (m_focus_dist, double ((m_start_cam_position - m_hit_point).length ())); + m_focus_dist = std::max (m_focus_dist, double ((cam_position () - hp).length ())); + m_hit_point = cam_position () + ray.second * m_focus_dist; } } @@ -242,11 +244,26 @@ D25ViewWidget::mouseMoveEvent (QMouseEvent *event) } else { + double focus_dist = 2.0; + QPoint d = event->pos () - m_start_pos; - double f = tan ((cam_fov () / 2) / 180.0 * M_PI) * m_focus_dist * 2.0 / double (height ()); + double f = tan ((cam_fov () / 2) / 180.0 * M_PI) * focus_dist * 2.0 / double (height ()); double dx = d.x () * f; double dy = -d.y () * f; + if (! m_top_view) { + + double da = dx / (cam_dist () - focus_dist) * 180.0 / M_PI; + m_cam_azimuth = m_start_cam_azimuth + da; + + double de = dy / (cam_dist () - focus_dist) * 180.0 / M_PI; + m_cam_elevation = m_start_cam_elevation + de; + +printf("@@@ -> dy=%g de=%g focus_dist=%g\n", dy, de, focus_dist); fflush(stdout); + + } + +#if 0 if (m_hit_point.x () * dx < 0 && (dx + 2.0 * m_hit_point.x ()) * m_hit_point.x () > 0) { double da = asin (dx / m_hit_point.x () - 1.0) * 180.0 / M_PI; @@ -254,6 +271,7 @@ D25ViewWidget::mouseMoveEvent (QMouseEvent *event) m_cam_azimuth = m_start_cam_azimuth + da; } +#endif #if 0 // elevation change @@ -740,13 +758,16 @@ D25ViewWidget::initializeGL () void D25ViewWidget::paintGL () { + GLfloat vertices[6000]; + size_t nmax = sizeof (vertices) / sizeof (GLfloat); + const qreal retinaScale = devicePixelRatio (); glViewport (0, 0, width () * retinaScale, height () * retinaScale); glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // @@@ white background: glClearColor (1.0, 1.0, 1.0, 1.0); - QMatrix4x4 scene_trans; + QMatrix4x4 scene_trans, scene_trans_wo_y; // provide the displacement and scaling (in this order!) scene_trans.scale (m_scale_factor); @@ -754,6 +775,9 @@ D25ViewWidget::paintGL () // this way we can use y as z coordinate when drawing scene_trans.scale (1.0, 1.0, -1.0); + scene_trans_wo_y = scene_trans; + scene_trans_wo_y.translate (QVector3D (0.0, -m_displacement.y (), 0.0)); + const int positions = 0; m_shapes_program->bind (); @@ -789,7 +813,83 @@ D25ViewWidget::paintGL () glEnable (GL_DEPTH_TEST); glEnableVertexAttribArray (positions); - m_gridplane_program->setUniformValue ("matrix", cam_perspective () * cam_trans () * scene_trans); + // draw pivot compass + + m_gridplane_program->setUniformValue ("matrix", cam_perspective () * cam_trans ()); + + size_t index = 0; + + double compass_rad = 0.3; + double compass_bars = 0.4; + + vertices[index++] = -compass_bars; + vertices[index++] = 0.0; + vertices[index++] = 0.0; + + vertices[index++] = compass_bars; + vertices[index++] = 0.0; + vertices[index++] = 0.0; + + vertices[index++] = 0.0; + vertices[index++] = 0.0; + vertices[index++] = -compass_bars; + + vertices[index++] = 0.0; + vertices[index++] = 0.0; + vertices[index++] = compass_bars; + + int ncircle = 64; + double x = compass_rad, z = 0.0; + double da = 1.0 / double (ncircle) * M_PI * 2.0; + + for (int i = 0; i < ncircle; ++i) { + + double a = double (i + 1) * da; + double xx = compass_rad * cos (a); + double zz = compass_rad * sin (a); + + vertices[index++] = x; + vertices[index++] = 0.0; + vertices[index++] = z; + + vertices[index++] = xx; + vertices[index++] = 0.0; + vertices[index++] = zz; + + x = xx; + z = zz; + + } + + m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, 0.25f); + + glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, vertices); + + glLineWidth (2.0); + glDrawArrays (GL_LINES, 0, index / 3); + + index = 0; + + // arrow + vertices[index++] = -0.25 * compass_rad; + vertices[index++] = 0.0; + vertices[index++] = 0.6 * compass_rad; + + vertices[index++] = 0.0; + vertices[index++] = 0.0; + vertices[index++] = -0.8 * compass_rad; + + vertices[index++] = 0.25 * compass_rad; + vertices[index++] = 0.0; + vertices[index++] = 0.6 * compass_rad; + + glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, vertices); + + glDrawArrays (GL_TRIANGLES, 0, index / 3); + + // draw base plane + + m_gridplane_program->setUniformValue ("matrix", cam_perspective () * cam_trans () * scene_trans_wo_y); std::pair gg = find_grid (std::max (m_bbox.width (), m_bbox.height ())); double gminor = gg.second, gmajor = gg.first; @@ -804,9 +904,6 @@ D25ViewWidget::paintGL () // major and minor grid lines - GLfloat gridline_vertices[6000]; - size_t nmax = sizeof (gridline_vertices) / sizeof (GLfloat); - const double epsilon = 1e-6; for (int major = 0; major < 2; ++major) { @@ -820,36 +917,34 @@ D25ViewWidget::paintGL () x = ceil (l / step) * step; for ( ; index < nmax && x < r - step * epsilon; x += step) { if ((fabs (floor (x / gmajor + 0.5) * gmajor - x) < epsilon) == (major != 0)) { - gridline_vertices [index++] = x; - gridline_vertices [index++] = 0.0; - gridline_vertices [index++] = b; - gridline_vertices [index++] = x; - gridline_vertices [index++] = 0.0; - gridline_vertices [index++] = t; + vertices [index++] = x; + vertices [index++] = 0.0; + vertices [index++] = b; + vertices [index++] = x; + vertices [index++] = 0.0; + vertices [index++] = t; } } y = ceil (b / step) * step; for ( ; index < nmax && y < t - step * epsilon; y += step) { if ((fabs (floor (y / gmajor + 0.5) * gmajor - y) < epsilon) == (major != 0)) { - gridline_vertices [index++] = l; - gridline_vertices [index++] = 0.0; - gridline_vertices [index++] = y; - gridline_vertices [index++] = r; - gridline_vertices [index++] = 0.0; - gridline_vertices [index++] = y; + vertices [index++] = l; + vertices [index++] = 0.0; + vertices [index++] = y; + vertices [index++] = r; + vertices [index++] = 0.0; + vertices [index++] = y; } } - glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, gridline_vertices); + glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, vertices); glLineWidth (2.0); glDrawArrays (GL_LINES, 0, index / 3); } - // base plane - GLfloat plane_vertices[] = { float (l), 0.0f, float (b), float (l), 0.0f, float (t), float (r), 0.0f, float (t), float (l), 0.0f, float (b), float (r), 0.0f, float (t), float (r), 0.0f, float (b) @@ -861,59 +956,6 @@ D25ViewWidget::paintGL () glDrawArrays (GL_TRIANGLES, 0, 6); -#if 0 -#if 0 - GLfloat gridline_vertices[] = { - -1.0, 0.0, -2.0, -1.0, 0.0, 0.0, - -0.75, 0.0, -2.0, -0.75, 0.0, 0.0, - -0.5, 0.0, -2.0, -0.5, 0.0, 0.0, - -0.25, 0.0, -2.0, -0.25, 0.0, 0.0, - 0.0, 0.0, -2.0, 0.0, 0.0, 0.0, - 0.25, 0.0, -2.0, 0.25, 0.0, 0.0, - 0.5, 0.0, -2.0, 0.5, 0.0, 0.0, - 0.75, 0.0, -2.0, 0.75, 0.0, 0.0, - 1.0, 0.0, -2.0, 1.0, 0.0, 0.0, - 1.0, 0.0, -2.0, -1.0, 0.0, -2.0, - 1.0, 0.0, -1.75, -1.0, 0.0, -1.75, - 1.0, 0.0, -1.5 , -1.0, 0.0, -1.5, - 1.0, 0.0, -1.25, -1.0, 0.0, -1.25, - 1.0, 0.0, -1.0, -1.0, 0.0, -1.0, - 1.0, 0.0, -0.75, -1.0, 0.0, -0.75, - 1.0, 0.0, -0.5 , -1.0, 0.0, -0.5, - 1.0, 0.0, -0.25, -1.0, 0.0, -0.25, - 1.0, 0.0, 0.0, -1.0, 0.0, 0.0 - }; -#else - GLfloat gridline_vertices[] = { - -1.0, -1.0, 0.0, -1.0, 1.0, 0.0, - -0.75, -1.0, 0.0, -0.75, 1.0, 0.0, - -0.5, -1.0, 0.0, -0.5, 1.0, 0.0, - -0.25, -1.0, 0.0, -0.25, 1.0, 0.0, - 0.0, -1.0, 0.0, 0.0, 1.0, 0.0, - 0.25, -1.0, 0.0, 0.25, 1.0, 0.0, - 0.5, -1.0, 0.0, 0.5, 1.0, 0.0, - 0.75, -1.0, 0.0, 0.75, 1.0, 0.0, - 1.0, -1.0, 0.0, 1.0, 1.0, 0.0, - 1.0, -1.0, 0.0, -1.0, -1.0, 0.0, - 1.0, -0.75, 0.0, -1.0, -0.75, 0.0, - 1.0, -0.5, 0.0, -1.0, -0.5, 0.0, - 1.0, -0.25, 0.0, -1.0, -0.25, 0.0, - 1.0, 0.0, 0.0, -1.0, 0.0, 0.0, - 1.0, 0.25, 0.0, -1.0, 0.25, 0.0, - 1.0, 0.5, 0.0, -1.0, 0.5, 0.0, - 1.0, 0.75, 0.0, -1.0, 0.75, 0.0, - 1.0, 1.0, 0.0, -1.0, 1.0, 0.0 - }; -#endif - - m_shapes_program->setUniformValue ("vertexColor", 1.0, 1.0, 1.0, 0.2f); - - glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, gridline_vertices); - - glLineWidth (2.0); - glDrawArrays (GL_LINES, 0, 36); -#endif - glDisableVertexAttribArray (positions); m_shapes_program->release (); From 2ec712b1040bf1f222b360b778d39af33cac957c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 13 Apr 2020 22:18:52 +0200 Subject: [PATCH 016/113] Implementation starts to make sense. TODO: major refactoring, a lot of utility functions like fit, ... --- .../view_25d/lay_plugin/layD25ViewWidget.cc | 78 ++++++++----------- 1 file changed, 34 insertions(+), 44 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index d8170443e..fa6edca8f 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -75,7 +75,7 @@ D25ViewWidget::reset () { m_scale_factor = 1.0; m_focus_dist = 0.0; - m_fov = 60.0; + m_fov = 90.0; m_cam_azimuth = m_cam_elevation = 0.0; m_top_view = false; m_dragging = m_rotating = false; @@ -138,6 +138,8 @@ D25ViewWidget::keyPressEvent (QKeyEvent *event) { if (event->key () == Qt::Key_Shift) { m_top_view = true; + m_dragging = false; + m_rotating = false; refresh (); } } @@ -147,6 +149,8 @@ D25ViewWidget::keyReleaseEvent (QKeyEvent *event) { if (event->key () == Qt::Key_Shift) { m_top_view = false; + m_dragging = false; + m_rotating = false; refresh (); } } @@ -195,7 +199,7 @@ D25ViewWidget::mousePressEvent (QMouseEvent *event) // by definition the ray goes through the camera position QVector3D hp = hit_point_with_scene (cam_direction ()); - m_focus_dist = std::max (m_focus_dist, double ((cam_position () - hp).length ())); + m_focus_dist = (cam_position () - hp).length (); m_hit_point = cam_position () + cam_direction () * m_focus_dist; } else if (m_rotating) { @@ -219,6 +223,7 @@ void D25ViewWidget::mouseReleaseEvent (QMouseEvent * /*event*/) { m_dragging = false; + m_rotating = false; } void @@ -244,46 +249,39 @@ D25ViewWidget::mouseMoveEvent (QMouseEvent *event) } else { - double focus_dist = 2.0; - - QPoint d = event->pos () - m_start_pos; - double f = tan ((cam_fov () / 2) / 180.0 * M_PI) * focus_dist * 2.0 / double (height ()); - double dx = d.x () * f; - double dy = -d.y () * f; - if (! m_top_view) { + // fixed focus point for rotation + double focus_dist = 2.0; + + QPoint d = event->pos () - m_start_pos; + double f = tan ((cam_fov () / 2) / 180.0 * M_PI) * focus_dist * 2.0 / double (height ()); + double dx = d.x () * f; + double dy = -d.y () * f; + double da = dx / (cam_dist () - focus_dist) * 180.0 / M_PI; m_cam_azimuth = m_start_cam_azimuth + da; double de = dy / (cam_dist () - focus_dist) * 180.0 / M_PI; m_cam_elevation = m_start_cam_elevation + de; -printf("@@@ -> dy=%g de=%g focus_dist=%g\n", dy, de, focus_dist); fflush(stdout); + } else { + + // simple change of azimuth only - with center in the middle + + QPoint m = event->pos () - m_start_pos; + QVector3D p (m_start_pos.x () - width () / 2, -m_start_pos.y () + height () / 2, 0); + QVector3D d (m.x (), -m.y (), 0); + + double cp = QVector3D::crossProduct (p, p + d).z () / p.length () / (p + d).length (); + cp = std::max (-1.0, std::min (1.0, cp)); + double da = asin (cp) * 180.0 / M_PI; + + m_cam_azimuth += da; + m_start_pos = event->pos (); } -#if 0 - if (m_hit_point.x () * dx < 0 && (dx + 2.0 * m_hit_point.x ()) * m_hit_point.x () > 0) { - - double da = asin (dx / m_hit_point.x () - 1.0) * 180.0 / M_PI; -// @@@ printf("@@@ hp=%g,%g,%g drag=%g,%g -> da=%g\n", m_hit_point.x(), m_hit_point.y(), m_hit_point.z(), drag.x(), drag.z(), da); fflush(stdout); - m_cam_azimuth = m_start_cam_azimuth + da; - - } -#endif - -#if 0 - // elevation change - if (! m_top_view) { - - dp = m_hit_point + QVector3D (0.0, float (drag.y ()), 0.0); - double de = QVector3D::crossProduct (dp, m_hit_point).length () * (drag.y () > 0 ? 1.0 : -1.0) / (dp.length () * m_hit_point.length ()) * 180.0 / M_PI; - m_cam_elevation = m_start_cam_elevation + de; - - } -#endif - } refresh (); @@ -715,19 +713,6 @@ D25ViewWidget::initializeGL () "layout (location = 0) in vec4 posAttr;\n" "uniform mat4 matrix;\n" "\n" - "vec4 color_by_z(vec4 face_color, highp float z) {\n" - " vec4 mist_color = vec4(1.0, 1.0, 1.0, 1.0);\n" - " highp float d = -2.309;\n" // tan(camera_fov/2)*camera_dist - " highp float dd = 0.0;\n" - " highp float f = 1.0;\n" - " if (z < d - dd) {\n" - " f = 0.0;\n" - " } else if (z < d + dd) {\n" - " f = (z - (d - dd)) / (2.0 * dd);\n" - " }\n" - " return (1.0 - f) * mist_color + f * face_color;\n" - "};\n" - "\n" "void main() {\n" " gl_Position = matrix * posAttr;\n" "}\n"; @@ -945,6 +930,11 @@ D25ViewWidget::paintGL () } + l = m_bbox.left (); + r = m_bbox.right (); + b = m_bbox.bottom (); + t = m_bbox.top (); + GLfloat plane_vertices[] = { float (l), 0.0f, float (b), float (l), 0.0f, float (t), float (r), 0.0f, float (t), float (l), 0.0f, float (b), float (r), 0.0f, float (t), float (r), 0.0f, float (b) From f9aa89a0b3e8462be7dd1d4fdca39ea2e289ef54 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 16 Apr 2020 23:14:22 +0200 Subject: [PATCH 017/113] WIP: bugfixes, refactoring. --- src/lay/lay/layResources.qrc | 6 + .../tools/view_25d/lay_plugin/D25View.ui | 253 ++++++- .../tools/view_25d/lay_plugin/layD25Camera.cc | 113 ++++ .../tools/view_25d/lay_plugin/layD25Camera.h | 142 ++++ .../view_25d/lay_plugin/layD25MemChunks.cc | 1 + .../view_25d/lay_plugin/layD25MemChunks.h | 44 ++ .../tools/view_25d/lay_plugin/layD25View.cc | 43 ++ .../tools/view_25d/lay_plugin/layD25View.h | 3 + .../view_25d/lay_plugin/layD25ViewUtils.cc | 2 +- .../view_25d/lay_plugin/layD25ViewWidget.cc | 635 ++++++++---------- .../view_25d/lay_plugin/layD25ViewWidget.h | 75 ++- .../tools/view_25d/lay_plugin/lay_plugin.pro | 6 +- .../view_25d/unit_tests/layD25CameraTests.cc | 102 +++ .../tools/view_25d/unit_tests/unit_tests.pro | 3 +- 14 files changed, 1050 insertions(+), 378 deletions(-) create mode 100644 src/plugins/tools/view_25d/lay_plugin/layD25Camera.cc create mode 100644 src/plugins/tools/view_25d/lay_plugin/layD25Camera.h create mode 100644 src/plugins/tools/view_25d/unit_tests/layD25CameraTests.cc diff --git a/src/lay/lay/layResources.qrc b/src/lay/lay/layResources.qrc index abaeec830..195eb763e 100644 --- a/src/lay/lay/layResources.qrc +++ b/src/lay/lay/layResources.qrc @@ -127,6 +127,12 @@ images/folder_12.png images/file_12.png images/empty_12.png + images/fit_front.png + images/fit_back.png + images/fit_left.png + images/fit_right.png + images/fit_top.png + images/fit_bottom.png syntax/ruby.xml diff --git a/src/plugins/tools/view_25d/lay_plugin/D25View.ui b/src/plugins/tools/view_25d/lay_plugin/D25View.ui index 3e8f0fc83..efdbd6365 100644 --- a/src/plugins/tools/view_25d/lay_plugin/D25View.ui +++ b/src/plugins/tools/view_25d/lay_plugin/D25View.ui @@ -6,8 +6,8 @@ 0 0 - 576 - 649 + 854 + 665 @@ -29,17 +29,249 @@ 9 + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 60 + 0 + + + + % + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + ... + + + + :/fit_left.png:/fit_left.png + + + + 24 + 24 + + + + true + + + + + + + ... + + + + :/fit_front.png:/fit_front.png + + + + 24 + 24 + + + + true + + + + + + + ... + + + + :/fit_right.png:/fit_right.png + + + + 24 + 24 + + + + true + + + + + + + ... + + + + :/fit_back.png:/fit_back.png + + + + 24 + 24 + + + + true + + + + + + + ... + + + + :/fit_top.png:/fit_top.png + + + + 24 + 24 + + + + true + + + + + + + ... + + + + :/fit_bottom.png:/fit_bottom.png + + + + 24 + 24 + + + + true + + + + + + - - - Qt::Horizontal + + + + 0 + 0 + - - QDialogButtonBox::Close + + QFrame::NoFrame + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Press and hold SHIFT for top view + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + @@ -51,10 +283,9 @@
layD25ViewWidget.h
- - buttonBox - - + + + buttonBox diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25Camera.cc b/src/plugins/tools/view_25d/lay_plugin/layD25Camera.cc new file mode 100644 index 000000000..3d8fb12ef --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25Camera.cc @@ -0,0 +1,113 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "layD25Camera.h" + +#include + +#include "math.h" + +namespace lay +{ + +D25Camera::D25Camera () +{ + init (); +} + +D25Camera::~D25Camera () +{ + // .. nothing yet .. +} + +void +D25Camera::init () +{ + m_fov = 90.0; + m_cam_azimuth = m_cam_elevation = 0.0; + m_top_view = false; +} + +void +D25Camera::camera_reset () +{ + init (); + camera_changed (); +} + +double +D25Camera::cam_fov () const +{ + return m_fov; // @@@ +} + +double +D25Camera::cam_dist () const +{ + return 4.0; // @@@ +} + +QVector3D +D25Camera::cam_direction () const +{ + return cam_trans ().inverted ().map (QVector3D (0, 0, -1)); +} + +QVector3D +D25Camera::cam_position () const +{ + return cam_direction () * -cam_dist (); +} + +double +D25Camera::cam_azimuth () const +{ + return m_cam_azimuth; +} + +double +D25Camera::cam_elevation () const +{ + return m_top_view ? -90.0 : m_cam_elevation; +} + +QMatrix4x4 +D25Camera::cam_perspective () const +{ + QMatrix4x4 t; + t.perspective (cam_fov (), aspect_ratio (), 0.1f, 10000.0f); + t.translate (QVector3D (0.0, 0.0, -cam_dist ())); + return t; +} + +QMatrix4x4 +D25Camera::cam_trans () const +{ + QMatrix4x4 t; + t.rotate (-cam_elevation (), 1.0, 0.0, 0.0); + t.rotate (cam_azimuth (), 0.0, 1.0, 0.0); + return t; +} + +} + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25Camera.h b/src/plugins/tools/view_25d/lay_plugin/layD25Camera.h new file mode 100644 index 000000000..22dd780e4 --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25Camera.h @@ -0,0 +1,142 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_layD25Camera +#define HDR_layD25Camera + +#include "layPluginCommon.h" + +#include +#include + +namespace lay +{ + +class LAY_PLUGIN_PUBLIC D25Camera +{ +public: + D25Camera (); + virtual ~D25Camera (); + + /** + * @brief Gets the position of the camera objective in the scene coordinate system + */ + QVector3D cam_position () const; + + /** + * @brief Gets the direction the camera looks into in the scene coordinate system + */ + QVector3D cam_direction () const; + + /** + * @brief Gets the perspective part of the transformation applied transform scene coordinates into the image plane + * The full transformation for scene to image plane is cam_perspective * cam_trans. + */ + QMatrix4x4 cam_perspective () const; + + /** + * @brief Gets the azimuth/elevation part of the transformation applied transform scene coordinates into the image plane + * The full transformation for scene to image plane is cam_perspective * cam_trans. + */ + QMatrix4x4 cam_trans () const; + + /** + * @brief Gets the distance of the objective in scene coordinates + */ + double cam_dist () const; + + /** + * @brief Gets the field of view of the camera + * The field of view is the objective opening angle. + */ + double cam_fov () const; + + /** + * @brief Gets a flag indicating whether top view is enabled + * In "top view" mode, the elevation is fixed to -90 degree. + */ + bool top_view () const + { + return m_top_view; + } + + /** + * @brief Sets a flag indicating whether top view is enabled + */ + void set_top_view (bool f) + { + m_top_view = f; + camera_changed (); + } + + /** + * @brief Gets the elevation angle + * A negative angle means the camera looks down, a positive angle means it looks up. + */ + double cam_elevation () const; + + /** + * @brief Sets the elevation angle + */ + void set_cam_elevation (double e) + { + m_cam_elevation = e; + camera_changed (); + } + + /** + * @brief Gets the azimuth angle + * ... + */ + double cam_azimuth () const; + + /** + * @brief Sets the azimuth angle + */ + void set_cam_azimuth (double a) + { + m_cam_azimuth = a; + camera_changed (); + } + + /** + * @brief Resets the camera's orientation + */ + void camera_reset (); + +protected: + virtual void camera_changed () { } + virtual double aspect_ratio () const { return 1.0; } + +private: + double m_cam_azimuth, m_cam_elevation; + bool m_top_view; + QVector3D m_displacement; + double m_fov; + + void init (); +}; + +} + +#endif + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.cc b/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.cc index c80a2d5a7..dfe8fb146 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.cc @@ -21,3 +21,4 @@ */ #include "layD25MemChunks.h" + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.h b/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.h index 034aa5377..945d1338d 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.h @@ -24,6 +24,7 @@ #define HDR_layD25MemChunks #include +#include #include "tlObject.h" #include // for memcpy @@ -31,6 +32,19 @@ namespace lay { +template +struct gl_type2enum +{ + GLenum operator() () const; +}; + +template <> +struct gl_type2enum +{ + GLenum operator() () const { return GL_FLOAT; } +}; + + /** * @brief Provides a semi-contiguous array of objects * @@ -201,6 +215,25 @@ public: mp_last_chunk->m_objects [mp_last_chunk->m_len++] = element; } + /** + * @brief Adds two elements + */ + void add (const Obj &e1, const Obj &e2) + { + add (e1); + add (e2); + } + + /** + * @brief Adds three elements + */ + void add (const Obj &e1, const Obj &e2, const Obj &e3) + { + add (e1); + add (e2); + add (e3); + } + /** * @brief begin iterator */ @@ -211,6 +244,17 @@ public: */ iterator end () const { return iterator (); } + /** + * @brief Draw to the given context + */ + void draw_to (QOpenGLFunctions *ctx, GLuint location, GLenum mode) const + { + for (iterator c = begin (); c != end (); ++c) { + ctx->glVertexAttribPointer (location, 3, gl_type2enum () (), GL_FALSE, 0, c->front ()); + ctx->glDrawArrays (mode, 0, c->size () / 3); + } + } + private: chunk *mp_chunks; chunk *mp_last_chunk; diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc index fdb5230d9..aa6c587a4 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -31,6 +31,8 @@ namespace lay { +const double initial_elevation = 3.0; + D25View::D25View (QWidget *parent) : QDialog (parent) { @@ -41,6 +43,13 @@ D25View::D25View (QWidget *parent) mp_ui->d25_view->setFocusPolicy (Qt::StrongFocus); mp_ui->d25_view->setFocus (); // @@@ + + connect (mp_ui->fit_back, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); + connect (mp_ui->fit_front, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); + connect (mp_ui->fit_left, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); + connect (mp_ui->fit_right, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); + connect (mp_ui->fit_top, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); + connect (mp_ui->fit_bottom, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); } D25View::~D25View () @@ -55,6 +64,10 @@ D25View::exec_dialog (lay::LayoutView *view) mp_view.reset (view); mp_ui->d25_view->attach_view (view); + mp_ui->d25_view->reset (); + mp_ui->d25_view->set_cam_azimuth (0.0); + mp_ui->d25_view->set_cam_elevation (initial_elevation); + int ret = QDialog::exec (); mp_ui->d25_view->attach_view (0); @@ -62,6 +75,36 @@ D25View::exec_dialog (lay::LayoutView *view) return ret; } +void +D25View::fit_button_clicked () +{ + double azimuth = mp_ui->d25_view->cam_azimuth (); + double elevation = mp_ui->d25_view->cam_elevation (); + + if (sender () == mp_ui->fit_back) { + azimuth = -180.0; + elevation = -initial_elevation; + } else if (sender () == mp_ui->fit_front) { + azimuth = 0.0; + elevation = -initial_elevation; + } else if (sender () == mp_ui->fit_left) { + azimuth = 90.0; + elevation = -initial_elevation; + } else if (sender () == mp_ui->fit_right) { + azimuth = -90.0; + elevation = -initial_elevation; + } else if (sender () == mp_ui->fit_top) { + elevation = -90; + } else if (sender () == mp_ui->fit_bottom) { + elevation = 90; + } + + mp_ui->d25_view->set_cam_azimuth (azimuth); + mp_ui->d25_view->set_cam_elevation (elevation); + + mp_ui->d25_view->fit (); +} + void D25View::accept () { diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.h b/src/plugins/tools/view_25d/lay_plugin/layD25View.h index 78526bd36..207ca7751 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.h @@ -54,6 +54,9 @@ public: protected: void accept (); +private slots: + void fit_button_clicked (); + private: Ui::D25View *mp_ui; tl::weak_ptr mp_view; diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc index 23cb6c951..3fb6e8dca 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc @@ -65,7 +65,7 @@ cutpoint_line_with_face (const QVector3D &line, const QVector3D &dir, const QVec static bool somewhat_perpendicular (const QVector3D &a, const QVector3D &b) { - // returns true if a and b are perpendicular within 30 degree + // returns true if a and b are perpendicular within +/-30 degree return fabs (QVector3D::dotProduct (a, b)) < 0.5 * a.length () * b.length (); } diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index fa6edca8f..1a077e786 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -43,6 +43,146 @@ namespace lay // ------------------------------------------------------------------------------ +D25InteractionMode::D25InteractionMode (D25ViewWidget *view) + : mp_view (view) +{ + // .. nothing yet .. +} + +D25InteractionMode::~D25InteractionMode () +{ + // .. nothing yet .. +} + + +// ------------------------------------------------------------------------------ + +class D25PanInteractionMode + : public D25InteractionMode +{ +public: + D25PanInteractionMode (D25ViewWidget *widget, const QPoint &pos) + : D25InteractionMode (widget), m_start_pos (pos) + { + m_start_displacement = widget->displacement (); + + double px = (pos.x () - widget->width () / 2) * 2.0 / widget->width (); + double py = -(pos.y () - widget->height () / 2) * 2.0 / widget->height (); + + // compute vector of line of sight + std::pair ray = camera_normal (view ()->cam_perspective () * view ()->cam_trans (), px, py); + + // by definition the ray goes through the camera position + QVector3D hp = widget->hit_point_with_scene (ray.second); + + m_focus_dist = (widget->cam_position () - hp).length (); + } + + virtual ~D25PanInteractionMode () + { + // .. nothing yet .. + } + + virtual void mouse_move (QMouseEvent *event) + { + QPoint d = event->pos () - m_start_pos; + double f = tan ((view ()->cam_fov () / 2) / 180.0 * M_PI) * m_focus_dist * 2.0 / double (view ()->height ()); + double dx = d.x () * f; + double dy = -d.y () * f; + + QVector3D xv (cos (view ()->cam_azimuth () * M_PI / 180.0), 0.0, sin (view ()->cam_azimuth () * M_PI / 180.0)); + double re = sin (view ()->cam_elevation () * M_PI / 180.0); + QVector3D yv (-re * xv.z (), cos (view ()->cam_elevation () * M_PI / 180.0), re * xv.x ()); + QVector3D drag = xv * dx + yv * dy; + + view ()->set_displacement (m_start_displacement + drag / view ()->scale_factor ()); + } + +private: + QPoint m_start_pos; + double m_focus_dist; + QVector3D m_start_displacement; +}; + +// ------------------------------------------------------------------------------ + +class D25Rotate2DInteractionMode + : public D25InteractionMode +{ +public: + D25Rotate2DInteractionMode (D25ViewWidget *widget, const QPoint &pos) + : D25InteractionMode (widget), m_start_pos (pos) + { + m_start_cam_azimuth = widget->cam_azimuth (); + m_start_cam_elevation = widget->cam_elevation (); + } + + virtual ~D25Rotate2DInteractionMode () + { + // .. nothing yet .. + } + + virtual void mouse_move (QMouseEvent *event) + { + // fixed focus point for rotation + double focus_dist = 2.0; + + QPoint d = event->pos () - m_start_pos; + double f = tan ((view ()->cam_fov () / 2) / 180.0 * M_PI) * focus_dist * 2.0 / double (view ()->height ()); + double dx = d.x () * f; + double dy = -d.y () * f; + + double da = dx / (view ()->cam_dist () - focus_dist) * 180.0 / M_PI; + view ()->set_cam_azimuth (m_start_cam_azimuth + da); + + double de = dy / (view ()->cam_dist () - focus_dist) * 180.0 / M_PI; + view ()->set_cam_elevation (m_start_cam_elevation + de); + } + +private: + QPoint m_start_pos; + double m_start_cam_azimuth, m_start_cam_elevation; +}; + +// ------------------------------------------------------------------------------ + +class D25RotateAzimuthInteractionMode + : public D25InteractionMode +{ +public: + D25RotateAzimuthInteractionMode (D25ViewWidget *widget, const QPoint &pos) + : D25InteractionMode (widget), m_start_pos (pos) + { + // .. nothing yet .. + } + + virtual ~D25RotateAzimuthInteractionMode () + { + // .. nothing yet .. + } + + virtual void mouse_move (QMouseEvent *event) + { + // simple change of azimuth only - with center in the middle + + QPoint m = event->pos () - m_start_pos; + QVector3D p (m_start_pos.x () - view ()->width () / 2, -m_start_pos.y () + view ()->height () / 2, 0); + QVector3D d (m.x (), -m.y (), 0); + + double cp = QVector3D::crossProduct (p, p + d).z () / p.length () / (p + d).length (); + cp = std::max (-1.0, std::min (1.0, cp)); + double da = asin (cp) * 180.0 / M_PI; + + view ()->set_cam_azimuth (view ()->cam_azimuth () + da); + m_start_pos = event->pos (); + } + +private: + QPoint m_start_pos; +}; + +// ------------------------------------------------------------------------------ + D25ViewWidget::D25ViewWidget (QWidget *parent) : QOpenGLWidget (parent), m_shapes_program (0) @@ -74,13 +214,9 @@ void D25ViewWidget::reset () { m_scale_factor = 1.0; - m_focus_dist = 0.0; - m_fov = 90.0; - m_cam_azimuth = m_cam_elevation = 0.0; - m_top_view = false; - m_dragging = m_rotating = false; + mp_mode.reset (0); - refresh (); + camera_reset (); } void @@ -93,40 +229,55 @@ D25ViewWidget::wheelEvent (QWheelEvent *event) double px = (event->pos ().x () - width () / 2) * 2.0 / width (); double py = -(event->pos ().y () - height () / 2) * 2.0 / height (); - // compute vector of line of sight - std::pair ray = camera_normal (cam_perspective () * cam_trans (), px, py); + if (top_view ()) { - // by definition the ray goes through the camera position - QVector3D hp = hit_point_with_scene (ray.second); + // Plain zoom - if (event->modifiers () & Qt::ControlModifier) { - - // "Ctrl" is closeup - - double f = event->angleDelta ().y () * (1.0 / (90 * 8)); - m_displacement += -((f / m_scale_factor) * std::min (cam_dist (), double ((cam_position () - hp).length ()))) * ray.second; - - } else { - - // No shift is zoom + QVector3D hp (px, py, 0.0); double f = exp (event->angleDelta ().y () * (1.0 / (90 * 8))); - QVector3D initial_displacement = m_displacement; - QVector3D displacement = m_displacement; - m_scale_factor *= f; - displacement += hp * (1.0 - f) / m_scale_factor; + m_displacement += hp * (1.0 - f) / m_scale_factor; - // normalize the scene translation so the scene does not "flee" + } else { - QMatrix4x4 ct = cam_trans (); - initial_displacement = ct.map (initial_displacement); - displacement = ct.map (displacement); + // compute vector of line of sight + std::pair ray = camera_normal (cam_perspective () * cam_trans (), px, py); - lay::normalize_scene_trans (cam_perspective (), displacement, m_scale_factor, initial_displacement.z ()); + // by definition the ray goes through the camera position + QVector3D hp = hit_point_with_scene (ray.second); - m_displacement = ct.inverted ().map (displacement); + if (event->modifiers () & Qt::ControlModifier) { + + // "Ctrl" is closeup + + double f = event->angleDelta ().y () * (1.0 / (90 * 8)); + m_displacement += -((f / m_scale_factor) * std::min (cam_dist (), double ((cam_position () - hp).length ()))) * ray.second; + + } else { + + // No shift is zoom + + double f = exp (event->angleDelta ().y () * (1.0 / (90 * 8))); + + QVector3D initial_displacement = m_displacement; + QVector3D displacement = m_displacement; + + m_scale_factor *= f; + displacement += hp * (1.0 - f) / m_scale_factor; + + // normalize the scene translation so the scene does not "flee" + + QMatrix4x4 ct = cam_trans (); + initial_displacement = ct.map (initial_displacement); + displacement = ct.map (displacement); + + lay::normalize_scene_trans (cam_perspective (), displacement, m_scale_factor, initial_displacement.z ()); + + m_displacement = ct.inverted ().map (displacement); + + } } @@ -137,10 +288,8 @@ void D25ViewWidget::keyPressEvent (QKeyEvent *event) { if (event->key () == Qt::Key_Shift) { - m_top_view = true; - m_dragging = false; - m_rotating = false; - refresh (); + mp_mode.reset (0); + set_top_view (true); } } @@ -148,10 +297,8 @@ void D25ViewWidget::keyReleaseEvent (QKeyEvent *event) { if (event->key () == Qt::Key_Shift) { - m_top_view = false; - m_dragging = false; - m_rotating = false; - refresh (); + mp_mode.reset (0); + set_top_view (false); } } @@ -178,181 +325,87 @@ D25ViewWidget::hit_point_with_scene (const QVector3D &line_dir) void D25ViewWidget::mousePressEvent (QMouseEvent *event) { - m_dragging = m_rotating = false; + mp_mode.reset (0); + if (event->button () == Qt::MidButton) { - m_dragging = true; + mp_mode.reset (new D25PanInteractionMode (this, event->pos ())); } else if (event->button () == Qt::LeftButton) { - m_rotating = true; - } - - m_start_pos = event->pos (); - m_start_cam_position = cam_position (); - m_start_cam_azimuth = cam_azimuth (); - m_start_cam_elevation = cam_elevation (); - m_start_displacement = m_displacement; - - m_focus_dist = 2.0; - m_hit_point = QVector3D (); - - if (m_dragging) { - - // by definition the ray goes through the camera position - QVector3D hp = hit_point_with_scene (cam_direction ()); - - m_focus_dist = (cam_position () - hp).length (); - m_hit_point = cam_position () + cam_direction () * m_focus_dist; - - } else if (m_rotating) { - - double px = (event->pos ().x () - width () / 2) * 2.0 / width (); - double py = -(event->pos ().y () - height () / 2) * 2.0 / height (); - - // compute vector of line of sight - std::pair ray = camera_normal (cam_perspective () * cam_trans (), px, py); - - // by definition the ray goes through the camera position - QVector3D hp = hit_point_with_scene (ray.second); - - m_focus_dist = std::max (m_focus_dist, double ((cam_position () - hp).length ())); - m_hit_point = cam_position () + ray.second * m_focus_dist; - + if (! top_view ()) { + mp_mode.reset (new D25Rotate2DInteractionMode (this, event->pos ())); + } else { + mp_mode.reset (new D25RotateAzimuthInteractionMode (this, event->pos ())); + } } } void D25ViewWidget::mouseReleaseEvent (QMouseEvent * /*event*/) { - m_dragging = false; - m_rotating = false; + mp_mode.reset (0); } void D25ViewWidget::mouseMoveEvent (QMouseEvent *event) { - if (! m_dragging && ! m_rotating) { - return; + if (mp_mode.get ()) { + mp_mode->mouse_move (event); } +} - if (m_dragging) { +inline double square (double x) { return x * x; } - QPoint d = event->pos () - m_start_pos; - double f = tan ((cam_fov () / 2) / 180.0 * M_PI) * m_focus_dist * 2.0 / double (height ()); - double dx = d.x () * f; - double dy = -d.y () * f; +void +D25ViewWidget::fit () +{ + // we pick a scale factor to adjust the z dimension roughly to 1/4 of the screen height at a focus distance of 2 + double norm_height = 0.5; + double in_focus = 2.0; + m_scale_factor = (tan (cam_fov () * M_PI / 180.0 / 2.0) * 2.0) * in_focus * norm_height / std::max (0.001, (m_zmax - m_zmin)); - QVector3D xv (cos (m_start_cam_azimuth * M_PI / 180.0), 0.0, sin (m_start_cam_azimuth * M_PI / 180.0)); - double re = sin (m_start_cam_elevation * M_PI / 180.0); - QVector3D yv (-re * xv.z (), cos (m_start_cam_elevation * M_PI / 180.0), re * xv.x ()); - QVector3D drag = xv * dx + yv * dy; + QVector3D dim = QVector3D (m_bbox.width (), m_zmax - m_zmin, m_bbox.height ()) * m_scale_factor; + QVector3D bll = QVector3D (m_bbox.left (), m_zmin, -(m_bbox.bottom () + m_bbox.height ())); - m_displacement = m_start_displacement + drag / m_scale_factor; + // now we pick a displacement which moves the center to y = 0 and x, y in a way that the dimension covers the field of + // view and is centered. + // (we use the elliptic approximation which works exactly for azimuth angles which are a multiple of 90 degree) - } else { + double dh = sqrt (square (cos (cam_azimuth () * M_PI / 180.0) * dim.x ()) + square (sin (cam_azimuth () * M_PI / 180.0) * dim.z ())); + double dv = sqrt (square (cos (cam_azimuth () * M_PI / 180.0) * dim.z ()) + square (sin (cam_azimuth () * M_PI / 180.0) * dim.x ())); - if (! m_top_view) { + double d = std::max (dh, fabs (sin (cam_elevation ()) * dv)); - // fixed focus point for rotation - double focus_dist = 2.0; + QVector3D new_center (0.0, 0.0, -dv / 2.0 + std::max (0.0, -d / (2.0 * tan (cam_fov () * M_PI / 180.0 / 2.0)) + cam_dist ())); + QVector3D new_center_in_scene = cam_trans ().inverted ().map (new_center); - QPoint d = event->pos () - m_start_pos; - double f = tan ((cam_fov () / 2) / 180.0 * M_PI) * focus_dist * 2.0 / double (height ()); - double dx = d.x () * f; - double dy = -d.y () * f; + new_center_in_scene.setY (0.0); - double da = dx / (cam_dist () - focus_dist) * 180.0 / M_PI; - m_cam_azimuth = m_start_cam_azimuth + da; - - double de = dy / (cam_dist () - focus_dist) * 180.0 / M_PI; - m_cam_elevation = m_start_cam_elevation + de; - - } else { - - // simple change of azimuth only - with center in the middle - - QPoint m = event->pos () - m_start_pos; - QVector3D p (m_start_pos.x () - width () / 2, -m_start_pos.y () + height () / 2, 0); - QVector3D d (m.x (), -m.y (), 0); - - double cp = QVector3D::crossProduct (p, p + d).z () / p.length () / (p + d).length (); - cp = std::max (-1.0, std::min (1.0, cp)); - double da = asin (cp) * 180.0 / M_PI; - - m_cam_azimuth += da; - m_start_pos = event->pos (); - - } - - } + m_displacement = (new_center_in_scene - dim * 0.5) / m_scale_factor - bll; refresh (); } -double -D25ViewWidget::cam_fov () const -{ - return m_fov; // @@@ -} - -double -D25ViewWidget::cam_dist () const -{ - return 4.0; // @@@ -} - -QVector3D -D25ViewWidget::cam_direction () const -{ - QVector3D cd = cam_trans ().map (QVector3D (0, 0, 1)); - cd.setZ (-cd.z ()); - return cd; -} - -QVector3D -D25ViewWidget::cam_position () const -{ - return cam_direction () * -cam_dist (); -} - -double -D25ViewWidget::cam_azimuth () const -{ - return m_cam_azimuth; -} - -double -D25ViewWidget::cam_elevation () const -{ - return m_top_view ? -90.0 : m_cam_elevation; -} - -QMatrix4x4 -D25ViewWidget::cam_perspective () const -{ - QMatrix4x4 t; - t.perspective (cam_fov (), float (width ()) / float (height ()), 0.1f, 10000.0f); - t.translate (QVector3D (0.0, 0.0, -cam_dist ())); - return t; -} - -QMatrix4x4 -D25ViewWidget::cam_trans () const -{ - QMatrix4x4 t; - t.rotate (-cam_elevation (), 1.0, 0.0, 0.0); - t.rotate (cam_azimuth (), 0.0, 1.0, 0.0); - return t; -} - void D25ViewWidget::refresh () { QVector3D cp = cam_position (); -printf("@@@ e=%g a=%g x,y,z=%g,%g,%g d=%g,%g,%g s=%g f=%g\n", cam_elevation (), cam_azimuth (), cp.x(), cp.y(), cp.z(), m_displacement.x(), m_displacement.y(), m_displacement.z(), m_scale_factor, m_focus_dist); fflush(stdout); +printf("@@@ e=%g a=%g x,y,z=%g,%g,%g d=%g,%g,%g s=%g\n", cam_elevation (), cam_azimuth (), cp.x(), cp.y(), cp.z(), m_displacement.x(), m_displacement.y(), m_displacement.z(), m_scale_factor); fflush(stdout); update (); } +void +D25ViewWidget::camera_changed () +{ + refresh (); +} + +double +D25ViewWidget::aspect_ratio () const +{ + return double (width ()) / double (height ()); +} + void D25ViewWidget::attach_view (LayoutView *view) { @@ -439,61 +492,26 @@ D25ViewWidget::render_polygon (D25ViewWidget::chunks_type &chunks, const db::Pol // triangle bottom - chunks.add (pts[0].x () * dbu); - chunks.add (zstart); - chunks.add (pts[0].y () * dbu); - - chunks.add (pts[2].x () * dbu); - chunks.add (zstart); - chunks.add (pts[2].y () * dbu); - - chunks.add (pts[1].x () * dbu); - chunks.add (zstart); - chunks.add (pts[1].y () * dbu); + chunks.add (pts[0].x () * dbu, zstart, pts[0].y () * dbu); + chunks.add (pts[2].x () * dbu, zstart, pts[2].y () * dbu); + chunks.add (pts[1].x () * dbu, zstart, pts[1].y () * dbu); // triangle top - - chunks.add (pts[0].x () * dbu); - chunks.add (zstop); - chunks.add (pts[0].y () * dbu); - - chunks.add (pts[1].x () * dbu); - chunks.add (zstop); - chunks.add (pts[1].y () * dbu); - - chunks.add (pts[2].x () * dbu); - chunks.add (zstop); - chunks.add (pts[2].y () * dbu); + chunks.add (pts[0].x () * dbu, zstop, pts[0].y () * dbu); + chunks.add (pts[1].x () * dbu, zstop, pts[1].y () * dbu); + chunks.add (pts[2].x () * dbu, zstop, pts[2].y () * dbu); if (poly.hull ().size () == 4) { // triangle bottom - - chunks.add (pts[0].x () * dbu); - chunks.add (zstart); - chunks.add (pts[0].y () * dbu); - - chunks.add (pts[3].x () * dbu); - chunks.add (zstart); - chunks.add (pts[3].y () * dbu); - - chunks.add (pts[2].x () * dbu); - chunks.add (zstart); - chunks.add (pts[2].y () * dbu); + chunks.add (pts[0].x () * dbu, zstart, pts[0].y () * dbu); + chunks.add (pts[3].x () * dbu, zstart, pts[3].y () * dbu); + chunks.add (pts[2].x () * dbu, zstart, pts[2].y () * dbu); // triangle top - - chunks.add (pts[0].x () * dbu); - chunks.add (zstop); - chunks.add (pts[0].y () * dbu); - - chunks.add (pts[2].x () * dbu); - chunks.add (zstop); - chunks.add (pts[2].y () * dbu); - - chunks.add (pts[3].x () * dbu); - chunks.add (zstop); - chunks.add (pts[3].y () * dbu); + chunks.add (pts[0].x () * dbu, zstop, pts[0].y () * dbu); + chunks.add (pts[2].x () * dbu, zstop, pts[2].y () * dbu); + chunks.add (pts[3].x () * dbu, zstop, pts[3].y () * dbu); } @@ -503,29 +521,12 @@ D25ViewWidget::render_polygon (D25ViewWidget::chunks_type &chunks, const db::Pol void D25ViewWidget::render_wall (D25ViewWidget::chunks_type &chunks, const db::Edge &edge, double dbu, double zstart, double zstop) { - chunks.add (edge.p1 ().x () * dbu); - chunks.add (zstart); - chunks.add (edge.p1 ().y () * dbu); - - chunks.add (edge.p2 ().x () * dbu); - chunks.add (zstop); - chunks.add (edge.p2 ().y () * dbu); - - chunks.add (edge.p1 ().x () * dbu); - chunks.add (zstop); - chunks.add (edge.p1 ().y () * dbu); - - chunks.add (edge.p1 ().x () * dbu); - chunks.add (zstart); - chunks.add (edge.p1 ().y () * dbu); - - chunks.add (edge.p2 ().x () * dbu); - chunks.add (zstart); - chunks.add (edge.p2 ().y () * dbu); - - chunks.add (edge.p2 ().x () * dbu); - chunks.add (zstop); - chunks.add (edge.p2 ().y () * dbu); + chunks.add (edge.p1 ().x () * dbu, zstart, edge.p1 ().y () * dbu); + chunks.add (edge.p2 ().x () * dbu, zstop, edge.p2 ().y () * dbu); + chunks.add (edge.p1 ().x () * dbu, zstop, edge.p1 ().y () * dbu); + chunks.add (edge.p1 ().x () * dbu, zstart, edge.p1 ().y () * dbu); + chunks.add (edge.p2 ().x () * dbu, zstart, edge.p2 ().y () * dbu); + chunks.add (edge.p2 ().x () * dbu, zstop, edge.p2 ().y () * dbu); } void @@ -642,8 +643,7 @@ D25ViewWidget::initializeGL () "uniform vec4 ambient;\n" "uniform vec3 illum;\n" "out lowp vec4 vertexColor;\n" - "uniform mat4 geo_matrix;\n" - "uniform mat4 cam_matrix;\n" + "uniform mat4 matrix;\n" "layout (triangles) in;\n" "layout (triangle_strip, max_vertices = 3) out;\n" "\n" @@ -655,11 +655,11 @@ D25ViewWidget::initializeGL () " float dp = dot(normalize(n), illum);\n" " vertexColor = color * (dp * 0.5 + 0.5) - (min(0.0, dp) * 0.5 * ambient);\n" " vertexColor.a = 1.0;\n" - " gl_Position = cam_matrix * geo_matrix * p0;\n" + " gl_Position = matrix * p0;\n" " EmitVertex();\n" - " gl_Position = cam_matrix * geo_matrix * p1;\n" + " gl_Position = matrix * p1;\n" " EmitVertex();\n" - " gl_Position = cam_matrix * geo_matrix * p2;\n" + " gl_Position = matrix * p2;\n" " EmitVertex();\n" " EndPrimitive();\n" "}\n"; @@ -743,9 +743,6 @@ D25ViewWidget::initializeGL () void D25ViewWidget::paintGL () { - GLfloat vertices[6000]; - size_t nmax = sizeof (vertices) / sizeof (GLfloat); - const qreal retinaScale = devicePixelRatio (); glViewport (0, 0, width () * retinaScale, height () * retinaScale); @@ -767,8 +764,9 @@ D25ViewWidget::paintGL () m_shapes_program->bind (); - m_shapes_program->setUniformValue ("geo_matrix", cam_trans () * scene_trans); - m_shapes_program->setUniformValue ("cam_matrix", cam_perspective ()); + // draw the actual layout + + m_shapes_program->setUniformValue ("matrix", cam_perspective () * cam_trans () * scene_trans); // NOTE: z axis of illum points towards the scene because we include the z inversion in the scene transformation matrix m_shapes_program->setUniformValue ("illum", QVector3D (-3.0, -4.0, 2.0).normalized ()); @@ -779,20 +777,19 @@ D25ViewWidget::paintGL () glEnableVertexAttribArray (positions); for (std::list::const_iterator l = m_layers.begin (); l != m_layers.end (); ++l) { - m_shapes_program->setUniformValue ("color", l->color [0], l->color [1], l->color [2], l->color [3]); - - for (chunks_type::iterator c = l->vertex_chunk->begin (); c != l->vertex_chunk->end (); ++c) { - glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, c->front ()); - glDrawArrays (GL_TRIANGLES, 0, c->size () / 3); - } - + l->vertex_chunk->draw_to (this, positions, GL_TRIANGLES); } glDisableVertexAttribArray (positions); m_shapes_program->release (); + // decoration + + // a vertex buffer for the decoration + lay::mem_chunks vertexes; + m_gridplane_program->bind (); glEnable (GL_DEPTH_TEST); @@ -802,26 +799,14 @@ D25ViewWidget::paintGL () m_gridplane_program->setUniformValue ("matrix", cam_perspective () * cam_trans ()); - size_t index = 0; - double compass_rad = 0.3; double compass_bars = 0.4; - vertices[index++] = -compass_bars; - vertices[index++] = 0.0; - vertices[index++] = 0.0; + vertexes.add (-compass_bars, 0.0, 0.0); + vertexes.add (compass_bars, 0.0, 0.0); - vertices[index++] = compass_bars; - vertices[index++] = 0.0; - vertices[index++] = 0.0; - - vertices[index++] = 0.0; - vertices[index++] = 0.0; - vertices[index++] = -compass_bars; - - vertices[index++] = 0.0; - vertices[index++] = 0.0; - vertices[index++] = compass_bars; + vertexes.add (0.0, 0.0, -compass_bars); + vertexes.add (0.0, 0.0, compass_bars); int ncircle = 64; double x = compass_rad, z = 0.0; @@ -833,13 +818,8 @@ D25ViewWidget::paintGL () double xx = compass_rad * cos (a); double zz = compass_rad * sin (a); - vertices[index++] = x; - vertices[index++] = 0.0; - vertices[index++] = z; - - vertices[index++] = xx; - vertices[index++] = 0.0; - vertices[index++] = zz; + vertexes.add (x, 0.0, z); + vertexes.add (xx, 0.0, zz); x = xx; z = zz; @@ -848,29 +828,18 @@ D25ViewWidget::paintGL () m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, 0.25f); - glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, vertices); - glLineWidth (2.0); - glDrawArrays (GL_LINES, 0, index / 3); - - index = 0; + vertexes.draw_to (this, positions, GL_LINES); // arrow - vertices[index++] = -0.25 * compass_rad; - vertices[index++] = 0.0; - vertices[index++] = 0.6 * compass_rad; - vertices[index++] = 0.0; - vertices[index++] = 0.0; - vertices[index++] = -0.8 * compass_rad; + vertexes.clear (); - vertices[index++] = 0.25 * compass_rad; - vertices[index++] = 0.0; - vertices[index++] = 0.6 * compass_rad; + vertexes.add (-0.25 * compass_rad, 0.0, 0.6 * compass_rad); + vertexes.add (0.0, 0.0, -0.8 * compass_rad); + vertexes.add (0.25 * compass_rad, 0.0, 0.6 * compass_rad); - glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, vertices); - - glDrawArrays (GL_TRIANGLES, 0, index / 3); + vertexes.draw_to (this, positions, GL_TRIANGLES); // draw base plane @@ -880,71 +849,61 @@ D25ViewWidget::paintGL () double gminor = gg.second, gmajor = gg.first; double margin = std::max (m_bbox.width (), m_bbox.height ()) * 0.02; - double l = m_bbox.left () - margin; - double r = m_bbox.right () + margin; - double b = m_bbox.bottom () - margin; - double t = m_bbox.top () + margin; - // @@@ + double l = m_bbox.left (); + double r = m_bbox.right (); + double b = m_bbox.bottom (); + double t = m_bbox.top (); // major and minor grid lines + vertexes.clear (); + const double epsilon = 1e-6; for (int major = 0; major < 2; ++major) { m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, major ? 0.25f : 0.15f); - size_t index = 0; double x, y; double step = (major ? gmajor : gminor); x = ceil (l / step) * step; - for ( ; index < nmax && x < r - step * epsilon; x += step) { + for ( ; x < r - step * epsilon; x += step) { if ((fabs (floor (x / gmajor + 0.5) * gmajor - x) < epsilon) == (major != 0)) { - vertices [index++] = x; - vertices [index++] = 0.0; - vertices [index++] = b; - vertices [index++] = x; - vertices [index++] = 0.0; - vertices [index++] = t; + vertexes.add (x, 0.0, b - margin); + vertexes.add (x, 0.0, t + margin); } } y = ceil (b / step) * step; - for ( ; index < nmax && y < t - step * epsilon; y += step) { + for ( ; y < t - step * epsilon; y += step) { if ((fabs (floor (y / gmajor + 0.5) * gmajor - y) < epsilon) == (major != 0)) { - vertices [index++] = l; - vertices [index++] = 0.0; - vertices [index++] = y; - vertices [index++] = r; - vertices [index++] = 0.0; - vertices [index++] = y; + vertexes.add (l - margin, 0.0, y); + vertexes.add (r + margin, 0.0, y); } } - glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, vertices); - glLineWidth (2.0); - glDrawArrays (GL_LINES, 0, index / 3); + vertexes.draw_to (this, positions, GL_LINES); } - l = m_bbox.left (); - r = m_bbox.right (); - b = m_bbox.bottom (); - t = m_bbox.top (); + // the plane itself - GLfloat plane_vertices[] = { - float (l), 0.0f, float (b), float (l), 0.0f, float (t), float (r), 0.0f, float (t), - float (l), 0.0f, float (b), float (r), 0.0f, float (t), float (r), 0.0f, float (b) - }; + vertexes.clear (); + + vertexes.add (l, 0.0, b); + vertexes.add (l, 0.0, t); + vertexes.add (r, 0.0, t); + + vertexes.add (l, 0.0, b); + vertexes.add (r, 0.0, b); + vertexes.add (r, 0.0, t); m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, 0.1f); - glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, plane_vertices); - - glDrawArrays (GL_TRIANGLES, 0, 6); + vertexes.draw_to (this, positions, GL_TRIANGLES); glDisableVertexAttribArray (positions); diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h index e89404360..36f8d59b8 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h @@ -23,6 +23,11 @@ #ifndef HDR_layD25ViewWidget #define HDR_layD25ViewWidget +#include "dbPolygon.h" + +#include "layD25MemChunks.h" +#include "layD25Camera.h" + #include #include #include @@ -32,9 +37,7 @@ #include #include -#include "dbPolygon.h" - -#include "layD25MemChunks.h" +#include namespace db { @@ -46,10 +49,25 @@ namespace lay { class LayoutView; +class D25ViewWidget; + +class D25InteractionMode +{ +public: + D25InteractionMode (D25ViewWidget *widget); + virtual ~D25InteractionMode (); + + D25ViewWidget *view () { return mp_view; } + virtual void mouse_move (QMouseEvent *event) = 0; + +private: + D25ViewWidget *mp_view; +}; class D25ViewWidget : public QOpenGLWidget, - private QOpenGLFunctions + private QOpenGLFunctions, + public D25Camera { Q_OBJECT @@ -66,22 +84,40 @@ public: void attach_view (lay::LayoutView *view); + QVector3D hit_point_with_scene(const QVector3D &line_dir); + void refresh (); + void reset (); + + QVector3D displacement () const { return m_displacement; } + + void set_displacement (const QVector3D &d) + { + m_displacement = d; + refresh (); + } + + double scale_factor () const { return m_scale_factor; } + + void set_scale_factor (double f) + { + m_scale_factor = f; + refresh (); + } + +protected: + virtual void camera_changed (); + virtual double aspect_ratio () const; + +public slots: + void fit (); + private: typedef lay::mem_chunks chunks_type; + std::auto_ptr mp_mode; QOpenGLShaderProgram *m_shapes_program, *m_gridplane_program; - bool m_dragging, m_rotating; double m_scale_factor; - double m_cam_azimuth, m_cam_elevation; - bool m_top_view; QVector3D m_displacement; - double m_focus_dist; - double m_fov; - QVector3D m_hit_point; - QPoint m_start_pos; - QVector3D m_start_cam_position; - double m_start_cam_azimuth, m_start_cam_elevation; - QVector3D m_start_displacement; lay::LayoutView *mp_view; db::DBox m_bbox; double m_zmin, m_zmax; @@ -99,21 +135,10 @@ private: void paintGL (); void resizeGL (int w, int h); - void refresh (); - void reset (); void prepare_view (); void render_layout (D25ViewWidget::chunks_type &chunks, const db::Layout &layout, const db::Cell &cell, unsigned int layer, double zstart, double zstop); void render_polygon (D25ViewWidget::chunks_type &chunks, const db::Polygon &poly, double dbu, double zstart, double zstop); void render_wall (D25ViewWidget::chunks_type &chunks, const db::Edge &poly, double dbu, double zstart, double zstop); - QVector3D hit_point_with_scene(const QVector3D &line_dir); - double cam_elevation () const; - double cam_azimuth () const; - QVector3D cam_position () const; - QVector3D cam_direction () const; - QMatrix4x4 cam_perspective () const; - QMatrix4x4 cam_trans () const; - double cam_dist () const; - double cam_fov () const; }; } diff --git a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro index 6afb49ac2..2ef786e9b 100644 --- a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro +++ b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro @@ -12,14 +12,16 @@ HEADERS = \ layD25View.h \ layD25ViewWidget.h \ layD25MemChunks.h \ - layD25ViewUtils.h + layD25ViewUtils.h \ + layD25Camera.h SOURCES = \ layD25View.cc \ layD25ViewWidget.cc \ layD25Plugin.cc \ layD25MemChunks.cc \ - layD25ViewUtils.cc + layD25ViewUtils.cc \ + layD25Camera.cc FORMS = \ D25View.ui \ diff --git a/src/plugins/tools/view_25d/unit_tests/layD25CameraTests.cc b/src/plugins/tools/view_25d/unit_tests/layD25CameraTests.cc new file mode 100644 index 000000000..2934a9a9c --- /dev/null +++ b/src/plugins/tools/view_25d/unit_tests/layD25CameraTests.cc @@ -0,0 +1,102 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "layD25Camera.h" +#include "tlUnitTest.h" + +static std::string v2s (const QVector4D &v) +{ + return tl::to_string (v.x ()) + "," + tl::to_string (v.y ()) + "," + tl::to_string (v.z ()); +} + +static std::string v2s (const QVector3D &v) +{ + return tl::to_string (v.x ()) + "," + tl::to_string (v.y ()) + "," + tl::to_string (v.z ()); +} + +static std::string v2s_2d (const QVector3D &v) +{ + return tl::to_string (v.x ()) + "," + tl::to_string (v.y ()); +} + +TEST(1_Transformations) +{ + lay::D25Camera cam; + + cam.set_cam_azimuth (45.0); + EXPECT_EQ (cam.cam_azimuth (), 45.0); + cam.set_cam_elevation (22.0); + EXPECT_EQ (cam.cam_elevation (), 22.0); + + cam.camera_reset (); + EXPECT_EQ (cam.cam_azimuth (), 0.0); + EXPECT_EQ (cam.cam_elevation (), 0.0); + + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (1, 0, 0, 1))), "1,0,0"); + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 1, 0, 1))), "0,1,0"); + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 0, 1, 1))), "0,0,1"); + EXPECT_EQ (v2s (cam.cam_direction ()), "0,0,-1"); + EXPECT_EQ (v2s (cam.cam_position ()), "0,0,4"); + + // looking up from the bottom, x axis stays the same (azimuth = 0) + cam.set_cam_elevation (90.0); + + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (1, 0, 0, 1))), "1,0,0"); + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 1, 0, 1))), "0,0,-1"); + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 0, 1, 1))), "0,1,0"); + + EXPECT_EQ (v2s (cam.cam_direction ()), "0,1,0"); + EXPECT_EQ (v2s (cam.cam_position ()), "0,-4,0"); + + // looking down from the top, x axis stays the same (azimuth = 0) + cam.set_cam_elevation (-90.0); + + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (1, 0, 0, 1))), "1,0,0"); + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 1, 0, 1))), "0,0,1"); + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 0, 1, 1))), "0,-1,0"); + + EXPECT_EQ (v2s (cam.cam_direction ()), "0,-1,0"); + EXPECT_EQ (v2s (cam.cam_position ()), "0,4,0"); + + // looking from the left, y axis stays the same (elevation = 0) + cam.set_cam_elevation (0.0); + cam.set_cam_azimuth (90.0); + + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (1, 0, 0, 1))), "0,0,-1"); + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 1, 0, 1))), "0,1,0"); + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 0, 1, 1))), "1,0,0"); + + EXPECT_EQ (v2s (cam.cam_direction ()), "1,0,0"); + EXPECT_EQ (v2s (cam.cam_position ()), "-4,0,0"); + + // looking from the right, y axis stays the same (elevation = 0) + cam.set_cam_elevation (0.0); + cam.set_cam_azimuth (-90.0); + + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (1, 0, 0, 1))), "0,0,1"); + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 1, 0, 1))), "0,1,0"); + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 0, 1, 1))), "-1,0,0"); + + EXPECT_EQ (v2s (cam.cam_direction ()), "-1,0,0"); + EXPECT_EQ (v2s (cam.cam_position ()), "4,0,0"); +} diff --git a/src/plugins/tools/view_25d/unit_tests/unit_tests.pro b/src/plugins/tools/view_25d/unit_tests/unit_tests.pro index 729ddc42e..c3e4e8e21 100644 --- a/src/plugins/tools/view_25d/unit_tests/unit_tests.pro +++ b/src/plugins/tools/view_25d/unit_tests/unit_tests.pro @@ -7,7 +7,8 @@ include($$PWD/../../../../lib_ut.pri) SOURCES = \ layD25MemChunksTests.cc \ - layD25ViewUtilsTests.cc + layD25ViewUtilsTests.cc \ + layD25CameraTests.cc INCLUDEPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../lay_plugin $$PWD/../../../common DEPENDPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../lay_plugin $$PWD/../../../common From f954afab7a0ba14cd456d9fc3e01fa4cedc6c660 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 16 Apr 2020 23:14:48 +0200 Subject: [PATCH 018/113] Added missing files. --- src/lay/lay/images/fit_back.png | Bin 0 -> 537 bytes src/lay/lay/images/fit_bottom.png | Bin 0 -> 530 bytes src/lay/lay/images/fit_front.png | Bin 0 -> 543 bytes src/lay/lay/images/fit_left.png | Bin 0 -> 548 bytes src/lay/lay/images/fit_right.png | Bin 0 -> 542 bytes src/lay/lay/images/fit_top.png | Bin 0 -> 526 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/lay/lay/images/fit_back.png create mode 100644 src/lay/lay/images/fit_bottom.png create mode 100644 src/lay/lay/images/fit_front.png create mode 100644 src/lay/lay/images/fit_left.png create mode 100644 src/lay/lay/images/fit_right.png create mode 100644 src/lay/lay/images/fit_top.png diff --git a/src/lay/lay/images/fit_back.png b/src/lay/lay/images/fit_back.png new file mode 100644 index 0000000000000000000000000000000000000000..cbfbbad302c9edd73247d93b027bd5eb7409c23a GIT binary patch literal 537 zcmV+!0_OdRP)1I-S>Ux4T;^ zm2RS*7JZ~rsTE3TlTw-{Br*4UOG3c+{dM2>FD%Pi`}G3LvP2k$bIQr_Fj=qHzpB-v zW*CNZjYfk20C=7kYz3D}6q&IKN|#rm&@h$RRGm$wS$jqas3x?Dcx5rfHr|0vHSi003{3 b{GC`I`<<^26aZsg00000NkvXXu0mjfE5PY~ literal 0 HcmV?d00001 diff --git a/src/lay/lay/images/fit_bottom.png b/src/lay/lay/images/fit_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..7308be3a2967f5cf857183f7bbfe733523a64786 GIT binary patch literal 530 zcmV+t0`2{YP)rcL3N<^dC$F6T$e9Zo^zI7c|YTE((I>mfPueIvt+pL&;?F8B1Uo zS}K5~n_cSa8b@%^jc(M8#X|r%vS*GH@jUNo>v}3Yb@zZ%4gjzQxA&^1ahXhJ-kv!& zc&|S0`)8pU@=A4F!IHPB_}cYX9Zj?L%&~!PXeX=GO9i*QN0_QrTCK0+p#oLa0-FmT zPooOf{Zh@;T8@i21j|0TOd<>G@3FHoCzb890V6_4D^)$QQZ*R+i{OXPF}@w0Z`1dY UX}95mZ2$lO07*qoM6N<$f^B@{xBvhE literal 0 HcmV?d00001 diff --git a/src/lay/lay/images/fit_front.png b/src/lay/lay/images/fit_front.png new file mode 100644 index 0000000000000000000000000000000000000000..5034b975b22eb7248164ad8ac0ad4a036ab6250e GIT binary patch literal 543 zcmV+)0^t3LP))22Y+k_#Y%NZ&G&)j1G0FtV6em4jTlSW4mo8TJ7$1sZiV4F|R(y@11!c=3@X_ z*nq5t@pycJd zbTfzhdqtF2nFObAqmv#iUj_J+eHOuiIlp-EF*3En=QH)$uNCRE4c`=dCdTmfB}+K~ zfRA2X-oCnhMZ?iyD>y0)db#)K!mxwnSp@@*7tN_Z$;ax@n62Q9xTeRu?$24vzwz;M z_MA9pYz1oR-8tL$c-yF8#zXGj$|c=a;B~864pG{u003azMK0=RHNOZURLRKg{){a8 h|0DRK*u>q&`2>kgj{kPc8$SR5002ovPDHLkV1hg#?8^WE literal 0 HcmV?d00001 diff --git a/src/lay/lay/images/fit_left.png b/src/lay/lay/images/fit_left.png new file mode 100644 index 0000000000000000000000000000000000000000..36dc09f5c00b813591bc670e672ea6daee6c96be GIT binary patch literal 548 zcmV+<0^9wGP)D6oweJv_%9pwHy8cK}~X3lN^Ku zO%Zr);^H9EVwe)#wS?eOLq7%~oEn1~ngVGlknS*julLmV-lo9LlRK@^XS?UQ_xZfv z?+Y-JgOHtBC=|kDUe7J!JU%xirfGfwz@{jQWk*0f9uLjU1TF%>*)3lvbDs|nRaor!0NF;fQW_L$-#`XUGzZwBXfWOOYXR6dc#W>SA zQxvMWD4~>g^7;HiEEaol1-K_mt1YOX$M*I`H@D2?aw$sbE&v=@vz`@on1_3A23E9Y z>&Q(x06@K7&*{1zO(YV2M?m>wei7^BQIe!5w*X0!UUgl+qm*u2vz`@I-lvx-M-JVV z{|IB8rIemn{hkTbhA~;KR&NFcq|@mN#+Wz)2qD>0sYC|_003Z&kFDOX6(NL(VHg>n m=QG0q+U+&~Kx3G*L+cw7j;Ky$^$sWi0000ugK~zYI?UO%90#O{tf4@V^h=$-O$0qlnQE+OqM!TaS(1wO` z_GT9c5t5S7L9dqnIJDVfP!J8#R?w(4mDOFBp7+%5UQ>{IaxbIJ&-D2Hc;C-^@5cie z;Q-ndbGcl=<#Lrcj++s`$ggQyQPZ?lMBB^Rf{3u_xjFrKR}Mr`3=X`3D2fK9)We)?1BcK) zJUSk?Z3==wzt^%XTP%ST0Odk~(n26k3xW9041k^|`VU5ek>KyR?24I8#%r3U!SWaO z0l+jr%_p3h_F_$C613ax4a>6RXf(RXa?1SPcKQ~43rn8bxuYPP&AJsuSx3ZRZ*LGa zjigj=>b~wCyXx)r&7e}LtTda=2T79dSOtYh6P|MAB-tQi33)h7oi+ g9RPs(FnfpQ3oQ|;^7mw!&;S4c07*qoM6N<$g7wJjJOBUy literal 0 HcmV?d00001 diff --git a/src/lay/lay/images/fit_top.png b/src/lay/lay/images/fit_top.png new file mode 100644 index 0000000000000000000000000000000000000000..843fd32d6970747d41b8a09f611c3fba06067130 GIT binary patch literal 526 zcmV+p0`dKcP)zQ8edGGwV2Ldoq z50Ri@rBa!VgomyO361cCxUOTlKo-*J^mR`_Hk(bvC&o|5Vw15H+27#-XzuEEv+>e+ zebZMIMY-^LA@4^rnM}}+_p_Pk{W&3l%LR%M0&~&3lUB2FAd2Grj~gtBg5|g{%$*#d z5CQw}=r|f^3WDHv&YGq7QsWgR%2JgRsh@YyePD_&X z#09WzTXGyn%VaVeJb-e!tRUh#0Brfau8LGujhLpnMk(Fn2?qesb-m0OOXYI8(Vl=> zt+vP*dz5AQffpdl@-t)X1`(J2UROniVQeDeE^l~8l+q$1c5U6YfngZ&dcA)6TR=Xa zzonF#Jpra^7OT}N{uTfLKq)QxXWuF!gqW6P6$C*j^aE(O+W-Kqe%|)2FEW~_f>NnH QmH+?%07*qoM6N<$g7yaCx&QzG literal 0 HcmV?d00001 From 7884446da166ac397e0031b569937c9c27228235 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 17 Apr 2020 00:25:21 +0200 Subject: [PATCH 019/113] Orientation cube, some refinement. --- .../tools/view_25d/lay_plugin/D25View.ui | 20 ++- .../tools/view_25d/lay_plugin/layD25Camera.h | 2 +- .../tools/view_25d/lay_plugin/layD25View.cc | 27 ++++ .../tools/view_25d/lay_plugin/layD25View.h | 2 + .../view_25d/lay_plugin/layD25ViewWidget.cc | 123 +++++++++++++++++- .../view_25d/lay_plugin/layD25ViewWidget.h | 5 +- 6 files changed, 173 insertions(+), 6 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/D25View.ui b/src/plugins/tools/view_25d/lay_plugin/D25View.ui index efdbd6365..f09a1a654 100644 --- a/src/plugins/tools/view_25d/lay_plugin/D25View.ui +++ b/src/plugins/tools/view_25d/lay_plugin/D25View.ui @@ -61,6 +61,24 @@
+ + + 0 + 0 + + + + + 150 + 16777215 + + + + -300 + + + 300 + Qt::Horizontal @@ -91,7 +109,7 @@ Qt::Horizontal - QSizePolicy::Fixed + QSizePolicy::Expanding diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25Camera.h b/src/plugins/tools/view_25d/lay_plugin/layD25Camera.h index 22dd780e4..06680ef47 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25Camera.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25Camera.h @@ -105,7 +105,7 @@ public: /** * @brief Gets the azimuth angle - * ... + * A positive angle means we look from the left. A negative means we look from the right. */ double cam_azimuth () const; diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc index aa6c587a4..158e19f3f 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -50,6 +50,8 @@ D25View::D25View (QWidget *parent) connect (mp_ui->fit_right, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); connect (mp_ui->fit_top, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); connect (mp_ui->fit_bottom, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); + connect (mp_ui->zoom_slider, SIGNAL (valueChanged (int)), this, SLOT (scale_slider_changed (int))); + connect (mp_ui->d25_view, SIGNAL (scale_factor_changed (double)), this, SLOT (scale_factor_changed (double))); } D25View::~D25View () @@ -58,6 +60,31 @@ D25View::~D25View () mp_ui = 0; } +static QString scale_factor_to_string (double f) +{ + QString s; + s.sprintf ("x %.3g", f); + return s; +} + +void +D25View::scale_slider_changed (int value) +{ + double f = exp (log (10.0) * -0.01 * value); + mp_ui->zoom_factor->setText (scale_factor_to_string (f)); + mp_ui->d25_view->set_scale_factor (f); +} + +void +D25View::scale_factor_changed (double f) +{ + mp_ui->zoom_factor->setText (scale_factor_to_string (f)); + int v = floor (0.5 - log10 (f) * 100.0); + mp_ui->zoom_slider->blockSignals (true); + mp_ui->zoom_slider->setValue (v); + mp_ui->zoom_slider->blockSignals (false); +} + int D25View::exec_dialog (lay::LayoutView *view) { diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.h b/src/plugins/tools/view_25d/lay_plugin/layD25View.h index 207ca7751..6f776ed3c 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.h @@ -56,6 +56,8 @@ protected: private slots: void fit_button_clicked (); + void scale_factor_changed (double f); + void scale_slider_changed (int value); private: Ui::D25View *mp_ui; diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index 1a077e786..48ca09105 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -240,6 +240,8 @@ D25ViewWidget::wheelEvent (QWheelEvent *event) m_scale_factor *= f; m_displacement += hp * (1.0 - f) / m_scale_factor; + emit scale_factor_changed (m_scale_factor); + } else { // compute vector of line of sight @@ -248,16 +250,16 @@ D25ViewWidget::wheelEvent (QWheelEvent *event) // by definition the ray goes through the camera position QVector3D hp = hit_point_with_scene (ray.second); - if (event->modifiers () & Qt::ControlModifier) { + if (! (event->modifiers () & Qt::ControlModifier)) { - // "Ctrl" is closeup + // No Ctrl is closeup double f = event->angleDelta ().y () * (1.0 / (90 * 8)); m_displacement += -((f / m_scale_factor) * std::min (cam_dist (), double ((cam_position () - hp).length ()))) * ray.second; } else { - // No shift is zoom + // "Ctrl" is zoom double f = exp (event->angleDelta ().y () * (1.0 / (90 * 8))); @@ -277,6 +279,8 @@ D25ViewWidget::wheelEvent (QWheelEvent *event) m_displacement = ct.inverted ().map (displacement); + emit scale_factor_changed (m_scale_factor); + } } @@ -382,6 +386,8 @@ D25ViewWidget::fit () m_displacement = (new_center_in_scene - dim * 0.5) / m_scale_factor - bll; refresh (); + + emit scale_factor_changed (m_scale_factor); } void @@ -905,6 +911,117 @@ D25ViewWidget::paintGL () vertexes.draw_to (this, positions, GL_TRIANGLES); + // the orientation cube + + if (! top_view ()) { + + glDisable (GL_DEPTH_TEST); + + int cube_size = 64; + int cube_margin = 20; + + QMatrix4x4 into_top_right_corner; + into_top_right_corner.translate (1.0 - 2.0 / width () * (cube_margin + cube_size / 2), 1.0 - 2.0 / height () * (cube_margin + cube_size / 2)); + // into_top_right_corner.translate (0.5, 0.5, 0.0); + into_top_right_corner.scale (2.0 * cube_size / double (height ()), 2.0 * cube_size / double (height ()), 1.0); + + m_gridplane_program->setUniformValue ("matrix", into_top_right_corner * cam_perspective () * cam_trans ()); + + vertexes.clear (); + + vertexes.add (-1.0, -1.0, 1.0); + vertexes.add (-1.0, -1.0, -1.0); + + vertexes.add (-1.0, 1.0, 1.0); + vertexes.add (-1.0, 1.0, -1.0); + + vertexes.add (1.0, -1.0, 1.0); + vertexes.add (1.0, -1.0, -1.0); + + vertexes.add (1.0, 1.0, 1.0); + vertexes.add (1.0, 1.0, -1.0); + + vertexes.add (-1.0, -1.0, 1.0); + vertexes.add (-1.0, 1.0, 1.0); + + vertexes.add (1.0, -1.0, 1.0); + vertexes.add (1.0, 1.0, 1.0); + + vertexes.add (-1.0, -1.0, 1.0); + vertexes.add (1.0, -1.0, 1.0); + + vertexes.add (-1.0, 1.0, 1.0); + vertexes.add (1.0, 1.0, 1.0); + + vertexes.add (-1.0, -1.0, -1.0); + vertexes.add (-1.0, 1.0, -1.0); + + vertexes.add (1.0, -1.0, -1.0); + vertexes.add (1.0, 1.0, -1.0); + + vertexes.add (-1.0, -1.0, -1.0); + vertexes.add (1.0, -1.0, -1.0); + + vertexes.add (-1.0, 1.0, -1.0); + vertexes.add (1.0, 1.0, -1.0); + + m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, 0.2f); + + vertexes.draw_to (this, positions, GL_LINES); + + vertexes.clear (); + + // A "K" at the front + vertexes.add (-0.8, -0.8, 1.0); + vertexes.add (-0.8, 0.8, 1.0); + vertexes.add (-0.2, 0.8, 1.0); + + vertexes.add (-0.8, -0.8, 1.0); + vertexes.add (-0.2, -0.8, 1.0); + vertexes.add (-0.2, 0.8, 1.0); + + vertexes.add (0.2, 0.8, 1.0); + vertexes.add (0.8, 0.8, 1.0); + vertexes.add (0.8, 0.6, 1.0); + + vertexes.add (0.2, 0.8, 1.0); + vertexes.add (0.8, 0.6, 1.0); + vertexes.add (0.6, 0.4, 1.0); + + vertexes.add (-0.2, 0.4, 1.0); + vertexes.add (0.2, 0.8, 1.0); + vertexes.add (0.6, 0.4, 1.0); + + vertexes.add (-0.2, 0.4, 1.0); + vertexes.add (0.6, 0.4, 1.0); + vertexes.add (0.2, 0.0, 1.0); + + vertexes.add (-0.2, 0.4, 1.0); + vertexes.add (0.2, 0.0, 1.0); + vertexes.add (-0.2, -0.4, 1.0); + + vertexes.add (-0.2, -0.4, 1.0); + vertexes.add (0.6, -0.4, 1.0); + vertexes.add (0.2, -0.0, 1.0); + + vertexes.add (-0.2, -0.4, 1.0); + vertexes.add (0.2, -0.8, 1.0); + vertexes.add (0.6, -0.4, 1.0); + + vertexes.add (0.2, -0.8, 1.0); + vertexes.add (0.8, -0.6, 1.0); + vertexes.add (0.6, -0.4, 1.0); + + vertexes.add (0.2, -0.8, 1.0); + vertexes.add (0.8, -0.8, 1.0); + vertexes.add (0.8, -0.6, 1.0); + + m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, 0.3f); + + vertexes.draw_to (this, positions, GL_TRIANGLES); + + } + glDisableVertexAttribArray (positions); m_shapes_program->release (); diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h index 36f8d59b8..ea6d5c790 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h @@ -104,6 +104,9 @@ public: refresh (); } +signals: + void scale_factor_changed (double f); + protected: virtual void camera_changed (); virtual double aspect_ratio () const; @@ -112,7 +115,7 @@ public slots: void fit (); private: - typedef lay::mem_chunks chunks_type; + typedef lay::mem_chunks chunks_type; std::auto_ptr mp_mode; QOpenGLShaderProgram *m_shapes_program, *m_gridplane_program; From b2216ec37284b683951039145f6991ff2c58b107 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 17 Apr 2020 11:15:50 +0200 Subject: [PATCH 020/113] WIP: tech component + editor. --- src/db/db/db.pro | 6 +- src/db/db/dbD25TechnologyComponent.cc | 286 ++++++++++++++++++ src/db/db/dbD25TechnologyComponent.h | 152 ++++++++++ .../laybasic/D25TechnologyComponentEditor.ui | 38 +++ .../laybasic/layD25TechnologyComponent.cc | 93 ++++++ .../laybasic/layD25TechnologyComponent.h | 54 ++++ src/laybasic/laybasic/laybasic.pro | 9 +- src/laybasic/laybasic/laybasicResources.qrc | 1 + src/laybasic/laybasic/syntax/d25_text.xml | 76 +++++ .../tools/view_25d/lay_plugin/layD25Plugin.cc | 2 +- 10 files changed, 711 insertions(+), 6 deletions(-) create mode 100644 src/db/db/dbD25TechnologyComponent.cc create mode 100644 src/db/db/dbD25TechnologyComponent.h create mode 100644 src/laybasic/laybasic/D25TechnologyComponentEditor.ui create mode 100644 src/laybasic/laybasic/layD25TechnologyComponent.cc create mode 100644 src/laybasic/laybasic/layD25TechnologyComponent.h create mode 100644 src/laybasic/laybasic/syntax/d25_text.xml diff --git a/src/db/db/db.pro b/src/db/db/db.pro index ea36ff395..81ca2b2c5 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -184,7 +184,8 @@ SOURCES = \ dbLayoutVsSchematic.cc \ gsiDeclDbNetlistCrossReference.cc \ gsiDeclDbLayoutVsSchematic.cc \ - dbNetlistObject.cc + dbNetlistObject.cc \ + dbD25TechnologyComponent.cc HEADERS = \ dbArray.h \ @@ -331,7 +332,8 @@ HEADERS = \ dbLayoutVsSchematicReader.h \ dbLayoutVsSchematicFormatDefs.h \ dbLayoutVsSchematic.h \ - dbNetlistObject.h + dbNetlistObject.h \ + dbD25TechnologyComponent.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbD25TechnologyComponent.cc b/src/db/db/dbD25TechnologyComponent.cc new file mode 100644 index 000000000..7e3742a5e --- /dev/null +++ b/src/db/db/dbD25TechnologyComponent.cc @@ -0,0 +1,286 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "dbD25TechnologyComponent.h" +#include "tlClassRegistry.h" +#include "tlString.h" + +namespace db +{ + +std::string d25_component_name () +{ + return std::string ("d25"); +} + +std::string d25_description () +{ + return tl::to_string (tr ("Z stack (2.5d)")); +} + +// ----------------------------------------------------------------------------------------- +// D25LayerInfo implementation + +D25LayerInfo::D25LayerInfo () + : m_layer (), m_zstart (0.0), m_zstop (0.0) +{ + // .. nothing yet .. +} + +D25LayerInfo::~D25LayerInfo () +{ + // .. nothing yet .. +} + +D25LayerInfo::D25LayerInfo (const D25LayerInfo &other) +{ + operator= (other); +} + +D25LayerInfo & +D25LayerInfo::operator= (const D25LayerInfo &other) +{ + if (this != &other) { + m_layer = other.m_layer; + m_zstart = other.m_zstart; + m_zstop = other.m_zstop; + } + return *this; +} + +bool +D25LayerInfo::operator== (const D25LayerInfo &other) const +{ + return fabs (m_zstart - other.m_zstart) < db::epsilon && fabs (m_zstop - other.m_zstop) < db::epsilon; +} + +void +D25LayerInfo::set_layer (const db::LayerProperties &l) +{ + m_layer = l; +} + +void +D25LayerInfo::set_layer_from_string (const std::string &l) +{ + db::LayerProperties lp; + tl::Extractor ex (l.c_str ()); + try { + lp.read (ex); + } catch (tl::Exception &) { + // ignore errors for now. + } + m_layer = lp; +} + +std::string +D25LayerInfo::layer_as_string () const +{ + return m_layer.to_string (); +} + +void +D25LayerInfo::set_zstart (double z0) +{ + m_zstart = z0; +} + +void +D25LayerInfo::set_zstop (double z1) +{ + m_zstop = z1; +} + +// ----------------------------------------------------------------------------------- +// D25TechnologyComponent implementation + +D25TechnologyComponent::D25TechnologyComponent () + : db::TechnologyComponent (d25_component_name (), d25_description ()) +{ + // provide some explanation for the initialization + m_src = + "# Provide z stack information here\n" + "# Each line is one layer. The specification consists of a layer specification, a colon and arguments.\n" + "# The arguments are named (like \"x=...\") or in serial. Parameters are separated by comma or blanks.\n" + "# Named arguments are:\n" + "#\n" + "# zstart The lower z position of the extruded layer in µm\n" + "# zstop The upper z position of the extruded layer in µm\n" + "# height The height of the extruded layer in µm\n" + "#\n" + "# 'height', 'zstart' and 'zstop' can be used in any combination. If no value is given for 'zstart', " + "# the upper level of the previous layer will be used.\n" + "#\n" + "# If a single unnamed parameter is given, it corresponds to 'height'. Two parameters correspond to\n" + "# 'zstart' and 'zstop'.\n" + "#\n" + "# Examples:\n" + "# 1: 0.5 1.5 # extrude layer 1/0 from 0.5 to 1.5 vertically\n" + "# 1: zstop=1.5, zstart=0.5 # same as above\n" + "# 1: height=1.0, zstop=1.5 # same as above\n" + "# 1: 1.0 zstop=1.5 # same as above\n" + ; +} + +D25TechnologyComponent::D25TechnologyComponent (const D25TechnologyComponent &d) + : db::TechnologyComponent (d25_component_name (), d25_description ()) +{ + m_layers = d.m_layers; + m_src = d.m_src; +} + +void +D25TechnologyComponent::compile_from_source (const std::string &src) +{ + int current_line = 0; + m_layers.clear (); + + try { + + std::vector lines = tl::split (src, "\n"); + for (std::vector::const_iterator l = lines.begin (); l != lines.end (); ++l) { + + ++current_line; + + tl::Extractor ex (l->c_str ()); + + if (ex.test ("#")) { + // ignore comments + } else if (ex.at_end ()) { + // ignore empty lines + } else { + + db::D25LayerInfo info; + if (! m_layers.empty ()) { + info.set_zstart (m_layers.back ().zstop ()); + info.set_zstop (m_layers.back ().zstop ()); + } + + tl::Variant z0, z1, h; + std::vector args; + + db::LayerProperties lp; + lp.read (ex); + info.set_layer (lp); + + ex.expect (":"); + + while (! ex.at_end ()) { + + double pv = 0.0; + + std::string pn; + if (ex.try_read_name (pn)) { + ex.expect ("="); + ex.read (pv); + } else { + ex.read (pv); + } + + ex.test (","); + + if (pn.empty ()) { + args.push_back (pv); + } else if (pn == "zstart") { + z0 = pv; + } else if (pn == "zstop") { + z1 = pv; + } else if (pn == "height") { + h = pv; + } else { + throw tl::Exception (tl::to_string (tr ("Invalid parameter name: ")) + pn); + } + + } + + if (args.size () == 0) { + if (z0.is_nil () && z1.is_nil ()) { + if (! h.is_nil ()) { + info.set_zstop (info.zstart () + h.to_double ()); + } + } else if (z0.is_nil ()) { + info.set_zstop (z1.to_double ()); + if (! h.is_nil ()) { + info.set_zstart (info.zstop () - h.to_double ()); + } + } else if (z1.is_nil ()) { + info.set_zstart (z0.to_double ()); + if (! h.is_nil ()) { + info.set_zstop (info.zstart () + h.to_double ()); + } + } + } else if (args.size () == 1) { + info.set_zstop ((! z0.is_nil () ? z0.to_double () : info.zstart ()) + args[0]); + } else if (args.size () == 2) { + info.set_zstart (args[0]); + info.set_zstop (args[1]); + } else { + throw tl::Exception (tl::to_string (tr ("Too many parameters (max 2)"))); + } + + m_layers.push_back (info); + + } + + } + + } catch (tl::Exception &ex) { + throw tl::Exception (ex.msg () + tl::sprintf (tl::to_string (tr (" in line %d")), current_line)); + } + + m_src = src; +} + +// ----------------------------------------------------------------------------------- +// D25TechnologyComponent technology component registration + +class D25TechnologyComponentProvider + : public db::TechnologyComponentProvider +{ +public: + D25TechnologyComponentProvider () + : db::TechnologyComponentProvider () + { + // .. nothing yet .. + } + + virtual db::TechnologyComponent *create_component () const + { + return new D25TechnologyComponent (); + } + + virtual tl::XMLElementBase *xml_element () const + { + return new db::TechnologyComponentXMLElement (d25_component_name (), + tl::make_element ((D25TechnologyComponent::const_iterator (D25TechnologyComponent::*) () const) &D25TechnologyComponent::begin, (D25TechnologyComponent::const_iterator (D25TechnologyComponent::*) () const) &D25TechnologyComponent::end, &D25TechnologyComponent::add, "layer", + tl::make_member (&D25LayerInfo::layer_as_string, &D25LayerInfo::set_layer_from_string, "layer") + + tl::make_member (&D25LayerInfo::zstart, &D25LayerInfo::set_zstart, "zstart") + + tl::make_member (&D25LayerInfo::zstop, &D25LayerInfo::set_zstop, "zstop") + ) + + tl::make_member (&D25TechnologyComponent::src, &D25TechnologyComponent::set_src, "src") + ); + } +}; + +static tl::RegisteredClass tc_decl (new D25TechnologyComponentProvider (), 3100, d25_component_name ().c_str ()); + +} diff --git a/src/db/db/dbD25TechnologyComponent.h b/src/db/db/dbD25TechnologyComponent.h new file mode 100644 index 000000000..8d16796e0 --- /dev/null +++ b/src/db/db/dbD25TechnologyComponent.h @@ -0,0 +1,152 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_dbD25TechnologyComponent +#define HDR_dbD25TechnologyComponent + +#include "dbTechnology.h" +#include "dbLayerProperties.h" + +namespace db +{ + +class DB_PUBLIC D25LayerInfo +{ +public: + D25LayerInfo (); + ~D25LayerInfo (); + D25LayerInfo (const D25LayerInfo &other); + D25LayerInfo &operator= (const D25LayerInfo &other); + + bool operator== (const D25LayerInfo &other) const; + + const db::LayerProperties &layer () const + { + return m_layer; + } + + void set_layer_from_string (const std::string &l); + std::string layer_as_string () const; + + void set_layer (const db::LayerProperties &l); + + double zstart () const + { + return m_zstart; + } + + void set_zstart (double z0); + + double zstop () const + { + return m_zstop; + } + + void set_zstop (double z1); + +private: + db::LayerProperties m_layer; + double m_zstart, m_zstop; +}; + +class DB_PUBLIC D25TechnologyComponent + : public db::TechnologyComponent +{ +public: + D25TechnologyComponent (); + D25TechnologyComponent (const D25TechnologyComponent &d); + + typedef std::list layers_type; + typedef layers_type::const_iterator const_iterator; + typedef layers_type::iterator iterator; + + void compile_from_source (const std::string &src); + + const_iterator begin () const + { + return m_layers.begin (); + } + + iterator begin () + { + return m_layers.begin (); + } + + const_iterator end () const + { + return m_layers.end (); + } + + iterator end () + { + return m_layers.end (); + } + + void clear () + { + m_layers.clear (); + } + + void erase (iterator p) + { + m_layers.erase (p); + } + + void insert (iterator p, const D25LayerInfo &info) + { + m_layers.insert (p, info); + } + + void add (const D25LayerInfo &info) + { + m_layers.push_back (info); + } + + size_t size () const + { + return m_layers.size (); + } + + const std::string &src () const + { + return m_src; + } + + // for persistency only, use "compile_from_source" to read from a source string + void set_src (const std::string &s) + { + m_src = s; + } + + db::TechnologyComponent *clone () const + { + return new D25TechnologyComponent (*this); + } + +private: + layers_type m_layers; + std::string m_src; +}; + +} + +#endif diff --git a/src/laybasic/laybasic/D25TechnologyComponentEditor.ui b/src/laybasic/laybasic/D25TechnologyComponentEditor.ui new file mode 100644 index 000000000..5a8b631cd --- /dev/null +++ b/src/laybasic/laybasic/D25TechnologyComponentEditor.ui @@ -0,0 +1,38 @@ + + + D25TechnologyComponentEditor + + + + 0 + 0 + 549 + 434 + + + + Settings + + + + + + 2.5d Vertical Stack Information + + + + + + + + Monospace + + + + + + + + + + diff --git a/src/laybasic/laybasic/layD25TechnologyComponent.cc b/src/laybasic/laybasic/layD25TechnologyComponent.cc new file mode 100644 index 000000000..4935360f1 --- /dev/null +++ b/src/laybasic/laybasic/layD25TechnologyComponent.cc @@ -0,0 +1,93 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "laybasicConfig.h" +#include "dbD25TechnologyComponent.h" +#include "layD25TechnologyComponent.h" + +#include +#include + +namespace lay +{ + +D25TechnologyComponentEditor::D25TechnologyComponentEditor (QWidget *parent) + : TechnologyComponentEditor (parent) +{ + setupUi (this); + + // TODO: activate_help_links (mp_ui->help_label); + + QResource res (tl::to_qstring (":/syntax/d25_text.xml")); + QByteArray data ((const char *) res.data (), int (res.size ())); + if (res.isCompressed ()) { + data = qUncompress (data); + } + + QBuffer input (&data); + input.open (QIODevice::ReadOnly); + mp_hl_basic_attributes.reset (new GenericSyntaxHighlighterAttributes ()); + mp_hl_attributes.reset (new GenericSyntaxHighlighterAttributes (mp_hl_basic_attributes.get ())); + lay::GenericSyntaxHighlighter *hl = new GenericSyntaxHighlighter (src_te, input, mp_hl_attributes.get ()); + input.close (); + + hl->setDocument (src_te->document ()); +} + +void +D25TechnologyComponentEditor::commit () +{ + db::D25TechnologyComponent *data = dynamic_cast (tech_component ()); + if (! data) { + return; + } + + std::string src = tl::to_string (src_te->toPlainText ()); + data->compile_from_source (src); +} + +void +D25TechnologyComponentEditor::setup () +{ + db::D25TechnologyComponent *data = dynamic_cast (tech_component ()); + if (! data) { + return; + } + + src_te->setPlainText (tl::to_qstring (data->src ())); +} + +class D25TechnologyComponentEditorProvider + : public lay::TechnologyEditorProvider +{ +public: + virtual lay::TechnologyComponentEditor *create_editor (QWidget *parent) const + { + return new D25TechnologyComponentEditor (parent); + } +}; + +static tl::RegisteredClass editor_decl (new D25TechnologyComponentEditorProvider (), 3100, "d25"); + +} // namespace lay + diff --git a/src/laybasic/laybasic/layD25TechnologyComponent.h b/src/laybasic/laybasic/layD25TechnologyComponent.h new file mode 100644 index 000000000..aeec28b7e --- /dev/null +++ b/src/laybasic/laybasic/layD25TechnologyComponent.h @@ -0,0 +1,54 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_layD25TechnologyComponent +#define HDR_layD25TechnologyComponent + +#include "ui_D25TechnologyComponentEditor.h" +#include "layTechnology.h" +#include "layGenericSyntaxHighlighter.h" + +#include + +namespace lay { + +class D25TechnologyComponentEditor + : public lay::TechnologyComponentEditor, + public Ui::D25TechnologyComponentEditor +{ +Q_OBJECT + +public: + D25TechnologyComponentEditor (QWidget *parent); + + void commit (); + void setup (); + +private: + std::auto_ptr mp_hl_attributes, mp_hl_basic_attributes; +}; + +} + +#endif + diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index 7a7c8a92e..42c128414 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -73,7 +73,8 @@ FORMS = \ NetInfoDialog.ui \ NetExportDialog.ui \ SelectCellViewForm.ui \ - LayoutStatistics.ui + LayoutStatistics.ui \ + D25TechnologyComponentEditor.ui RESOURCES = \ laybasicResources.qrc \ @@ -183,7 +184,8 @@ SOURCES = \ layGenericSyntaxHighlighter.cc \ layDispatcher.cc \ laySelectCellViewForm.cc \ - layLayoutStatisticsForm.cc + layLayoutStatisticsForm.cc \ + layD25TechnologyComponent.cc HEADERS = \ gtf.h \ @@ -284,7 +286,8 @@ HEADERS = \ layGenericSyntaxHighlighter.h \ layDispatcher.h \ laySelectCellViewForm.h \ - layLayoutStatisticsForm.h + layLayoutStatisticsForm.h \ + layD25TechnologyComponent.h INCLUDEPATH += $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC $$LYM_INC DEPENDPATH += $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC $$LYM_INC diff --git a/src/laybasic/laybasic/laybasicResources.qrc b/src/laybasic/laybasic/laybasicResources.qrc index 86e08c975..f9243ccc4 100644 --- a/src/laybasic/laybasic/laybasicResources.qrc +++ b/src/laybasic/laybasic/laybasicResources.qrc @@ -45,5 +45,6 @@ images/icon_device_bjt_24.png images/icon_device_bjt_16.png syntax/ur_text.xml + syntax/d25_text.xml diff --git a/src/laybasic/laybasic/syntax/d25_text.xml b/src/laybasic/laybasic/syntax/d25_text.xml new file mode 100644 index 000000000..c845bf7b1 --- /dev/null +++ b/src/laybasic/laybasic/syntax/d25_text.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc b/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc index 7f92fcd62..c2042a2f4 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc @@ -108,7 +108,7 @@ public: } }; -static tl::RegisteredClass config_decl (new lay::D25PluginDeclaration (), 3000, "lay::D25Plugin"); +static tl::RegisteredClass config_decl (new lay::D25PluginDeclaration (), 3100, "lay::D25Plugin"); } From ca2b0cd96af9d321056c46a8c716a3bdea4ad5fb Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 17 Apr 2020 16:24:56 +0200 Subject: [PATCH 021/113] Added tests for tech component, syntax highlighter fixed. --- src/db/db/dbD25TechnologyComponent.cc | 31 ++++++-- src/db/db/dbD25TechnologyComponent.h | 2 + .../dbD25TechnologyComponentTests.cc | 70 +++++++++++++++++++ src/db/unit_tests/unit_tests.pro | 3 +- src/laybasic/laybasic/syntax/d25_text.xml | 6 +- 5 files changed, 104 insertions(+), 8 deletions(-) create mode 100644 src/db/unit_tests/dbD25TechnologyComponentTests.cc diff --git a/src/db/db/dbD25TechnologyComponent.cc b/src/db/db/dbD25TechnologyComponent.cc index 7e3742a5e..f710d829a 100644 --- a/src/db/db/dbD25TechnologyComponent.cc +++ b/src/db/db/dbD25TechnologyComponent.cc @@ -134,10 +134,11 @@ D25TechnologyComponent::D25TechnologyComponent () "# 'zstart' and 'zstop'.\n" "#\n" "# Examples:\n" - "# 1: 0.5 1.5 # extrude layer 1/0 from 0.5 to 1.5 vertically\n" - "# 1: zstop=1.5, zstart=0.5 # same as above\n" - "# 1: height=1.0, zstop=1.5 # same as above\n" - "# 1: 1.0 zstop=1.5 # same as above\n" + "# 1: 0.5 1.5 # extrude layer 1/0 from 0.5 to 1.5 vertically\n" + "# 1/0: 0.5 1.5 # same with explicit datatype\n" + "# 1: zstop=1.5, zstart=0.5 # same with named parameters\n" + "# 1: height=1.0, zstop=1.5 # same with z stop minus height\n" + "# 1: 1.0 zstop=1.5 # same with height as unnamed parameter\n" ; } @@ -186,6 +187,10 @@ D25TechnologyComponent::compile_from_source (const std::string &src) while (! ex.at_end ()) { + if (ex.test ("#")) { + break; + } + double pv = 0.0; std::string pn; @@ -227,6 +232,9 @@ D25TechnologyComponent::compile_from_source (const std::string &src) if (! h.is_nil ()) { info.set_zstop (info.zstart () + h.to_double ()); } + } else { + info.set_zstart (z0.to_double ()); + info.set_zstop (z1.to_double ()); } } else if (args.size () == 1) { info.set_zstop ((! z0.is_nil () ? z0.to_double () : info.zstart ()) + args[0]); @@ -250,6 +258,21 @@ D25TechnologyComponent::compile_from_source (const std::string &src) m_src = src; } +std::string +D25TechnologyComponent::to_string () const +{ + std::string res; + + for (const_iterator i = begin (); i != end (); ++i) { + if (! res.empty ()) { + res += "\n"; + } + res += i->layer ().to_string () + ": zstart=" + tl::to_string (i->zstart ()) + ", zstop=" + tl::to_string (i->zstop ()); + } + + return res; +} + // ----------------------------------------------------------------------------------- // D25TechnologyComponent technology component registration diff --git a/src/db/db/dbD25TechnologyComponent.h b/src/db/db/dbD25TechnologyComponent.h index 8d16796e0..4c31363c1 100644 --- a/src/db/db/dbD25TechnologyComponent.h +++ b/src/db/db/dbD25TechnologyComponent.h @@ -137,6 +137,8 @@ public: m_src = s; } + std::string to_string () const; + db::TechnologyComponent *clone () const { return new D25TechnologyComponent (*this); diff --git a/src/db/unit_tests/dbD25TechnologyComponentTests.cc b/src/db/unit_tests/dbD25TechnologyComponentTests.cc new file mode 100644 index 000000000..3dc872953 --- /dev/null +++ b/src/db/unit_tests/dbD25TechnologyComponentTests.cc @@ -0,0 +1,70 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "dbD25TechnologyComponent.h" +#include "tlUnitTest.h" + + +TEST(1) +{ + db::D25TechnologyComponent comp; + + comp.compile_from_source ("1/0: 1.0 1.5 # a comment"); + EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); + + comp.compile_from_source ("1/0: zstart=1.0 zstop=1.5"); + EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); + + comp.compile_from_source ("1/0: zstart=1.0 height=0.5"); + EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); + + comp.compile_from_source ("1/0: zstop=1.5 height=0.5"); + EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); + + comp.compile_from_source ("1/0: zstart=1.0 zstop=1.5\nname: height=3"); + EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5\nname: zstart=1.5, zstop=4.5"); + + comp.compile_from_source ("1/0: zstart=1.0 zstop=1.5\nname: zstart=4.0 height=3\n\n# a comment line"); + EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5\nname: zstart=4, zstop=7"); + + try { + comp.compile_from_source ("blabla"); + EXPECT_EQ (false, true); + } catch (...) { } + + try { + comp.compile_from_source ("1/0: 1 2 3"); + EXPECT_EQ (false, true); + } catch (...) { } + + try { + comp.compile_from_source ("1/0: foo=1 bar=2"); + EXPECT_EQ (false, true); + } catch (...) { } + + try { + comp.compile_from_source ("1/0: 1;2"); + EXPECT_EQ (false, true); + } catch (...) { } +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 5aa37d85e..f69577d0a 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -73,7 +73,8 @@ SOURCES = \ dbLayoutQueryTests.cc \ dbPolygonToolsTests.cc \ dbTechnologyTests.cc \ - dbStreamLayerTests.cc + dbStreamLayerTests.cc \ + dbD25TechnologyComponentTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC diff --git a/src/laybasic/laybasic/syntax/d25_text.xml b/src/laybasic/laybasic/syntax/d25_text.xml index c845bf7b1..654dd7c93 100644 --- a/src/laybasic/laybasic/syntax/d25_text.xml +++ b/src/laybasic/laybasic/syntax/d25_text.xml @@ -15,7 +15,7 @@ - + @@ -46,13 +46,13 @@ - + - + From 1604c089dc9be1f894a690013447fe047769fc45 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 17 Apr 2020 19:02:13 +0200 Subject: [PATCH 022/113] Link between tech comp and 2.5d view --- .../tools/view_25d/lay_plugin/layD25View.cc | 12 +- .../view_25d/lay_plugin/layD25ViewWidget.cc | 106 +++++++++++++++--- .../view_25d/lay_plugin/layD25ViewWidget.h | 4 +- .../unit_tests/layD25ViewUtilsTests.cc | 16 ++- 4 files changed, 119 insertions(+), 19 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc index 158e19f3f..f10ae46ac 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -89,7 +89,16 @@ int D25View::exec_dialog (lay::LayoutView *view) { mp_view.reset (view); - mp_ui->d25_view->attach_view (view); + bool any = mp_ui->d25_view->attach_view (view); + + if (! any) { + + mp_view.reset (0); + mp_ui->d25_view->attach_view (0); + + throw tl::Exception (tl::to_string (tr ("No z data configured for the layers in the view.\nUse \"Tools/Manage Technologies\" to set up a z stack."))); + + } mp_ui->d25_view->reset (); mp_ui->d25_view->set_cam_azimuth (0.0); @@ -98,6 +107,7 @@ D25View::exec_dialog (lay::LayoutView *view) int ret = QDialog::exec (); mp_ui->d25_view->attach_view (0); + mp_view.reset (0); return ret; } diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index 48ca09105..52fbd46ca 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -26,6 +26,7 @@ #include "layLayoutView.h" #include "dbRecursiveShapeIterator.h" +#include "dbD25TechnologyComponent.h" #include "dbEdgeProcessor.h" #include "dbPolygonGenerators.h" #include "dbPolygonTools.h" @@ -412,20 +413,88 @@ D25ViewWidget::aspect_ratio () const return double (width ()) / double (height ()); } -void +bool D25ViewWidget::attach_view (LayoutView *view) { + bool any = false; + if (mp_view != view) { mp_view = view; - prepare_view (); + any = prepare_view (); reset (); } + + return any; } -void +namespace { + + class ZDataCache + { + public: + ZDataCache () { } + + const db::D25LayerInfo *operator() (lay::LayoutView *view, int cv_index, int layer_index) + { + std::map >::const_iterator c = m_cache.find (cv_index); + if (c != m_cache.end ()) { + std::map::const_iterator l = c->second.find (layer_index); + if (l != c->second.end ()) { + return l->second; + } else { + return 0; + } + } + + std::map &lcache = m_cache [cv_index]; + + const db::D25TechnologyComponent *comp = 0; + + const lay::CellView &cv = view->cellview (cv_index); + if (cv.is_valid () && cv->technology ()) { + const db::Technology *tech = cv->technology (); + comp = dynamic_cast (tech->component_by_name ("d25")); + } + + if (comp) { + + std::map zi_by_lp; + for (db::D25TechnologyComponent::const_iterator i = comp->begin (); i != comp->end (); ++i) { + zi_by_lp.insert (std::make_pair (i->layer (), i.operator-> ())); + } + + const db::Layout &ly = cv->layout (); + for (int l = 0; l < int (ly.layers ()); ++l) { + if (ly.is_valid_layer (l)) { + const db::LayerProperties &lp = ly.get_properties (l); + std::map::const_iterator z = zi_by_lp.find (lp); + if (z == zi_by_lp.end () && ! lp.name.empty ()) { + // If possible, try by name only + z = zi_by_lp.find (db::LayerProperties (lp.name)); + } + if (z != zi_by_lp.end ()) { + lcache[l] = z->second; + } + } + } + + } + + return operator() (view, cv_index, layer_index); + + } + + + private: + std::map > m_cache; + }; + +} + +bool D25ViewWidget::prepare_view () { m_layers.clear (); @@ -437,14 +506,25 @@ D25ViewWidget::prepare_view () if (! mp_view) { m_bbox = db::DBox (-1.0, -1.0, 1.0, 1.0); - return; + return false; } - double z = 0.0, dz = 0.2; // @@@ + ZDataCache zdata; + bool any = false; for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) { - if (! lp->has_children () && lp->visible (true) && lp->cellview_index () >= 0 && lp->cellview_index () < int (mp_view->cellviews ())) { + const db::D25LayerInfo *zi = 0; + if (! lp->has_children () && lp->visible (true)) { + zi = zdata (mp_view, lp->cellview_index (), lp->layer_index ()); + } + + if (zi) { + + any = true; + + double z0 = zi->zstart (); + double z1 = zi->zstop (); lay::color_t color = lp->fill_color (true); @@ -459,24 +539,24 @@ D25ViewWidget::prepare_view () m_layers.push_back (info); const lay::CellView &cv = mp_view->cellview ((unsigned int) lp->cellview_index ()); - render_layout (m_vertex_chunks.back (), cv->layout (), *cv.cell (), (unsigned int) lp->layer_index (), z, z + dz); + render_layout (m_vertex_chunks.back (), cv->layout (), *cv.cell (), (unsigned int) lp->layer_index (), z0, z1); m_bbox += db::DBox (cv.cell ()->bbox ((unsigned int) lp->layer_index ())) * cv->layout ().dbu (); if (! zset) { - m_zmin = z; - m_zmax = z + dz; + m_zmin = z0; + m_zmax = z1; zset = true; } else { - m_zmin = std::min (z, m_zmin); - m_zmax = std::max (z + dz, m_zmax); + m_zmin = std::min (z0, m_zmin); + m_zmax = std::max (z1, m_zmax); } - z += dz; // @@@ - } } + + return any; } void diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h index ea6d5c790..e23b068a5 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h @@ -82,7 +82,7 @@ public: void mouseReleaseEvent (QMouseEvent *event); void mouseMoveEvent (QMouseEvent *event); - void attach_view (lay::LayoutView *view); + bool attach_view(lay::LayoutView *view); QVector3D hit_point_with_scene(const QVector3D &line_dir); void refresh (); @@ -138,7 +138,7 @@ private: void paintGL (); void resizeGL (int w, int h); - void prepare_view (); + bool prepare_view(); void render_layout (D25ViewWidget::chunks_type &chunks, const db::Layout &layout, const db::Cell &cell, unsigned int layer, double zstart, double zstop); void render_polygon (D25ViewWidget::chunks_type &chunks, const db::Polygon &poly, double dbu, double zstart, double zstop); void render_wall (D25ViewWidget::chunks_type &chunks, const db::Edge &poly, double dbu, double zstart, double zstop); diff --git a/src/plugins/tools/view_25d/unit_tests/layD25ViewUtilsTests.cc b/src/plugins/tools/view_25d/unit_tests/layD25ViewUtilsTests.cc index b24265b22..4e428c818 100644 --- a/src/plugins/tools/view_25d/unit_tests/layD25ViewUtilsTests.cc +++ b/src/plugins/tools/view_25d/unit_tests/layD25ViewUtilsTests.cc @@ -92,7 +92,8 @@ TEST(3_HitWithCuboid) EXPECT_EQ (v2s (r.second), "1,1,4"); r = lay::hit_point_with_cuboid (QVector3D (1, 1, 6), QVector3D (0, 0, 1), QVector3D (-1, -1, 3), QVector3D (2, 2, 2)); - EXPECT_EQ (r.first, false); + EXPECT_EQ (r.first, true); + EXPECT_EQ (v2s (r.second), "1,1,3"); r = lay::hit_point_with_cuboid (QVector3D (5, -6, 0), QVector3D (0, 0, 1), QVector3D (-1, -1, 3), QVector3D (2, 2, 2)); EXPECT_EQ (r.first, true); @@ -100,10 +101,19 @@ TEST(3_HitWithCuboid) r = lay::hit_point_with_cuboid (QVector3D (5, -6, 4), QVector3D (0, 0, 1), QVector3D (-1, -1, 3), QVector3D (2, 2, 2)); EXPECT_EQ (r.first, true); - EXPECT_EQ (v2s (r.second), "5,-6,5"); + EXPECT_EQ (v2s (r.second), "5,-6,3"); r = lay::hit_point_with_cuboid (QVector3D (5, -6, 6), QVector3D (0, 0, 1), QVector3D (-1, -1, 3), QVector3D (2, 2, 2)); - EXPECT_EQ (r.first, false); + EXPECT_EQ (r.first, true); + EXPECT_EQ (v2s (r.second), "5,-6,3"); + + r = lay::hit_point_with_cuboid (QVector3D (5, 0, 0), QVector3D (-1, 0, 0), QVector3D (-1, -1, 3), QVector3D (2, 2, 2)); + EXPECT_EQ (r.first, true); + EXPECT_EQ (v2s (r.second), "1,0,0"); + + r = lay::hit_point_with_cuboid (QVector3D (-5, 0, 0), QVector3D (1, 0, 0), QVector3D (-1, -1, 3), QVector3D (2, 2, 2)); + EXPECT_EQ (r.first, true); + EXPECT_EQ (v2s (r.second), "-1,0,0"); } TEST(4_CameraNormal) From 747184beef4f66633a674a41368f9a467f3acb4c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 17 Apr 2020 23:46:02 +0200 Subject: [PATCH 023/113] Using background color as configured. --- .../view_25d/lay_plugin/layD25ViewWidget.cc | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index 52fbd46ca..01d5afdec 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -192,7 +192,6 @@ D25ViewWidget::D25ViewWidget (QWidget *parent) format.setDepthBufferSize (24); format.setSamples (4); // more -> widget extends beyond boundary! format.setStencilBufferSize (8); - // @@@? format.setVersion (3, 2); format.setProfile (QSurfaceFormat::CoreProfile); setFormat (format); @@ -394,10 +393,6 @@ D25ViewWidget::fit () void D25ViewWidget::refresh () { - QVector3D cp = cam_position (); - -printf("@@@ e=%g a=%g x,y,z=%g,%g,%g d=%g,%g,%g s=%g\n", cam_elevation (), cam_azimuth (), cp.x(), cp.y(), cp.z(), m_displacement.x(), m_displacement.y(), m_displacement.z(), m_scale_factor); fflush(stdout); - update (); } @@ -621,7 +616,7 @@ D25ViewWidget::render_layout (D25ViewWidget::chunks_type &chunks, const db::Layo db::EdgeProcessor ep; std::vector poly_heap; - // @@@ hidden cells, hierarchy depth ... + // TODO: hidden cells, hierarchy depth ... db::RecursiveShapeIterator s (layout, cell, layer); s.shape_flags (db::ShapeIterator::Polygons | db::ShapeIterator::Paths | db::ShapeIterator::Boxes); for ( ; ! s.at_end (); ++s) { @@ -703,10 +698,7 @@ D25ViewWidget::initializeGL () QOpenGLFunctions::initializeOpenGLFunctions(); glEnable (GL_BLEND); - // @@@ dark background glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - // @@@ white background - // @@@ glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA); static const char *shapes_vertex_shader_source = "#version 320 es\n" @@ -757,9 +749,12 @@ D25ViewWidget::initializeGL () "#undef mediump\n" "in lowp vec4 vertexColor;\n" "out lowp vec4 fragColor;\n" + "uniform highp float mist_factor;\n" + "uniform highp float mist_add;\n" "\n" "vec4 color_by_z(lowp vec4 c, highp float z) {\n" - " lowp vec4 mist_color = vec4(c.g * 0.4, c.g * 0.4, c.g * 0.4, 1.0);\n" + " highp float mist_rgb = c.g * mist_factor + mist_add;\n" + " lowp vec4 mist_color = vec4(mist_rgb, mist_rgb, mist_rgb, 1.0);\n" " highp float d = 0.12;\n" // d + dd/2 = 0.15 = 1/? " highp float dd = 0.06;\n" " highp float f = 1.0;\n" @@ -833,7 +828,13 @@ D25ViewWidget::paintGL () glViewport (0, 0, width () * retinaScale, height () * retinaScale); glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - // @@@ white background: glClearColor (1.0, 1.0, 1.0, 1.0); + + QColor c = mp_view->background_color (); + float foreground_rgb = (c.green () > 128 ? 0.0f : 1.0f); + float ambient = (c.green () > 128 ? 1.0f : 0.5f); + float mist_factor = (c.green () > 128 ? 0.2f : 0.4f); + float mist_add = (c.green () > 128 ? 0.8f : 0.2f); + glClearColor (float (c.red ()) / 255.0f, float (c.green ()) / 255.0f, float (c.blue ()) / 255.0f, 1.0); QMatrix4x4 scene_trans, scene_trans_wo_y; @@ -857,7 +858,9 @@ D25ViewWidget::paintGL () // NOTE: z axis of illum points towards the scene because we include the z inversion in the scene transformation matrix m_shapes_program->setUniformValue ("illum", QVector3D (-3.0, -4.0, 2.0).normalized ()); - m_shapes_program->setUniformValue ("ambient", QVector4D (0.5, 0.5, 0.5, 0.5)); + m_shapes_program->setUniformValue ("ambient", QVector4D (ambient, ambient, ambient, 0.5)); + m_shapes_program->setUniformValue ("mist_factor", mist_factor); + m_shapes_program->setUniformValue ("mist_add", mist_add); glEnable (GL_DEPTH_TEST); glEnableVertexAttribArray (positions); @@ -912,7 +915,7 @@ D25ViewWidget::paintGL () } - m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, 0.25f); + m_gridplane_program->setUniformValue ("color", foreground_rgb, foreground_rgb, foreground_rgb, 0.25f); glLineWidth (2.0); vertexes.draw_to (this, positions, GL_LINES); @@ -949,7 +952,7 @@ D25ViewWidget::paintGL () for (int major = 0; major < 2; ++major) { - m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, major ? 0.25f : 0.15f); + m_gridplane_program->setUniformValue ("color", foreground_rgb, foreground_rgb, foreground_rgb, major ? 0.25f : 0.15f); double x, y; double step = (major ? gmajor : gminor); @@ -987,7 +990,7 @@ D25ViewWidget::paintGL () vertexes.add (r, 0.0, b); vertexes.add (r, 0.0, t); - m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, 0.1f); + m_gridplane_program->setUniformValue ("color", foreground_rgb, foreground_rgb, foreground_rgb, 0.1f); vertexes.draw_to (this, positions, GL_TRIANGLES); @@ -1045,7 +1048,7 @@ D25ViewWidget::paintGL () vertexes.add (-1.0, 1.0, -1.0); vertexes.add (1.0, 1.0, -1.0); - m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, 0.2f); + m_gridplane_program->setUniformValue ("color", foreground_rgb, foreground_rgb, foreground_rgb, 0.2f); vertexes.draw_to (this, positions, GL_LINES); @@ -1096,7 +1099,7 @@ D25ViewWidget::paintGL () vertexes.add (0.8, -0.8, 1.0); vertexes.add (0.8, -0.6, 1.0); - m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, 0.3f); + m_gridplane_program->setUniformValue ("color", foreground_rgb, foreground_rgb, foreground_rgb, 0.3f); vertexes.draw_to (this, positions, GL_TRIANGLES); From 07ecaac5989fcd408bcec7dfff17f1f540150bc8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 18 Apr 2020 00:17:17 +0200 Subject: [PATCH 024/113] Enhancements. --- src/plugins/tools/view_25d/lay_plugin/layD25View.cc | 3 +-- .../tools/view_25d/lay_plugin/layD25ViewWidget.cc | 9 ++++----- src/plugins/tools/view_25d/view_25d.pro | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc index f10ae46ac..94855b6e0 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -39,10 +39,8 @@ D25View::D25View (QWidget *parent) mp_ui = new Ui::D25View (); mp_ui->setupUi (this); - // @@@ should be an event filter? mp_ui->d25_view->setFocusPolicy (Qt::StrongFocus); mp_ui->d25_view->setFocus (); - // @@@ connect (mp_ui->fit_back, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); connect (mp_ui->fit_front, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); @@ -103,6 +101,7 @@ D25View::exec_dialog (lay::LayoutView *view) mp_ui->d25_view->reset (); mp_ui->d25_view->set_cam_azimuth (0.0); mp_ui->d25_view->set_cam_elevation (initial_elevation); + mp_ui->d25_view->fit (); int ret = QDialog::exec (); diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index 01d5afdec..8dbe74798 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -731,7 +731,7 @@ D25ViewWidget::initializeGL () " vec4 p2 = gl_in[2].gl_Position;\n" " vec3 n = cross(p2.xyz - p0.xyz, p1.xyz - p0.xyz);\n" " float dp = dot(normalize(n), illum);\n" - " vertexColor = color * (dp * 0.5 + 0.5) - (min(0.0, dp) * 0.5 * ambient);\n" + " vertexColor = color * (dp * 0.5 + 0.5) - (min(0.0, dp) * ambient);\n" " vertexColor.a = 1.0;\n" " gl_Position = matrix * p0;\n" " EmitVertex();\n" @@ -827,14 +827,13 @@ D25ViewWidget::paintGL () const qreal retinaScale = devicePixelRatio (); glViewport (0, 0, width () * retinaScale, height () * retinaScale); - glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - QColor c = mp_view->background_color (); float foreground_rgb = (c.green () > 128 ? 0.0f : 1.0f); - float ambient = (c.green () > 128 ? 1.0f : 0.5f); + float ambient = (c.green () > 128 ? 0.8f : 0.25f); float mist_factor = (c.green () > 128 ? 0.2f : 0.4f); float mist_add = (c.green () > 128 ? 0.8f : 0.2f); glClearColor (float (c.red ()) / 255.0f, float (c.green ()) / 255.0f, float (c.blue ()) / 255.0f, 1.0); + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); QMatrix4x4 scene_trans, scene_trans_wo_y; @@ -858,7 +857,7 @@ D25ViewWidget::paintGL () // NOTE: z axis of illum points towards the scene because we include the z inversion in the scene transformation matrix m_shapes_program->setUniformValue ("illum", QVector3D (-3.0, -4.0, 2.0).normalized ()); - m_shapes_program->setUniformValue ("ambient", QVector4D (ambient, ambient, ambient, 0.5)); + m_shapes_program->setUniformValue ("ambient", QVector4D (ambient, ambient, ambient, 1.0)); m_shapes_program->setUniformValue ("mist_factor", mist_factor); m_shapes_program->setUniformValue ("mist_add", mist_add); diff --git a/src/plugins/tools/view_25d/view_25d.pro b/src/plugins/tools/view_25d/view_25d.pro index 1f10c85ca..30278d0c8 100644 --- a/src/plugins/tools/view_25d/view_25d.pro +++ b/src/plugins/tools/view_25d/view_25d.pro @@ -1,6 +1,6 @@ TEMPLATE = subdirs -!equals(HAVE_QT, "0") { +equals(HAVE_QT5, "1") { SUBDIRS = lay_plugin unit_tests } From 2cdcf621d8fbb3d4df240d8e30a4541304ae7b68 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 18 Apr 2020 00:37:15 +0200 Subject: [PATCH 025/113] D25 tech component editor: line numbers shown, more syntax variants --- src/db/db/dbD25TechnologyComponent.cc | 25 ++++++++++++++++++- .../dbD25TechnologyComponentTests.cc | 3 +++ .../laybasic/D25TechnologyComponentEditor.ui | 22 +++++++++++++--- .../laybasic/layD25TechnologyComponent.cc | 9 +++++++ .../laybasic/layD25TechnologyComponent.h | 3 +++ src/laybasic/laybasic/syntax/d25_text.xml | 2 +- 6 files changed, 59 insertions(+), 5 deletions(-) diff --git a/src/db/db/dbD25TechnologyComponent.cc b/src/db/db/dbD25TechnologyComponent.cc index f710d829a..1ab0ed2ea 100644 --- a/src/db/db/dbD25TechnologyComponent.cc +++ b/src/db/db/dbD25TechnologyComponent.cc @@ -237,8 +237,31 @@ D25TechnologyComponent::compile_from_source (const std::string &src) info.set_zstop (z1.to_double ()); } } else if (args.size () == 1) { - info.set_zstop ((! z0.is_nil () ? z0.to_double () : info.zstart ()) + args[0]); + if (! h.is_nil ()) { + if (! z0.is_nil ()) { + throw tl::Exception (tl::to_string (tr ("Rundundant parameters: zstart already given"))); + } + if (! z1.is_nil ()) { + throw tl::Exception (tl::to_string (tr ("Rundundant parameters: zstop implicitly given"))); + } + info.set_zstart (args[0]); + info.set_zstop (args[0] + h.to_double ()); + } else { + if (! z1.is_nil ()) { + throw tl::Exception (tl::to_string (tr ("Rundundant parameters: zstop implicitly given"))); + } + info.set_zstop ((! z0.is_nil () ? z0.to_double () : info.zstart ()) + args[0]); + } } else if (args.size () == 2) { + if (! z0.is_nil ()) { + throw tl::Exception (tl::to_string (tr ("Rundundant parameters: zstart already given"))); + } + if (! z1.is_nil ()) { + throw tl::Exception (tl::to_string (tr ("Rundundant parameters: zstop already given"))); + } + if (! h.is_nil ()) { + throw tl::Exception (tl::to_string (tr ("Rundundant parameters: height implicitly given"))); + } info.set_zstart (args[0]); info.set_zstop (args[1]); } else { diff --git a/src/db/unit_tests/dbD25TechnologyComponentTests.cc b/src/db/unit_tests/dbD25TechnologyComponentTests.cc index 3dc872953..181ccb92a 100644 --- a/src/db/unit_tests/dbD25TechnologyComponentTests.cc +++ b/src/db/unit_tests/dbD25TechnologyComponentTests.cc @@ -39,6 +39,9 @@ TEST(1) comp.compile_from_source ("1/0: zstart=1.0 height=0.5"); EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); + comp.compile_from_source ("1/0: 1.0 height=0.5"); + EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); + comp.compile_from_source ("1/0: zstop=1.5 height=0.5"); EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); diff --git a/src/laybasic/laybasic/D25TechnologyComponentEditor.ui b/src/laybasic/laybasic/D25TechnologyComponentEditor.ui index 5a8b631cd..b1901ba63 100644 --- a/src/laybasic/laybasic/D25TechnologyComponentEditor.ui +++ b/src/laybasic/laybasic/D25TechnologyComponentEditor.ui @@ -13,15 +13,31 @@ Settings - - + + 2.5d Vertical Stack Information - + + + + + 0 + 0 + + + + Line + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + diff --git a/src/laybasic/laybasic/layD25TechnologyComponent.cc b/src/laybasic/laybasic/layD25TechnologyComponent.cc index 4935360f1..c004b4a1f 100644 --- a/src/laybasic/laybasic/layD25TechnologyComponent.cc +++ b/src/laybasic/laybasic/layD25TechnologyComponent.cc @@ -52,6 +52,15 @@ D25TechnologyComponentEditor::D25TechnologyComponentEditor (QWidget *parent) input.close (); hl->setDocument (src_te->document ()); + + connect (src_te, SIGNAL (cursorPositionChanged ()), this, SLOT (cursor_position_changed ())); +} + +void +D25TechnologyComponentEditor::cursor_position_changed () +{ + int line = src_te->textCursor ().block ().firstLineNumber () + 1; + lnum_label->setText (tl::to_qstring (tl::sprintf (tl::to_string (tr ("Line %d")), line))); } void diff --git a/src/laybasic/laybasic/layD25TechnologyComponent.h b/src/laybasic/laybasic/layD25TechnologyComponent.h index aeec28b7e..206838b70 100644 --- a/src/laybasic/laybasic/layD25TechnologyComponent.h +++ b/src/laybasic/laybasic/layD25TechnologyComponent.h @@ -44,6 +44,9 @@ public: void commit (); void setup (); +private slots: + void cursor_position_changed (); + private: std::auto_ptr mp_hl_attributes, mp_hl_basic_attributes; }; diff --git a/src/laybasic/laybasic/syntax/d25_text.xml b/src/laybasic/laybasic/syntax/d25_text.xml index 654dd7c93..5e0877b74 100644 --- a/src/laybasic/laybasic/syntax/d25_text.xml +++ b/src/laybasic/laybasic/syntax/d25_text.xml @@ -63,7 +63,7 @@ - + From d117e024f08f44a190fa6fe81c22d5493fdf0a99 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 18 Apr 2020 00:38:25 +0200 Subject: [PATCH 026/113] Removed TODO markers --- src/plugins/tools/view_25d/lay_plugin/layD25Camera.cc | 4 ++-- src/plugins/tools/view_25d/lay_plugin/layD25View.cc | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25Camera.cc b/src/plugins/tools/view_25d/lay_plugin/layD25Camera.cc index 3d8fb12ef..f2d358c8d 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25Camera.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25Camera.cc @@ -58,13 +58,13 @@ D25Camera::camera_reset () double D25Camera::cam_fov () const { - return m_fov; // @@@ + return m_fov; } double D25Camera::cam_dist () const { - return 4.0; // @@@ + return 4.0; } QVector3D diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc index 94855b6e0..fd8d57bac 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -145,7 +145,6 @@ void D25View::accept () { QDialog::accept (); - // @@@ } } From 374c8d2c4d6fed3db6e4426056c5246e8bb6c7b8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 2 Aug 2020 17:34:07 +0200 Subject: [PATCH 027/113] Some refactoring: pulled out the layout functionality from the layout view and put into a plugin. --- src/laybasic/laybasic/layEditable.cc | 8 + src/laybasic/laybasic/layEditable.h | 7 + src/laybasic/laybasic/layLayoutView.cc | 2198 +--------------- src/laybasic/laybasic/layLayoutView.h | 118 +- .../laybasic/layLayoutViewFunctions.cc | 2249 +++++++++++++++++ .../laybasic/layLayoutViewFunctions.h | 171 ++ src/laybasic/laybasic/laybasic.pro | 6 +- 7 files changed, 2466 insertions(+), 2291 deletions(-) create mode 100644 src/laybasic/laybasic/layLayoutViewFunctions.cc create mode 100644 src/laybasic/laybasic/layLayoutViewFunctions.h diff --git a/src/laybasic/laybasic/layEditable.cc b/src/laybasic/laybasic/layEditable.cc index c2ef0fa38..8b75008f1 100644 --- a/src/laybasic/laybasic/layEditable.cc +++ b/src/laybasic/laybasic/layEditable.cc @@ -444,6 +444,14 @@ Editables::select (const db::DPoint &pt, lay::Editable::SelectionMode mode) signal_selection_changed (); } +void +Editables::repeat_selection (Editable::SelectionMode mode) +{ + if (m_last_selected_point.is_point ()) { + select (m_last_selected_point, mode); + } +} + bool Editables::begin_move (const db::DPoint &p, lay::angle_constraint_type ac) { diff --git a/src/laybasic/laybasic/layEditable.h b/src/laybasic/laybasic/layEditable.h index a5d6831e7..b60ed500a 100644 --- a/src/laybasic/laybasic/layEditable.h +++ b/src/laybasic/laybasic/layEditable.h @@ -493,6 +493,13 @@ public: */ void select (const db::DPoint &pt, Editable::SelectionMode mode); + /** + * @brief Repeat the previous selection + * + * This method will not do anything if there is no previous, click-at selection. + */ + void repeat_selection (Editable::SelectionMode mode); + /** * @brief Start "move" operation * diff --git a/src/laybasic/laybasic/layLayoutView.cc b/src/laybasic/laybasic/layLayoutView.cc index f7ae519f5..62a8566a3 100644 --- a/src/laybasic/laybasic/layLayoutView.cc +++ b/src/laybasic/laybasic/layLayoutView.cc @@ -46,14 +46,12 @@ #include "layLayoutView.h" #include "layViewOp.h" #include "layViewObject.h" -#include "layLayoutViewConfigPages.h" #include "laybasicConfig.h" #include "layConverters.h" #include "layGridNet.h" #include "layMove.h" #include "layZoomBox.h" #include "layMouseTracker.h" -#include "layTipDialog.h" #include "layEditable.h" #include "layFixedFont.h" #include "laySelector.h" @@ -68,16 +66,10 @@ #include "layBookmarkManagementForm.h" #include "layNetlistBrowserDialog.h" #include "layBookmarksView.h" -#include "laySelectCellViewForm.h" -#include "layCellSelectionForm.h" -#include "layLayoutPropertiesForm.h" -#include "layLayoutStatisticsForm.h" #include "dbClipboard.h" #include "dbLayout.h" #include "dbLayoutUtils.h" -#include "dbRecursiveShapeIterator.h" #include "dbManager.h" -#include "dbEdgeProcessor.h" #include "dbLibrary.h" #include "rdb.h" #include "rdbMarkerBrowserDialog.h" @@ -460,20 +452,10 @@ LayoutView::init (db::Manager *mgr, QWidget * /*parent*/) m_marker_halo = true; m_transient_selection_mode = true; m_sel_inside_pcells = false; - m_move_to_origin_mode_x = 0; - m_move_to_origin_mode_y = 0; - m_del_cell_mode = 0; - m_layer_hier_mode = 0; m_add_other_layers = false; m_always_show_source = false; m_always_show_ld = true; m_always_show_layout_index = false; - m_duplicate_hier_mode = 2; - m_clear_before = true; - m_copy_cva = -1; - m_copy_cvr = -1; - m_copy_layera = -1; - m_copy_layerr = -1; m_search_range = 5; m_layer_properties_lists.push_back (new LayerPropertiesList ()); @@ -616,9 +598,6 @@ LayoutView::init (db::Manager *mgr, QWidget * /*parent*/) create_plugins (); - m_new_layer_props.layer = 1; - m_new_layer_props.datatype = 0; - config_setup (); } @@ -5272,923 +5251,6 @@ LayoutView::cut () } } -void -LayoutView::cm_align_cell_origin () -{ - int cv_index = active_cellview_index (); - if (cv_index >= 0) { - - const db::Cell *cell = cellview (cv_index).cell (); - if (! cell) { - return; - } - if (cell->is_proxy ()) { - throw tl::Exception (tl::to_string (QObject::tr ("Cannot use this function on a PCell or library cell"))); - } - - lay::AlignCellOptionsDialog dialog (this); - if (dialog.exec_dialog (m_align_cell_options)) { - - clear_selection (); - - transaction (tl::to_string (QObject::tr ("Align cell origin"))); - - db::Box bbox; - - if (m_align_cell_options.visible_only) { - for (lay::LayerPropertiesConstIterator l = begin_layers (); !l.at_end (); ++l) { - if (! l->has_children () && l->layer_index () >= 0 && l->cellview_index () == cv_index && l->visible (true /*real*/)) { - bbox += cell->bbox (l->layer_index ()); - } - } - } else { - bbox = cell->bbox (); - } - - db::Coord refx, refy; - switch (m_align_cell_options.mode_x) { - case -1: - refx = bbox.left (); - break; - case 1: - refx = bbox.right (); - break; - default: - refx = bbox.center ().x (); - break; - } - switch (m_align_cell_options.mode_y) { - case -1: - refy = bbox.bottom (); - break; - case 1: - refy = bbox.top (); - break; - default: - refy = bbox.center ().y (); - break; - } - - db::Layout &layout = cellview (cv_index)->layout (); - db::Cell &nc_cell = layout.cell (cell->cell_index ()); - - db::Trans t (db::Vector (-refx + db::coord_traits::rounded (m_align_cell_options.xpos / layout.dbu ()), -refy + db::coord_traits::rounded (m_align_cell_options.ypos / layout.dbu ()))); - - for (unsigned int i = 0; i < layout.layers (); ++i) { - if (layout.is_valid_layer (i)) { - db::Shapes &shapes = nc_cell.shapes (i); - for (db::Shapes::shape_iterator s = shapes.begin (db::ShapeIterator::All); ! s.at_end (); ++s) { - shapes.transform (*s, t); - } - } - } - - for (db::Cell::const_iterator inst = nc_cell.begin (); ! inst.at_end (); ++inst) { - nc_cell.transform (*inst, t); - } - - if (m_align_cell_options.adjust_parents) { - - std::vector > insts_to_modify; - for (db::Cell::parent_inst_iterator pi = nc_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { - insts_to_modify.push_back (std::make_pair (& layout.cell (pi->parent_cell_index ()), pi->child_inst ())); - } - - db::Trans ti (db::Vector (refx, refy)); - for (std::vector >::const_iterator im = insts_to_modify.begin (); im != insts_to_modify.end (); ++im) { - im->first->transform (im->second, db::Trans (db::Vector (im->second.complex_trans ().trans (db::Vector (refx, refy))))); - } - - } - - commit (); - - } - - } -} - -void -LayoutView::cm_cell_user_properties () -{ - if (! mp_hierarchy_panel) { - return; - } - - int cv_index = active_cellview_index (); - cell_path_type path; - mp_hierarchy_panel->current_cell (cv_index, path); - - if (cv_index >= 0 && path.size () > 0) { - - db::Layout &layout = cellview (cv_index)->layout (); - db::Cell &cell = layout.cell (path.back ()); - db::properties_id_type prop_id = cell.prop_id (); - - lay::UserPropertiesForm props_form (this); - if (props_form.show (this, cv_index, prop_id)) { - - transaction (tl::to_string (QObject::tr ("Edit cell's user propertes"))); - cell.prop_id (prop_id); - commit (); - - } - - } -} - -void -LayoutView::cm_cell_replace () -{ - if (! mp_hierarchy_panel) { - return; - } - - int cv_index = active_cellview_index (); - std::vector paths; - mp_hierarchy_panel->selected_cells (cv_index, paths); - - if (cv_index >= 0 && paths.size () > 0) { - - if (paths.size () > 1) { - throw tl::Exception (tl::to_string (QObject::tr ("Replace cell cannot be used when multiple cells are selected"))); - } - - db::Layout &layout = cellview (cv_index)->layout (); - - bool needs_to_ask = false; - for (std::vector::const_iterator p = paths.begin (); p != paths.end () && ! needs_to_ask; ++p) { - if (layout.is_valid_cell_index (p->back ()) && ! layout.cell (p->back ()).is_leaf ()) { - needs_to_ask = true; - } - } - - - lay::ReplaceCellOptionsDialog mode_dialog (this); - - db::cell_index_type with_cell = paths.front ().back (); - int mode = needs_to_ask ? m_del_cell_mode : 0; - - if (mode_dialog.exec_dialog (cellview (cv_index), mode, with_cell)) { - - if (needs_to_ask) { - m_del_cell_mode = mode; - } - - if (with_cell != paths.front ().back ()) { - - // remember the current path - cell_path_type cell_path (cellview (cv_index).combined_unspecific_path ()); - - clear_selection (); - - transaction (tl::to_string (QObject::tr ("Replace cells"))); - - // replace instances of the target cell with the new cell - - db::Cell &target_cell = layout.cell (paths.front ().back ()); - - std::vector > parents; - for (db::Cell::parent_inst_iterator pi = target_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { - parents.push_back (std::make_pair (pi->parent_cell_index (), pi->child_inst ())); - } - - for (std::vector >::const_iterator p = parents.begin (); p != parents.end (); ++p) { - db::CellInstArray ia = p->second.cell_inst (); - ia.object ().cell_index (with_cell); - layout.cell (p->first).replace (p->second, ia); - } - - std::set cells_to_delete; - for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { - if (! p->empty () && layout.is_valid_cell_index (p->back ())) { - cells_to_delete.insert (p->back ()); - if (mode == 2) { - layout.cell (p->back ()).collect_called_cells (cells_to_delete); - } - } - } - - // support a propagation use case: - std::set cells_below_replacement_cell; - cells_below_replacement_cell.insert (with_cell); - layout.cell (with_cell).collect_called_cells (cells_below_replacement_cell); - for (std::set::const_iterator c = cells_below_replacement_cell.begin (); c != cells_below_replacement_cell.end (); ++c) { - cells_to_delete.erase (*c); - } - - if (mode == 0 || mode == 2) { - layout.delete_cells (cells_to_delete); - } else if (mode == 1) { - layout.prune_cells (cells_to_delete); - } - - layout.cleanup (); - - commit (); - - // If one of the cells in the path was deleted, establish a valid path - - bool needs_update = false; - for (size_t i = cell_path.size (); i > 0; ) { - --i; - if (! layout.is_valid_cell_index (cell_path [i])) { - cell_path.erase (cell_path.begin () + i, cell_path.end ()); - needs_update = true; - } - } - - if (needs_update) { - select_cell (cell_path, cv_index); - } - - } - - } - - } -} - -void -LayoutView::cm_lay_convert_to_static () -{ - // end move operations, cancel edit operations - cancel_edits (); - clear_selection (); - - int cv_index = active_cellview_index (); - if (cv_index >= 0) { - - db::Layout &layout = cellview (cv_index)->layout (); - - transaction (tl::to_string (QObject::tr ("Convert all cells to static"))); - - std::vector cells; - for (db::Layout::const_iterator c = layout.begin (); c != layout.end (); ++c) { - cells.push_back (c->cell_index ()); - } - - std::map cell_map; - for (std::vector::const_iterator c = cells.begin (); c != cells.end (); ++c) { - if (layout.is_valid_cell_index (*c)) { - db::cell_index_type new_cell = layout.convert_cell_to_static (*c); - if (new_cell != *c) { - cell_map.insert (std::make_pair (*c, new_cell)); - } - } - } - - // rewrite instances - for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { - for (db::Cell::const_iterator i = c->begin (); ! i.at_end (); ++i) { - std::map::const_iterator cm = cell_map.find (i->cell_index ()); - if (cm != cell_map.end ()) { - db::CellInstArray ci = i->cell_inst (); - ci.object ().cell_index (cm->second); - c->replace (*i, ci); - } - } - } - - layout.cleanup (); - - commit (); - - } -} - -void -LayoutView::cm_cell_convert_to_static () -{ - if (! mp_hierarchy_panel) { - return; - } - - int cv_index = active_cellview_index (); - std::vector paths; - mp_hierarchy_panel->selected_cells (cv_index, paths); - - if (cv_index >= 0 && paths.size () > 0) { - - db::Layout &layout = cellview (cv_index)->layout (); - - // remember the current path - cell_path_type cell_path (cellview (cv_index).combined_unspecific_path ()); - - clear_selection (); - - transaction (tl::to_string (QObject::tr ("Convert cells to static"))); - - std::map cell_map; - - for (std::vector::iterator p = paths.begin (); p != paths.end (); ++p) { - if (! p->empty () && layout.is_valid_cell_index (p->back ())) { - db::cell_index_type new_cell = layout.convert_cell_to_static (p->back ()); - if (new_cell != p->back ()) { - cell_map.insert (std::make_pair (p->back (), new_cell)); - p->back () = new_cell; - } - } - } - - // rewrite instances - for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { - for (db::Cell::const_iterator i = c->begin (); ! i.at_end (); ++i) { - std::map::const_iterator cm = cell_map.find (i->cell_index ()); - if (cm != cell_map.end ()) { - db::CellInstArray ci = i->cell_inst (); - ci.object ().cell_index (cm->second); - c->replace (*i, ci); - } - } - } - - layout.cleanup (); - - commit (); - - // If one of the cells in the path was deleted, establish a valid path - - bool needs_update = false; - for (size_t i = cell_path.size (); i > 0; ) { - --i; - if (! layout.is_valid_cell_index (cell_path [i])) { - cell_path.erase (cell_path.begin () + i, cell_path.end ()); - needs_update = true; - } - } - - if (needs_update) { - select_cell (cell_path, cv_index); - } - - } -} - -static void -collect_cells_to_delete (const db::Layout &layout, const db::Cell &cell, std::set &called) -{ - // don't delete proxies - they are deleted later when the layout is cleaned - for (db::Cell::child_cell_iterator cc = cell.begin_child_cells (); ! cc.at_end (); ++cc) { - if (called.find (*cc) == called.end () && !layout.cell (*cc).is_proxy ()) { - called.insert (*cc); - collect_cells_to_delete (layout, layout.cell (*cc), called); - } - } -} - -void -LayoutView::cm_cell_delete () -{ - if (! mp_hierarchy_panel) { - return; - } - - int cv_index = active_cellview_index (); - std::vector paths; - mp_hierarchy_panel->selected_cells (cv_index, paths); - - if (cv_index >= 0 && paths.size () > 0) { - - db::Layout &layout = cellview (cv_index)->layout (); - - bool needs_to_ask = false; - for (std::vector::const_iterator p = paths.begin (); p != paths.end () && ! needs_to_ask; ++p) { - if (layout.is_valid_cell_index (p->back ()) && ! layout.cell (p->back ()).is_leaf ()) { - needs_to_ask = true; - } - } - - int mode = m_del_cell_mode; - if (! needs_to_ask) { - mode = 0; - } - - lay::DeleteCellModeDialog mode_dialog (this); - if (! needs_to_ask || mode_dialog.exec_dialog (mode)) { - - if (needs_to_ask) { - m_del_cell_mode = mode; - } - - // remember the current path - cell_path_type cell_path (cellview (cv_index).combined_unspecific_path ()); - - clear_selection (); - - std::set cells_to_delete; - for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { - if (! p->empty () && layout.is_valid_cell_index (p->back ())) { - cells_to_delete.insert (p->back ()); - if (mode == 2) { - collect_cells_to_delete (layout, layout.cell (p->back ()), cells_to_delete); - } - } - } - - transaction (tl::to_string (QObject::tr ("Delete cells"))); - - if (mode == 0 || mode == 2) { - layout.delete_cells (cells_to_delete); - } else if (mode == 1) { - layout.prune_cells (cells_to_delete); - } - - layout.cleanup (); - - commit (); - - // If one of the cells in the path was deleted, establish a valid path - - bool needs_update = false; - for (size_t i = cell_path.size (); i > 0; ) { - --i; - if (! layout.is_valid_cell_index (cell_path [i])) { - cell_path.erase (cell_path.begin () + i, cell_path.end ()); - needs_update = true; - } - } - - if (needs_update) { - select_cell (cell_path, cv_index); - } - - } - - } -} - -void -LayoutView::cm_layer_copy () -{ - if (mp_control_panel) { - mp_control_panel->copy (); - } -} - -void -LayoutView::cm_layer_cut () -{ - if (mp_control_panel) { - db::Transaction trans (manager (), tl::to_string (QObject::tr ("Cut Layers"))); - mp_control_panel->cut (); - } -} - -void -LayoutView::cm_layer_paste () -{ - if (mp_control_panel) { - db::Transaction trans (manager (), tl::to_string (QObject::tr ("Paste Layers"))); - mp_control_panel->paste (); - } -} - -void -LayoutView::cm_cell_cut () -{ - if (mp_hierarchy_panel) { - // TODO: currently the hierarchy panel's cut function does it's own transaction handling. - // Otherwise the cut function is not working propertly. - mp_hierarchy_panel->cut (); - } -} - -void -LayoutView::cm_cell_paste () -{ - if (mp_hierarchy_panel) { - db::Transaction trans (manager (), tl::to_string (QObject::tr ("Paste Cells"))); - mp_hierarchy_panel->paste (); - } -} - -void -LayoutView::cm_cell_copy () -{ - if (mp_hierarchy_panel) { - mp_hierarchy_panel->copy (); - } -} - -void -LayoutView::cm_cell_flatten () -{ - if (! mp_hierarchy_panel) { - return; - } - - tl_assert (is_editable ()); - - int cv_index = active_cellview_index (); - if (cv_index >= 0) { - - const lay::CellView &cv = cellview (cv_index); - if (cv.is_valid ()) { - - std::vector paths; - mp_hierarchy_panel->selected_cells (cv_index, paths); - if (paths.empty ()) { - throw tl::Exception (tl::to_string (QObject::tr ("No cells selected for flattening"))); - } - - for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { - if (p->size () > 0 && cv->layout ().cell (p->back ()).is_proxy ()) { - throw tl::Exception (tl::to_string (QObject::tr ("Cannot use this function on a PCell or library cell"))); - } - } - - FlattenInstOptionsDialog options_dialog (this); - - int flatten_insts_levels = -1; - bool prune = true; - if (options_dialog.exec_dialog (flatten_insts_levels, prune) && flatten_insts_levels != 0) { - - bool supports_undo = true; - - if (manager () && manager ()->is_enabled ()) { - - lay::TipDialog td (QApplication::activeWindow (), - tl::to_string (QObject::tr ("Undo buffering for the following operation can be memory and time consuming.\nChoose \"Yes\" to use undo buffering or \"No\" for no undo buffering. Warning: in the latter case, the undo history will be lost.\n\nChoose undo buffering?")), - "flatten-undo-buffering", - lay::TipDialog::yesnocancel_buttons); - - lay::TipDialog::button_type button = lay::TipDialog::null_button; - td.exec_dialog (button); - if (button == lay::TipDialog::cancel_button) { - return; - } - - supports_undo = (button == lay::TipDialog::yes_button); - - } else { - supports_undo = false; - } - - cancel_edits (); - clear_selection (); - - if (manager ()) { - if (! supports_undo) { - manager ()->clear (); - } else { - manager ()->transaction (tl::to_string (QObject::tr ("Flatten cell"))); - } - } - - db::Layout &layout = cv->layout (); - - std::set child_cells; - for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { - if (p->size () > 0) { - layout.cell (p->back ()).collect_called_cells (child_cells); - } - } - - // don't flatten cells which are child cells of the cells to flatten - std::set cells_to_flatten; - for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { - if (p->size () > 0 && child_cells.find (p->back ()) == child_cells.end ()) { - cells_to_flatten.insert (p->back ()); - } - } - - for (std::set::const_iterator c = cells_to_flatten.begin (); c != cells_to_flatten.end (); ++c) { - db::Cell &target_cell = layout.cell (*c); - layout.flatten (target_cell, flatten_insts_levels, prune); - } - - layout.cleanup (); - - if (supports_undo && manager ()) { - manager ()->commit (); - } - - } - - } - - } -} - -void -LayoutView::cm_cell_rename () -{ - if (! mp_hierarchy_panel) { - return; - } - - int cv_index = active_cellview_index (); - cell_path_type path; - mp_hierarchy_panel->current_cell (cv_index, path); - - if (cv_index >= 0 && path.size () > 0) { - - lay::RenameCellDialog name_dialog (this); - - db::Layout &layout = cellview (cv_index)->layout (); - std::string name (layout.cell_name (path.back ())); - if (name_dialog.exec_dialog (layout, name)) { - - transaction (tl::to_string (QObject::tr ("Rename cell"))); - layout.rename_cell (path.back (), name.c_str ()); - commit (); - - } - - } -} - -void -LayoutView::cm_cell_select () -{ - if (mp_hierarchy_panel) { - mp_hierarchy_panel->cm_cell_select (); - } -} - -void -LayoutView::cm_open_current_cell () -{ - set_current_cell_path (active_cellview_index (), cellview (active_cellview_index ()).combined_unspecific_path ()); -} - -void -LayoutView::cm_cell_hide () -{ - if (mp_hierarchy_panel) { - - std::vector paths; - mp_hierarchy_panel->selected_cells (active_cellview_index (), paths); - - transaction (tl::to_string (QObject::tr ("Hide cell"))); - - for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { - if (! p->empty ()) { - hide_cell (p->back (), active_cellview_index ()); - } - } - - commit (); - - } -} - -void -LayoutView::cm_cell_show () -{ - if (mp_hierarchy_panel) { - - std::vector paths; - mp_hierarchy_panel->selected_cells (active_cellview_index (), paths); - - transaction (tl::to_string (QObject::tr ("Show cell"))); - - for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { - if (! p->empty ()) { - show_cell (p->back (), active_cellview_index ()); - } - } - - commit (); - - } -} - -void -LayoutView::cm_cell_show_all () -{ - if (mp_hierarchy_panel) { - transaction (tl::to_string (QObject::tr ("Show all cells"))); - show_all_cells (); - commit (); - } -} - -void -LayoutView::cm_select_all () -{ - if (mp_control_panel) { - mp_control_panel->cm_select_all (); - } -} - -void -LayoutView::cm_new_tab () -{ - if (mp_control_panel) { - mp_control_panel->cm_new_tab (); - } -} - -void -LayoutView::cm_remove_tab () -{ - if (mp_control_panel) { - mp_control_panel->cm_remove_tab (); - } -} - -void -LayoutView::cm_rename_tab () -{ - if (mp_control_panel) { - mp_control_panel->cm_rename_tab (); - } -} - -void -LayoutView::cm_make_invalid () -{ - if (mp_control_panel) { - mp_control_panel->cm_make_invalid (); - } -} - -void -LayoutView::cm_make_valid () -{ - if (mp_control_panel) { - mp_control_panel->cm_make_valid (); - } -} - -void -LayoutView::cm_hide () -{ - if (mp_control_panel) { - mp_control_panel->cm_hide (); - } -} - -void -LayoutView::cm_hide_all () -{ - if (mp_control_panel) { - mp_control_panel->cm_hide_all (); - } -} - -void -LayoutView::cm_show_only () -{ - if (mp_control_panel) { - mp_control_panel->cm_show_only (); - } -} - -void -LayoutView::cm_show_all () -{ - if (mp_control_panel) { - mp_control_panel->cm_show_all (); - } -} - -void -LayoutView::cm_show () -{ - if (mp_control_panel) { - mp_control_panel->cm_show (); - } -} - -void -LayoutView::cm_rename () -{ - if (mp_control_panel) { - mp_control_panel->cm_rename (); - } -} - -void -LayoutView::cm_delete () -{ - if (mp_control_panel) { - mp_control_panel->cm_delete (); - } -} - -void -LayoutView::cm_insert () -{ - if (mp_control_panel) { - mp_control_panel->cm_insert (); - } -} - -void -LayoutView::cm_group () -{ - if (mp_control_panel) { - mp_control_panel->cm_group (); - } -} - -void -LayoutView::cm_ungroup () -{ - if (mp_control_panel) { - mp_control_panel->cm_ungroup (); - } -} - -void -LayoutView::cm_source () -{ - if (mp_control_panel) { - mp_control_panel->cm_source (); - } -} - -void -LayoutView::cm_sort_by_name () -{ - if (mp_control_panel) { - mp_control_panel->cm_sort_by_name (); - } -} - -void -LayoutView::cm_sort_by_ild () -{ - if (mp_control_panel) { - mp_control_panel->cm_sort_by_ild (); - } -} - -void -LayoutView::cm_sort_by_idl () -{ - if (mp_control_panel) { - mp_control_panel->cm_sort_by_idl (); - } -} - -void -LayoutView::cm_sort_by_ldi () -{ - if (mp_control_panel) { - mp_control_panel->cm_sort_by_ldi (); - } -} - -void -LayoutView::cm_sort_by_dli () -{ - if (mp_control_panel) { - mp_control_panel->cm_sort_by_dli (); - } -} - -void -LayoutView::cm_regroup_by_index () -{ - if (mp_control_panel) { - mp_control_panel->cm_regroup_by_index (); - } -} - -void -LayoutView::cm_regroup_by_datatype () -{ - if (mp_control_panel) { - mp_control_panel->cm_regroup_by_datatype (); - } -} - -void -LayoutView::cm_regroup_by_layer () -{ - if (mp_control_panel) { - mp_control_panel->cm_regroup_by_layer (); - } -} - -void -LayoutView::cm_regroup_flatten () -{ - if (mp_control_panel) { - mp_control_panel->cm_regroup_flatten (); - } -} - -void -LayoutView::cm_expand_all () -{ - if (mp_control_panel) { - mp_control_panel->cm_expand_all (); - } -} - -void -LayoutView::cm_add_missing () -{ - if (mp_control_panel) { - mp_control_panel->cm_add_missing (); - } -} - void LayoutView::add_missing_layers () { @@ -6277,12 +5339,6 @@ LayoutView::add_new_layers (const LayerState &state) } } -void -LayoutView::cm_remove_unused () -{ - remove_unused_layers (); -} - void LayoutView::remove_unused_layers () { @@ -6489,138 +5545,6 @@ LayoutView::default_mode () return 0; // TODO: any generic scheme? is select, should be ruler.. } -void -LayoutView::do_cm_duplicate (bool interactive) -{ - // Do duplicate simply by concatenating copy & paste currently. - // Save the clipboard state before in order to preserve the current content - db::Clipboard saved_clipboard; - db::Clipboard::instance ().swap (saved_clipboard); - - try { - copy (); - clear_selection (); - cancel (); - if (interactive) { - paste_interactive (); - } else { - paste (); - } - db::Clipboard::instance ().swap (saved_clipboard); - } catch (...) { - db::Clipboard::instance ().swap (saved_clipboard); - throw; - } -} - -void -LayoutView::do_cm_paste (bool interactive) -{ - if (! db::Clipboard::instance ().empty ()) { - cancel (); - clear_selection (); - if (interactive) { - paste_interactive (); - } else { - paste (); - } - } -} - -void -LayoutView::cm_new_cell () -{ - static double s_new_cell_window_size = 2.0; - static std::string s_new_cell_cell_name; - - NewCellPropertiesDialog cell_prop_dia (this); - if (cell_prop_dia.exec_dialog (& cellview (active_cellview_index ())->layout (), s_new_cell_cell_name, s_new_cell_window_size)) { - - db::cell_index_type new_ci = new_cell (active_cellview_index (), s_new_cell_cell_name.c_str ()); - select_cell (new_ci, active_cellview_index ()); - - db::DBox zb = db::DBox (-0.5 * s_new_cell_window_size, -0.5 * s_new_cell_window_size, 0.5 * s_new_cell_window_size, 0.5 * s_new_cell_window_size); - if (get_max_hier_levels () < 1 || get_min_hier_levels () > 0) { - zoom_box_and_set_hier_levels (zb, std::make_pair (0, 1)); - } else { - zoom_box (zb); - } - - } -} - -// TODO: this constant is defined in MainWindow.cc too ... -const int max_dirty_files = 15; - -void -LayoutView::cm_reload () -{ - std::vector selected; - - if (cellviews () > 1) { - - SelectCellViewForm form (0, this, tl::to_string (QObject::tr ("Select Layouts To Reload"))); - form.select_all (); - - if (form.exec () == QDialog::Accepted) { - selected = form.selected_cellviews (); - } - - } else if (cellviews () > 0) { - selected.push_back (0); - } - - if (selected.size () > 0) { - - int dirty_layouts = 0; - std::string dirty_files; - - for (std::vector ::const_iterator i = selected.begin (); i != selected.end (); ++i) { - - const lay::CellView &cv = cellview (*i); - - if (cv->layout ().is_editable () && cv->is_dirty ()) { - ++dirty_layouts; - if (dirty_layouts == max_dirty_files) { - dirty_files += "\n..."; - } else if (dirty_layouts < max_dirty_files) { - if (! dirty_files.empty ()) { - dirty_files += "\n"; - } - dirty_files += cv->name (); - } - } - - } - - bool can_reload = true; - if (dirty_layouts != 0) { - - QMessageBox mbox (this); - mbox.setText (tl::to_qstring (tl::to_string (QObject::tr ("The following layouts need saving:\n\n")) + dirty_files + "\n\nPress 'Reload Without Saving' to reload anyhow and discard changes.")); - mbox.setWindowTitle (QObject::tr ("Save Needed")); - mbox.setIcon (QMessageBox::Warning); - QAbstractButton *yes_button = mbox.addButton (QObject::tr ("Reload Without Saving"), QMessageBox::YesRole); - mbox.addButton (QMessageBox::Cancel); - - mbox.exec (); - - can_reload = (mbox.clickedButton() == yes_button); - - } - - if (can_reload) { - - // Actually reload - for (std::vector ::const_iterator i = selected.begin (); i != selected.end (); ++i) { - reload_layout (*i); - } - - } - - } -} - std::vector LayoutView::menu_symbols () { @@ -6640,309 +5564,9 @@ LayoutView::menu_activated (const std::string &symbol) } } - if (symbol == "cm_show_properties") { - show_properties (this); - } else if (symbol == "cm_delete") { - - del (); - // because a "delete" might involve objects currently edited, we cancel the edit after we have deleted the object - cancel (); - clear_selection (); - - } else if (symbol == "cm_unselect_all") { - select (db::DBox (), lay::Editable::Reset); - } else if (symbol == "cm_select_all") { - select (full_box (), lay::Editable::Replace); - } else if (symbol == "cm_lv_paste") { - cm_layer_paste (); - } else if (symbol == "cm_lv_cut") { - cm_layer_cut (); - } else if (symbol == "cm_lv_copy") { - cm_layer_copy (); - } else if (symbol == "cm_cell_paste") { - cm_cell_paste (); - } else if (symbol == "cm_cell_cut") { - cm_cell_cut (); - } else if (symbol == "cm_cell_copy") { - cm_cell_copy (); - } else if (symbol == "cm_duplicate") { - do_cm_duplicate (false); - } else if (symbol == "cm_duplicate_interactive") { - do_cm_duplicate (true); - } else if (symbol == "cm_copy") { - - copy (); - clear_selection (); - - } else if (symbol == "cm_paste") { - do_cm_paste (false); - } else if (symbol == "cm_paste_interactive") { - do_cm_paste (true); - } else if (symbol == "cm_cut") { - - cut (); - cancel (); // see del() for reason why cancel is after cut - clear_selection (); - - } else if (symbol == "cm_zoom_fit_sel") { - zoom_fit_sel (); - } else if (symbol == "cm_zoom_fit") { - zoom_fit (); - } else if (symbol == "cm_pan_left") { - pan_left (); - } else if (symbol == "cm_pan_right") { - pan_right (); - } else if (symbol == "cm_pan_up") { - pan_up (); - } else if (symbol == "cm_pan_down") { - pan_down (); - } else if (symbol == "cm_zoom_in") { - zoom_in (); - } else if (symbol == "cm_zoom_out") { - zoom_out (); - } else if (symbol == "cm_select_current_cell") { - - if (active_cellview_index () >= 0) { - lay::LayoutView::cell_path_type path; - int cvi = active_cellview_index (); - current_cell_path (path); - select_cell_fit (path, cvi); - } - - } else if (symbol == "cm_open_current_cell") { - - if (active_cellview_index () >= 0) { - cm_open_current_cell (); - } - - } else if (symbol == "cm_select_cell") { - - if (active_cellview_index () >= 0) { - - CellSelectionForm form (0, this, "cell_selection_form"); - - if (form.exec () == QDialog::Accepted && - form.selected_cellview_index () >= 0) { - select_cell (form.selected_cellview ().combined_unspecific_path (), form.selected_cellview_index ()); - set_current_cell_path (form.selected_cellview_index (), form.selected_cellview ().combined_unspecific_path ()); - zoom_fit (); - } - - } - - } else if (symbol == "cm_new_cell") { - cm_new_cell (); - } else if (symbol == "cm_adjust_origin") { - if (active_cellview_index () >= 0) { - cm_align_cell_origin (); - } - } else if (symbol == "cm_cell_convert_to_static") { - if (active_cellview_index () >= 0) { - cm_cell_convert_to_static (); - } - } else if (symbol == "cm_lay_convert_to_static") { - if (active_cellview_index () >= 0) { - cm_lay_convert_to_static (); - } - } else if (symbol == "cm_lay_move") { - if (active_cellview_index () >= 0) { - cm_lay_move (); - } - } else if (symbol == "cm_lay_scale") { - if (active_cellview_index () >= 0) { - cm_lay_scale (); - } - } else if (symbol == "cm_lay_free_rot") { - if (active_cellview_index () >= 0) { - cm_lay_free_rot (); - } - } else if (symbol == "cm_lay_rot_ccw") { - if (active_cellview_index () >= 0) { - cm_lay_rot_ccw (); - } - } else if (symbol == "cm_lay_rot_cw") { - if (active_cellview_index () >= 0) { - cm_lay_rot_cw (); - } - } else if (symbol == "cm_lay_flip_y") { - if (active_cellview_index () >= 0) { - cm_lay_flip_y (); - } - } else if (symbol == "cm_lay_flip_x") { - if (active_cellview_index () >= 0) { - cm_lay_flip_x (); - } - } else if (symbol == "cm_sel_move") { - if (active_cellview_index () >= 0) { - cm_sel_move (); - } - } else if (symbol == "cm_sel_move_to") { - if (active_cellview_index () >= 0) { - cm_sel_move_to (); - } - } else if (symbol == "cm_sel_move_interactive") { - if (active_cellview_index () >= 0) { - cm_sel_move_interactive (); - } - } else if (symbol == "cm_sel_scale") { - if (active_cellview_index () >= 0) { - cm_sel_scale (); - } - } else if (symbol == "cm_sel_free_rot") { - if (active_cellview_index () >= 0) { - cm_sel_free_rot (); - } - } else if (symbol == "cm_sel_rot_ccw") { - if (active_cellview_index () >= 0) { - cm_sel_rot_ccw (); - } - } else if (symbol == "cm_sel_rot_cw") { - if (active_cellview_index () >= 0) { - cm_sel_rot_cw (); - } - } else if (symbol == "cm_sel_flip_y") { - if (active_cellview_index () >= 0) { - cm_sel_flip_y (); - } - } else if (symbol == "cm_sel_flip_x") { - if (active_cellview_index () >= 0) { - cm_sel_flip_x (); - } - } else if (symbol == "cm_edit_layer") { - if (active_cellview_index () >= 0) { - cm_edit_layer (); - } - } else if (symbol == "cm_delete_layer") { - if (active_cellview_index () >= 0) { - cm_delete_layer (); - } - } else if (symbol == "cm_clear_layer") { - if (active_cellview_index () >= 0) { - cm_clear_layer (); - } - } else if (symbol == "cm_copy_layer") { - if (active_cellview_index () >= 0) { - cm_copy_layer (); - } - } else if (symbol == "cm_new_layer") { - if (active_cellview_index () >= 0) { - cm_new_layer (); - } - } else if (symbol == "cm_layout_props") { - LayoutPropertiesForm lp_form (this, this, "layout_props_form"); - lp_form.exec (); - } else if (symbol == "cm_layout_stats") { - LayoutStatisticsForm lp_form (this, this, "layout_props_form"); - lp_form.exec (); - } else if (symbol == "cm_reload") { - cm_reload (); - } else if (symbol == "cm_inc_max_hier") { - int new_to = get_max_hier_levels () + 1; - set_hier_levels (std::make_pair (get_min_hier_levels (), new_to)); - } else if (symbol == "cm_dec_max_hier") { - int new_to = get_max_hier_levels () > 0 ? get_max_hier_levels () - 1 : 0; - set_hier_levels (std::make_pair (std::min (get_min_hier_levels (), new_to), new_to)); - } else if (symbol == "cm_max_hier") { - max_hier (); - } else if (symbol == "cm_max_hier_0") { - set_hier_levels (std::make_pair (std::min (get_min_hier_levels (), 0), 0)); - } else if (symbol == "cm_max_hier_1") { - set_hier_levels (std::make_pair (std::min (get_min_hier_levels (), 0), 1)); - } else if (symbol == "cm_prev_display_state") { - if (has_prev_display_state ()) { - prev_display_state (); - } - } else if (symbol == "cm_next_display_state") { - if (has_next_display_state ()) { - next_display_state (); - } - } else if (symbol == "cm_redraw") { - redraw (); - } else if (symbol == "cm_cell_delete") { - cm_cell_delete (); - } else if (symbol == "cm_cell_replace") { - cm_cell_replace (); - } else if (symbol == "cm_cell_rename") { - cm_cell_rename (); - } else if (symbol == "cm_cell_flatten") { - cm_cell_flatten (); - } else if (symbol == "cm_cell_select") { - cm_cell_select (); - } else if (symbol == "cm_cell_hide") { - cm_cell_hide (); - } else if (symbol == "cm_cell_show") { - cm_cell_show (); - } else if (symbol == "cm_cell_show_all") { - cm_cell_show_all (); - } else if (symbol == "cm_cell_user_properties") { - if (active_cellview_index () >= 0) { - cm_cell_user_properties (); - } - } else if (symbol == "cm_lv_select_all") { - cm_select_all (); - } else if (symbol == "cm_lv_new_tab") { - cm_new_tab (); - } else if (symbol == "cm_lv_rename_tab") { - cm_rename_tab (); - } else if (symbol == "cm_lv_make_invalid") { - cm_make_invalid (); - } else if (symbol == "cm_lv_remove_tab") { - cm_remove_tab (); - } else if (symbol == "cm_lv_make_valid") { - cm_make_valid (); - } else if (symbol == "cm_lv_hide_all") { - cm_hide_all (); - } else if (symbol == "cm_lv_hide") { - cm_hide (); - } else if (symbol == "cm_lv_show_only") { - cm_show_only (); - } else if (symbol == "cm_lv_show_all") { - cm_show_all (); - } else if (symbol == "cm_lv_show") { - cm_show (); - } else if (symbol == "cm_lv_rename") { - cm_rename (); - } else if (symbol == "cm_lv_delete") { - cm_delete (); - } else if (symbol == "cm_lv_insert") { - cm_insert (); - } else if (symbol == "cm_lv_group") { - cm_group (); - } else if (symbol == "cm_lv_ungroup") { - cm_ungroup (); - } else if (symbol == "cm_lv_source") { - cm_source (); - } else if (symbol == "cm_lv_sort_by_name") { - cm_sort_by_name (); - } else if (symbol == "cm_lv_sort_by_ild") { - cm_sort_by_ild (); - } else if (symbol == "cm_lv_sort_by_idl") { - cm_sort_by_idl (); - } else if (symbol == "cm_lv_sort_by_ldi") { - cm_sort_by_ldi (); - } else if (symbol == "cm_lv_sort_by_dli") { - cm_sort_by_dli (); - } else if (symbol == "cm_lv_regroup_by_index") { - cm_regroup_by_index (); - } else if (symbol == "cm_lv_regroup_by_datatype") { - cm_regroup_by_datatype (); - } else if (symbol == "cm_lv_regroup_by_layer") { - cm_regroup_by_layer (); - } else if (symbol == "cm_lv_regroup_flatten") { - cm_regroup_flatten (); - } else if (symbol == "cm_lv_expand_all") { - cm_expand_all (); - } else if (symbol == "cm_lv_add_missing") { - cm_add_missing (); - } else if (symbol == "cm_lv_remove_unused") { - cm_remove_unused (); - } else { - - // distribute the menu on the plugins - one should take it. - for (std::vector::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) { - (*p)->menu_activated (symbol); - } - + // distribute the menu item call on the plugins - one should take it. + for (std::vector::const_iterator p = plugins ().begin (); p != plugins ().end (); ++p) { + (*p)->menu_activated (symbol); } } @@ -7132,229 +5756,6 @@ LayoutView::new_cell (int cv_index, const std::string &cell_name) return new_ci; } -void -LayoutView::do_transform (const db::DCplxTrans &tr) -{ - // end move operations, cancel edit operations - cancel_edits (); - lay::Editables::transform (tr); -} - -void -LayoutView::transform_layout (const db::DCplxTrans &tr_mic) -{ - // end move operations, cancel edit operations - cancel_edits (); - clear_selection (); - - int cv_index = active_cellview_index (); - if (cv_index >= 0) { - - db::Layout &layout = cellview (cv_index)->layout (); - - db::ICplxTrans tr (db::DCplxTrans (1.0 / layout.dbu ()) * tr_mic * db::DCplxTrans (layout.dbu ())); - - bool has_proxy = false; - for (db::Layout::const_iterator c = layout.begin (); ! has_proxy && c != layout.end (); ++c) { - has_proxy = c->is_proxy (); - } - - if (has_proxy && - QMessageBox::question (this, - QObject::tr ("Transforming PCells Or Library Cells"), - QObject::tr ("The layout contains PCells or library cells or both.\n" - "Any changes to such cells may be lost when their layout is refreshed later.\n" - "Consider using 'Convert all cells to static' before transforming the layout.\n" - "\n" - "Would you like to continue?\n" - "Choose 'Yes' to continue anyway. Choose 'No' to cancel."), - QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { - return; - } - - transaction (tl::to_string (QObject::tr ("Transform layout"))); - layout.transform (tr); - commit (); - - } -} - -void -LayoutView::cm_lay_flip_x () -{ - transform_layout (db::DCplxTrans (db::FTrans::m90)); -} - -void -LayoutView::cm_lay_flip_y () -{ - transform_layout (db::DCplxTrans (db::FTrans::m0)); -} - -void -LayoutView::cm_lay_rot_ccw () -{ - db::DCplxTrans tr (db::DFTrans::r90); - transform_layout (db::DCplxTrans (db::FTrans::r90)); -} - -void -LayoutView::cm_lay_rot_cw () -{ - transform_layout (db::DCplxTrans (db::FTrans::r270)); -} - -void -LayoutView::cm_lay_free_rot () -{ - bool ok = false; - QString s = QInputDialog::getText (QApplication::activeWindow (), - QObject::tr ("Free rotation"), - QObject::tr ("Rotation angle in degree (counterclockwise)"), - QLineEdit::Normal, QString::fromUtf8 ("0.0"), - &ok); - - if (ok) { - - double angle = 0.0; - tl::from_string (tl::to_string (s), angle); - - transform_layout (db::DCplxTrans (1.0, angle, false, db::DVector ())); - - } -} - -void -LayoutView::cm_lay_scale () -{ - bool ok = false; - QString s = QInputDialog::getText (QApplication::activeWindow (), - QObject::tr ("Scaling"), - QObject::tr ("Scaling factor"), - QLineEdit::Normal, QString::fromUtf8 ("1.0"), - &ok); - - if (ok) { - - double scale = 0.0; - tl::from_string (tl::to_string (s), scale); - - transform_layout (db::DCplxTrans (scale)); - - } -} - -void -LayoutView::cm_lay_move () -{ - lay::MoveOptionsDialog options (this); - if (options.exec_dialog (m_move_dist)) { - transform_layout (db::DCplxTrans (m_move_dist)); - } -} - -void -LayoutView::cm_sel_flip_x () -{ - db::DCplxTrans tr (db::DFTrans::m90); - db::DBox sel_bbox (lay::Editables::selection_bbox ()); - if (! sel_bbox.empty ()) { - tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); - } - do_transform (tr); -} - -void -LayoutView::cm_sel_flip_y () -{ - db::DCplxTrans tr (db::DFTrans::m0); - db::DBox sel_bbox (lay::Editables::selection_bbox ()); - if (! sel_bbox.empty ()) { - tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); - } - do_transform (tr); -} - -void -LayoutView::cm_sel_rot_ccw () -{ - db::DCplxTrans tr (db::DFTrans::r90); - db::DBox sel_bbox (lay::Editables::selection_bbox ()); - if (! sel_bbox.empty ()) { - tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); - } - do_transform (tr); -} - -void -LayoutView::cm_sel_rot_cw () -{ - db::DCplxTrans tr (db::DFTrans::r270); - db::DBox sel_bbox (lay::Editables::selection_bbox ()); - if (! sel_bbox.empty ()) { - tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); - } - do_transform (tr); -} - -void -LayoutView::cm_sel_free_rot () -{ - bool ok = false; - QString s = QInputDialog::getText (QApplication::activeWindow (), - QObject::tr ("Free rotation"), - QObject::tr ("Rotation angle in degree (counterclockwise)"), - QLineEdit::Normal, QString::fromUtf8 ("0.0"), - &ok); - - if (ok) { - - double angle = 0.0; - tl::from_string (tl::to_string (s), angle); - - db::DCplxTrans tr = db::DCplxTrans (1.0, angle, false, db::DVector ()); - db::DBox sel_bbox (lay::Editables::selection_bbox ()); - if (! sel_bbox.empty ()) { - tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); - } - do_transform (tr); - - } -} - -void -LayoutView::cm_sel_scale () -{ - bool ok = false; - QString s = QInputDialog::getText (QApplication::activeWindow (), - QObject::tr ("Scaling"), - QObject::tr ("Scaling factor"), - QLineEdit::Normal, QString::fromUtf8 ("1.0"), - &ok); - - if (ok) { - - double scale = 0.0; - tl::from_string (tl::to_string (s), scale); - - db::DCplxTrans tr = db::DCplxTrans (scale); - db::DBox sel_bbox (lay::Editables::selection_bbox ()); - if (! sel_bbox.empty ()) { - tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); - } - do_transform (tr); - - } -} - -void -LayoutView::cm_sel_move_interactive () -{ - if (mp_move_service->begin_move ()) { - switch_mode (-1); // move mode - } -} - void LayoutView::switch_mode (int m) { @@ -7364,400 +5765,6 @@ LayoutView::switch_mode (int m) } } -void -LayoutView::cm_sel_move_to () -{ - db::DBox sel_bbox (lay::Editables::selection_bbox ()); - if (sel_bbox.empty ()) { - throw tl::Exception (tl::to_string (QObject::tr ("Nothing selected to move"))); - } - - double x = sel_bbox.left () + (sel_bbox.width () * (1 + m_move_to_origin_mode_x) * 0.5); - double y = sel_bbox.bottom () + (sel_bbox.height () * (1 + m_move_to_origin_mode_y) * 0.5); - db::DPoint move_target (x, y); - - lay::MoveToOptionsDialog options (this); - if (options.exec_dialog (m_move_to_origin_mode_x, m_move_to_origin_mode_y, move_target)) { - - x = sel_bbox.left () + (sel_bbox.width () * (1 + m_move_to_origin_mode_x) * 0.5); - y = sel_bbox.bottom () + (sel_bbox.height () * (1 + m_move_to_origin_mode_y) * 0.5); - - do_transform (db::DCplxTrans (move_target - db::DPoint (x, y))); - - } -} - -void -LayoutView::cm_sel_move () -{ - lay::MoveOptionsDialog options (this); - if (options.exec_dialog (m_move_dist)) { - do_transform (db::DCplxTrans (m_move_dist)); - } -} - -void -LayoutView::cm_copy_layer () -{ - struct { int *cv; int *layer; } specs [] = { - { &m_copy_cva, &m_copy_layera }, - { &m_copy_cvr, &m_copy_layerr } - }; - - for (unsigned int i = 0; i < sizeof (specs) / sizeof (specs[0]); ++i) { - - int &cv = *(specs[i].cv); - int &layer = *(specs[i].layer); - - if (cv >= int (m_cellviews.size ())) { - cv = -1; - } - - int index = active_cellview_index (); - if (cv < 0) { - cv = index; - } - - if (cv < 0 || ! (*cellview_iter (cv))->layout ().is_valid_layer ((unsigned int) layer)) { - layer = -1; - } - - } - - lay::DuplicateLayerDialog dialog (this); - if (dialog.exec_dialog (this, m_copy_cva, m_copy_layera, m_copy_cvr, m_copy_layerr, m_duplicate_hier_mode, m_clear_before)) { - - bool supports_undo = true; - - if (manager () && manager ()->is_enabled ()) { - - lay::TipDialog td (QApplication::activeWindow (), - tl::to_string (QObject::tr ("Undo buffering for the following operation can be memory and time consuming.\nChoose \"Yes\" to use undo buffering or \"No\" for no undo buffering. Warning: in the latter case, the undo history will be lost.\n\nChoose undo buffering?")), - "copy-layer-undo-buffering", - lay::TipDialog::yesnocancel_buttons); - - lay::TipDialog::button_type button = lay::TipDialog::null_button; - td.exec_dialog (button); - if (button == lay::TipDialog::cancel_button) { - return; - } - - supports_undo = (button == lay::TipDialog::yes_button); - - } else { - supports_undo = false; - } - - cancel (); - - if (manager ()) { - if (! supports_undo) { - manager ()->clear (); - } else { - manager ()->transaction (tl::to_string (QObject::tr ("Duplicate layer"))); - } - } - - try { - - bool same_layout = (&cellview (m_copy_cvr)->layout () == &cellview (m_copy_cva)->layout ()); - if (same_layout && m_copy_layera == m_copy_layerr) { - throw tl::Exception (tl::to_string (QObject::tr ("Source and target layer must not be identical for duplicate operation"))); - } - - if (m_duplicate_hier_mode == 0) { - - // clear the result layer for all called cells in flat mode - if (m_clear_before) { - std::set called_cells; - called_cells.insert (cellview (m_copy_cvr).cell_index ()); - cellview (m_copy_cvr).cell ()->collect_called_cells (called_cells); - for (std::set::const_iterator c = called_cells.begin (); c != called_cells.end (); ++c) { - cellview (m_copy_cvr)->layout ().cell (*c).shapes (m_copy_layerr).clear (); - } - } - - db::Cell &target_cell = *cellview (m_copy_cvr).cell (); - - if (! same_layout) { - - // flat mode (different layouts) - db::PropertyMapper pm (cellview (m_copy_cvr)->layout (), cellview (m_copy_cva)->layout ()); - for (db::RecursiveShapeIterator si (cellview (m_copy_cva)->layout (), *cellview (m_copy_cva).cell (), m_copy_layera); ! si.at_end (); ++si) { - target_cell.shapes (m_copy_layerr).insert (*si, si.trans (), pm); - } - - } else { - - // flat mode (same layouts) - tl::ident_map pm1; - db::Shapes &res = target_cell.shapes (m_copy_layerr); - - db::Layout &layout = cellview (m_copy_cvr)->layout (); - try { - - // using update/start_layout and end_changes improves the performance since changing the - // shapes collection will invalidate the layout and cause updates inside the RecursiveShapeIerator - layout.update (); - layout.start_changes (); - for (db::RecursiveShapeIterator si (cellview (m_copy_cva)->layout (), *cellview (m_copy_cva).cell (), m_copy_layera); ! si.at_end (); ++si) { - res.insert (*si, si.trans (), pm1); - } - layout.end_changes (); - - } catch (...) { - layout.end_changes (); - throw; - } - - } - - } else if (m_duplicate_hier_mode == 1) { - - db::Cell &cell = *cellview (m_copy_cva).cell (); - db::Cell &target_cell = *cellview (m_copy_cvr).cell (); - - if (m_clear_before) { - target_cell.clear (m_copy_layerr); - } - - if (m_copy_cvr == m_copy_cva) { - - // current cell only mode: identical cell - cell.copy (m_copy_layera, m_copy_layerr); - - } else if (! same_layout) { - - // current cell only mode (different layouts) - db::PropertyMapper pm (cellview (m_copy_cvr)->layout (), cellview (m_copy_cva)->layout ()); - for (db::Shapes::shape_iterator si = cellview (m_copy_cva).cell ()->shapes (m_copy_layera).begin (db::ShapeIterator::All); ! si.at_end (); ++si) { - target_cell.shapes (m_copy_layerr).insert (*si, pm); - } - - } else { - - // current cell only mode (same layouts, but different cells) - for (db::Shapes::shape_iterator si = cellview (m_copy_cva).cell ()->shapes (m_copy_layera).begin (db::ShapeIterator::All); ! si.at_end (); ++si) { - target_cell.shapes (m_copy_layerr).insert (*si); - } - - } - - } else if (m_duplicate_hier_mode == 2) { - - // subcells cell by cell - source and target layout must be identical - std::set called_cells; - cellview (m_copy_cva).cell ()->collect_called_cells (called_cells); - called_cells.insert (cellview (m_copy_cva).cell_index ()); - - db::Layout &layout = cellview (m_copy_cva)->layout (); - for (std::set::const_iterator c = called_cells.begin (); c != called_cells.end (); ++c) { - db::Cell &cell = layout.cell (*c); - if (m_clear_before) { - cell.clear (m_copy_layerr); - } - cell.copy (m_copy_layera, m_copy_layerr); - } - - } - - if (manager () && supports_undo) { - manager ()->commit (); - } - - } catch (...) { - if (manager () && supports_undo) { - manager ()->commit (); - } - throw; - } - - } -} - -void -LayoutView::cm_new_layer () -{ - int index = active_cellview_index (); - - if (index >= 0 && int (m_cellviews.size ()) > index) { - - const lay::CellView &cv = cellview (index); - - lay::NewLayerPropertiesDialog prop_dia (this); - if (prop_dia.exec_dialog (cv, m_new_layer_props)) { - - for (unsigned int l = 0; l < cv->layout ().layers (); ++l) { - if (cv->layout ().is_valid_layer (l) && cv->layout ().get_properties (l).log_equal (m_new_layer_props)) { - throw tl::Exception (tl::to_string (QObject::tr ("A layer with that signature already exists: ")) + m_new_layer_props.to_string ()); - } - } - - transaction (tl::to_string (QObject::tr ("New layer"))); - - unsigned int l = cv->layout ().insert_layer (m_new_layer_props); - std::vector nl; - nl.push_back (l); - add_new_layers (nl, index); - update_content (); - - commit (); - - } - - } -} - -void -LayoutView::cm_edit_layer () -{ - lay::LayerPropertiesConstIterator sel = current_layer (); - if (sel.is_null ()) { - throw tl::Exception (tl::to_string (QObject::tr ("No layer selected for editing it's properties"))); - } - - int index = sel->cellview_index (); - if (sel->has_children () || index < 0 || int (m_cellviews.size ()) <= index || sel->layer_index () < 0) { - throw tl::Exception (tl::to_string (QObject::tr ("No valid layer selected for editing it's properties"))); - } - - const lay::CellView &cv = cellview (index); - - db::LayerProperties layer_props = cv->layout ().get_properties ((unsigned int) sel->layer_index ()); - - lay::NewLayerPropertiesDialog prop_dia (this); - if (prop_dia.exec_dialog (cv, layer_props)) { - - for (unsigned int l = 0; l < cv->layout ().layers (); ++l) { - if (cv->layout ().is_valid_layer (l) && int (l) != sel->layer_index () && cv->layout ().get_properties (l).log_equal (layer_props)) { - throw tl::Exception (tl::to_string (QObject::tr ("A layer with that signature already exists: ")) + layer_props.to_string ()); - } - } - - transaction (tl::to_string (QObject::tr ("Edit layer"))); - - cv->layout ().set_properties (sel->layer_index (), layer_props); - - lay::LayerProperties lp (*sel); - lay::ParsedLayerSource s = lp.source (false); - s.layer (layer_props.layer); - s.datatype (layer_props.datatype); - if (! layer_props.name.empty ()) { - s.name (layer_props.name); - } else { - s.clear_name (); - } - lp.set_source (s); - set_properties (sel, lp); - - update_content (); - - commit (); - - } -} - -void -LayoutView::cm_delete_layer () -{ - std::vector sel = selected_layers (); - std::sort (sel.begin (), sel.end (), CompareLayerIteratorBottomUp ()); - - // collect valid layers - std::vector valid_sel; - std::set > valid_layers; - for (std::vector::const_iterator si = sel.begin (); si != sel.end (); ++si) { - int cv_index = (*si)->cellview_index (); - const lay::CellView &cv = cellview (cv_index); - if (!(*si)->has_children () && cv_index >= 0 && int (m_cellviews.size ()) > cv_index && (*si)->layer_index () >= 0 && cv.is_valid ()) { - valid_sel.push_back (*si); - valid_layers.insert (std::make_pair (&cv->layout (), (*si)->layer_index ())); - } - } - - if (valid_sel.empty ()) { - throw tl::Exception (tl::to_string (QObject::tr ("No or no valid layer selected for deleting them"))); - } - - cancel_edits (); - clear_selection (); - - transaction (tl::to_string (QObject::tr ("Delete layers"))); - - // Hint: delete_layer must come before the layers are actually deleted in because - // for undo this must be the last thing to do (otherwise the layout is not propertly set up) - - for (std::vector::const_iterator si = valid_sel.begin (); si != valid_sel.end (); ++si) { - lay::LayerPropertiesConstIterator lp = *si; - delete_layer (lp); - } - - for (std::set >::const_iterator li = valid_layers.begin (); li != valid_layers.end(); ++li) { - - unsigned int layer_index = li->second; - db::Layout *layout = li->first; - - for (db::Layout::iterator c = layout->begin (); c != layout->end (); ++c) { - c->shapes (layer_index).clear (); - } - - layout->delete_layer (layer_index); - - } - - update_content (); - - commit (); -} - -void -LayoutView::cm_clear_layer () -{ - std::vector sel = selected_layers (); - if (sel.empty ()) { - throw tl::Exception (tl::to_string (QObject::tr ("No layer selected for clearing"))); - } - - lay::ClearLayerModeDialog mode_dialog (this); - if (mode_dialog.exec_dialog (m_layer_hier_mode)) { - - cancel_edits (); - clear_selection (); - - transaction (tl::to_string (QObject::tr ("Clear layer"))); - - for (std::vector::const_iterator si = sel.begin (); si != sel.end (); ++si) { - - if (! (*si)->has_children () && (*si)->layer_index () >= 0 && cellview ((*si)->cellview_index ()).is_valid ()) { - - int layer_index = (*si)->layer_index (); - const lay::CellView &cv = cellview ((*si)->cellview_index ()); - - if (m_layer_hier_mode == 0) { - cv.cell ()->clear ((unsigned int) layer_index); - } else if (m_layer_hier_mode == 1) { - - cv.cell ()->clear ((unsigned int) layer_index); - - std::set called_cells; - cv.cell ()->collect_called_cells (called_cells); - for (std::set ::const_iterator cc = called_cells.begin (); cc != called_cells.end (); ++cc) { - cv->layout ().cell (*cc).clear ((unsigned int) layer_index); - } - - } else { - cv->layout ().clear_layer ((unsigned int) layer_index); - } - - } - - } - - commit (); - - } -} - template static void make_unique_name (T *object, Iter from, Iter to) { @@ -7954,203 +5961,4 @@ LayoutView::sizeHint () const } } -// ------------------------------------------------------------ -// Declaration of the "plugin" for the menu entries - -class LayoutViewPluginDeclaration - : public lay::PluginDeclaration -{ -public: - virtual void get_menu_entries (std::vector &menu_entries) const - { - std::string at; - - // secret menu entries - at = "@secrets.end"; - menu_entries.push_back (lay::menu_item ("cm_paste_interactive", "paste_interactive:edit", at, tl::to_string (QObject::tr ("Paste Interactive")))); - menu_entries.push_back (lay::menu_item ("cm_duplicate_interactive", "duplicate_interactive:edit", at, tl::to_string (QObject::tr ("Duplicate Interactive")))); - menu_entries.push_back (lay::menu_item ("cm_sel_move_interactive", "sel_move_interactive:edit", at, tl::to_string (QObject::tr ("Move Interactive")))); - - at = "edit_menu.end"; - menu_entries.push_back (lay::menu_item ("cm_undo", "undo:edit", at, tl::to_string (QObject::tr ("Undo(Ctrl+Z)")))); - menu_entries.push_back (lay::menu_item ("cm_redo", "redo:edit", at, tl::to_string (QObject::tr ("Redo(Ctrl+Y)")))); - - menu_entries.push_back (lay::separator ("basic_group", at)); - menu_entries.push_back (lay::submenu ("layout_menu:edit:edit_mode", at, tl::to_string (QObject::tr ("Layout")))); - { - std::string at = "edit_menu.layout_menu.end"; - menu_entries.push_back (lay::menu_item ("cm_lay_flip_x", "lay_flip_x:edit_mode", at, tl::to_string (QObject::tr ("Flip Horizontally")))); - menu_entries.push_back (lay::menu_item ("cm_lay_flip_y", "lay_flip_y:edit_mode", at, tl::to_string (QObject::tr ("Flip Vertically")))); - menu_entries.push_back (lay::menu_item ("cm_lay_rot_cw", "lay_rot_cw:edit_mode", at, tl::to_string (QObject::tr ("Rotate Clockwise")))); - menu_entries.push_back (lay::menu_item ("cm_lay_rot_ccw", "lay_rot_ccw:edit_mode", at, tl::to_string (QObject::tr ("Rotate Counterclockwise")))); - menu_entries.push_back (lay::menu_item ("cm_lay_free_rot", "lay_free_rot:edit_mode", at, tl::to_string (QObject::tr ("Rotation By Angle")))); - menu_entries.push_back (lay::menu_item ("cm_lay_scale", "lay_scale:edit_mode", at, tl::to_string (QObject::tr ("Scale")))); - menu_entries.push_back (lay::menu_item ("cm_lay_move", "lay_move:edit_mode", at, tl::to_string (QObject::tr ("Move By")))); - menu_entries.push_back (lay::separator ("cellop_group", at)); - menu_entries.push_back (lay::menu_item ("cm_lay_convert_to_static", "lay_convert_to_static:edit_mode", at, tl::to_string (QObject::tr ("Convert All Cells To Static")))); - } - - menu_entries.push_back (lay::submenu ("cell_menu:edit:edit_mode", at, tl::to_string (QObject::tr ("Cell")))); - { - std::string at = "edit_menu.cell_menu.end"; - menu_entries.push_back (lay::menu_item ("cm_new_cell", "new_cell:edit:edit_mode", at, tl::to_string (QObject::tr ("New Cell")))); - menu_entries.push_back (lay::menu_item ("cm_cell_delete", "delete_cell:edit:edit_mode", at, tl::to_string (QObject::tr ("Delete Cell")))); - menu_entries.push_back (lay::menu_item ("cm_cell_rename", "rename_cell:edit:edit_mode", at, tl::to_string (QObject::tr ("Rename Cell")))); - menu_entries.push_back (lay::menu_item ("cm_cell_replace", "replace_cell:edit:edit_mode", at, tl::to_string (QObject::tr ("Replace Cell")))); - menu_entries.push_back (lay::menu_item ("cm_cell_flatten", "flatten_cell:edit:edit_mode", at, tl::to_string (QObject::tr ("Flatten Cell")))); - menu_entries.push_back (lay::separator ("ops_group", at)); - menu_entries.push_back (lay::menu_item ("cm_adjust_origin", "adjust_cell_origin:edit:edit_mode", at, tl::to_string (QObject::tr ("Adjust Origin")))); - menu_entries.push_back (lay::menu_item ("cm_cell_convert_to_static", "convert_cell_to_static:edit_mode", at, tl::to_string (QObject::tr ("Convert Cell To Static")))); - menu_entries.push_back (lay::separator ("props_group", at)); - menu_entries.push_back (lay::menu_item ("cm_cell_user_properties", "user_properties", at, tl::to_string (QObject::tr ("User Properties")))); - } - - menu_entries.push_back (lay::submenu ("layer_menu:edit:edit_mode", at, tl::to_string (QObject::tr ("Layer")))); - { - std::string at = "edit_menu.layer_menu.end"; - menu_entries.push_back (lay::menu_item ("cm_new_layer", "new_layer:edit:edit_mode", at, tl::to_string (QObject::tr ("New Layer")))); - menu_entries.push_back (lay::menu_item ("cm_clear_layer", "clear_layer:edit:edit_mode", at, tl::to_string (QObject::tr ("Clear Layer")))); - menu_entries.push_back (lay::menu_item ("cm_delete_layer", "delete_layer:edit:edit_mode", at, tl::to_string (QObject::tr ("Delete Layer")))); - menu_entries.push_back (lay::menu_item ("cm_copy_layer", "copy_layer:edit:edit_mode", at, tl::to_string (QObject::tr ("Copy Layer")))); - menu_entries.push_back (lay::menu_item ("cm_edit_layer", "edit_layer:edit:edit_mode", at, tl::to_string (QObject::tr ("Edit Layer Specification")))); - } - - menu_entries.push_back (lay::submenu ("selection_menu:edit", at, tl::to_string (QObject::tr ("Selection")))); - { - std::string at = "edit_menu.selection_menu.end"; - menu_entries.push_back (lay::menu_item ("cm_sel_flip_x", "sel_flip_x", at, tl::to_string (QObject::tr ("Flip Horizontally")))); - menu_entries.push_back (lay::menu_item ("cm_sel_flip_y", "sel_flip_y", at, tl::to_string (QObject::tr ("Flip Vertically")))); - menu_entries.push_back (lay::menu_item ("cm_sel_rot_cw", "sel_rot_cw", at, tl::to_string (QObject::tr ("Rotate Clockwise")))); - menu_entries.push_back (lay::menu_item ("cm_sel_rot_ccw", "sel_rot_ccw", at, tl::to_string (QObject::tr ("Rotate Counterclockwise")))); - menu_entries.push_back (lay::menu_item ("cm_sel_free_rot", "sel_free_rot", at, tl::to_string (QObject::tr ("Rotation By Angle")))); - menu_entries.push_back (lay::menu_item ("cm_sel_scale", "sel_scale", at, tl::to_string (QObject::tr ("Scale")))); - menu_entries.push_back (lay::menu_item ("cm_sel_move", "sel_move", at, tl::to_string (QObject::tr ("Move By")))); - menu_entries.push_back (lay::menu_item ("cm_sel_move_to", "sel_move_to", at, tl::to_string (QObject::tr ("Move To")))); - } - - menu_entries.push_back (lay::separator ("utils_group", at)); - menu_entries.push_back (lay::submenu ("utils_menu:edit:edit_mode", at, tl::to_string (QObject::tr ("Utilities")))); - - menu_entries.push_back (lay::separator ("misc_group", at)); - menu_entries.push_back (lay::menu_item ("cm_delete", "delete:edit", at, tl::to_string (QObject::tr ("Delete(Del)")))); - menu_entries.push_back (lay::menu_item ("cm_show_properties", "show_properties:edit", at, tl::to_string (QObject::tr ("Properties(Q)")))); - - menu_entries.push_back (lay::separator ("cpc_group", at)); - menu_entries.push_back (lay::menu_item ("cm_copy", "copy:edit", at, tl::to_string (QObject::tr ("Copy(Ctrl+C)")))); - menu_entries.push_back (lay::menu_item ("cm_cut", "cut:edit", at, tl::to_string (QObject::tr ("Cut(Ctrl+X)")))); - menu_entries.push_back (lay::menu_item ("cm_paste", "paste:edit", at, tl::to_string (QObject::tr ("Paste(Ctrl+V)")))); - menu_entries.push_back (lay::menu_item ("cm_duplicate", "duplicate:edit", at, tl::to_string (QObject::tr ("Duplicate(Ctrl+B)")))); - - menu_entries.push_back (lay::separator ("modes_group", at)); - menu_entries.push_back (lay::submenu ("mode_menu", at, tl::to_string (QObject::tr ("Mode")))); - - menu_entries.push_back (lay::submenu ("select_menu", at, tl::to_string (QObject::tr ("Select")))); - { - std::string at = "edit_menu.select_menu.end"; - menu_entries.push_back (lay::menu_item ("cm_select_all", "select_all", at, tl::to_string (QObject::tr ("Select All")))); - menu_entries.push_back (lay::menu_item ("cm_unselect_all", "unselect_all", at, tl::to_string (QObject::tr ("Unselect All")))); - menu_entries.push_back (lay::separator ("edit_select_basic_group", at)); - menu_entries.push_back (lay::menu_item ("lv:enable_all", "enable_all", at, tl::to_string (QObject::tr ("Enable All")))); - menu_entries.push_back (lay::menu_item ("lv:disable_all", "disable_all", at, tl::to_string (QObject::tr ("Disable All")))); - menu_entries.push_back (lay::separator ("edit_select_individual_group", at)); - }; - - menu_entries.push_back (lay::separator ("cancel_group", at)); - menu_entries.push_back (lay::menu_item ("cm_cancel", "cancel", at, tl::to_string (QObject::tr ("Cancel(Esc)")))); - - at = "bookmark_menu.end"; - menu_entries.push_back (lay::submenu ("goto_bookmark_menu", at, tl::to_string (QObject::tr ("Goto Bookmark")))); - menu_entries.push_back (lay::menu_item ("cm_bookmark_view", "bookmark_view", at, tl::to_string (QObject::tr ("Bookmark This View")))); - - menu_entries.push_back (lay::separator ("bookmark_mgm_group", at)); - menu_entries.push_back (lay::menu_item ("cm_manage_bookmarks", "manage_bookmarks", at, tl::to_string (QObject::tr ("Manage Bookmarks")))); - menu_entries.push_back (lay::menu_item ("cm_load_bookmarks", "load_bookmarks", at, tl::to_string (QObject::tr ("Load Bookmarks")))); - menu_entries.push_back (lay::menu_item ("cm_save_bookmarks", "save_bookmarks", at, tl::to_string (QObject::tr ("Save Bookmarks")))); - - at = "zoom_menu.end"; - menu_entries.push_back (lay::submenu ("global_trans", at, tl::to_string (QObject::tr ("Global Transformation")))); - { - std::string at = "zoom_menu.global_trans.end"; - menu_entries.push_back (lay::config_menu_item ("r0", at, tl::to_string (QObject::tr ("\\(r0\\)<:/r0.png>")), cfg_global_trans, "?r0 *1 0,0")); - menu_entries.push_back (lay::config_menu_item ("r90", at, tl::to_string (QObject::tr ("\\(r90\\)<:/r90.png>")), cfg_global_trans, "?r90 *1 0,0")); - menu_entries.push_back (lay::config_menu_item ("r180", at, tl::to_string (QObject::tr ("\\(r180\\)<:/r180.png>")), cfg_global_trans, "?r180 *1 0,0")); - menu_entries.push_back (lay::config_menu_item ("r270", at, tl::to_string (QObject::tr ("\\(r270\\)<:/r270.png>")), cfg_global_trans, "?r270 *1 0,0")); - menu_entries.push_back (lay::config_menu_item ("m0", at, tl::to_string (QObject::tr ("\\(m0\\)<:/m0.png>")), cfg_global_trans, "?m0 *1 0,0")); - menu_entries.push_back (lay::config_menu_item ("m45", at, tl::to_string (QObject::tr ("\\(m45\\)<:/m45.png>")), cfg_global_trans, "?m45 *1 0,0")); - menu_entries.push_back (lay::config_menu_item ("m90", at, tl::to_string (QObject::tr ("\\(m90\\)<:/m90.png>")), cfg_global_trans, "?m90 *1 0,0")); - menu_entries.push_back (lay::config_menu_item ("m135", at, tl::to_string (QObject::tr ("\\(m135\\)<:/m135.png>")), cfg_global_trans, "?m135 *1 0,0")); - } - - menu_entries.push_back (lay::separator ("hier_group", at)); - menu_entries.push_back (lay::menu_item ("cm_max_hier", "max_hier", at, tl::to_string (QObject::tr ("Full Hierarchy(*)")))); - menu_entries.push_back (lay::menu_item ("cm_max_hier_0", "max_hier_0", at, tl::to_string (QObject::tr ("Box Only(0)")))); - menu_entries.push_back (lay::menu_item ("cm_max_hier_1", "max_hier_1", at, tl::to_string (QObject::tr ("Top Level Only(1)")))); - menu_entries.push_back (lay::menu_item ("cm_inc_max_hier", "inc_max_hier", at, tl::to_string (QObject::tr ("Increment Hierarchy(+)")))); - menu_entries.push_back (lay::menu_item ("cm_dec_max_hier", "dec_max_hier", at, tl::to_string (QObject::tr ("Decrement Hierarchy(-)")))); - - menu_entries.push_back (lay::separator ("zoom_group", at)); - menu_entries.push_back (lay::menu_item ("cm_zoom_fit", "zoom_fit", at, tl::to_string (QObject::tr ("Zoom Fit(F2)")))); - menu_entries.push_back (lay::menu_item ("cm_zoom_fit_sel", "zoom_fit_sel", at, tl::to_string (QObject::tr ("Zoom Fit Selection(Shift+F2)")))); - menu_entries.push_back (lay::menu_item ("cm_zoom_in", "zoom_in", at, tl::to_string (QObject::tr ("Zoom In(Return)")))); - menu_entries.push_back (lay::menu_item ("cm_zoom_out", "zoom_out", at, tl::to_string (QObject::tr ("Zoom Out(Shift+Return)")))); - /* disabled because that interferes with the use of the arrow keys for moving the selection - MenuLayoutEntry::separator ("pan_group"); - menu_entries.push_back (lay::menu_item ("cm_pan_up", "pan_up", at, tl::to_string (QObject::tr ("Pan Up(Up)")))); - menu_entries.push_back (lay::menu_item ("cm_pan_down", "pan_down", at, tl::to_string (QObject::tr ("Pan Down(Down)")))); - menu_entries.push_back (lay::menu_item ("cm_pan_left", "pan_left", at, tl::to_string (QObject::tr ("Pan Left(Left)")))); - menu_entries.push_back (lay::menu_item ("cm_pan_right", "pan_right", at, tl::to_string (QObject::tr ("Pan Right(Right)")))); - */ - - menu_entries.push_back (lay::separator ("redraw_group", at)); - menu_entries.push_back (lay::menu_item ("cm_redraw", "redraw", at, tl::to_string (QObject::tr ("Redraw")))); - menu_entries.push_back (lay::separator ("state_group", at)); - menu_entries.push_back (lay::menu_item_copy ("cm_prev_display_state", "prev_display_state", at, "@toolbar.prev_display_state")); - menu_entries.push_back (lay::menu_item_copy ("cm_next_display_state", "next_display_state", at, "@toolbar.next_display_state")); - - menu_entries.push_back (lay::separator ("select_group", at)); - menu_entries.push_back (lay::menu_item ("cm_select_cell", "select_cell:edit", at, tl::to_string (QObject::tr ("Select Cell")))); - menu_entries.push_back (lay::menu_item ("cm_select_current_cell", "select_current_cell", at, tl::to_string (QObject::tr ("Show As New Top(Ctrl+S)")))); - menu_entries.push_back (lay::menu_item ("cm_goto_position", "goto_position", at, tl::to_string (QObject::tr ("Goto Position(Ctrl+G)")))); - - // Add a hook for inserting new items after the modes - menu_entries.push_back (lay::separator ("end_modes", "@toolbar.end")); - - } - - bool menu_activated (const std::string &symbol) const - { - if (symbol == "lv:enable_all") { - - for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { - cls->set_editable_enabled (true); - } - return true; - - } else if (symbol == "lv:disable_all") { - - for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { - cls->set_editable_enabled (false); - } - return true; - - } else { - return false; - } - } - - void implements_primary_mouse_modes (std::vector > > &modes) - { - std::vector mode_titles; - lay::LayoutView::intrinsic_mouse_modes (&mode_titles); - - int mode_id = 0; - for (std::vector ::const_iterator t = mode_titles.begin (); t != mode_titles.end (); ++t, --mode_id) { - // modes: pair(title, pair(insert_pos, id)) - modes.push_back (std::make_pair (*t, std::make_pair ("edit_menu.mode_menu.end;@toolbar.end_modes", mode_id))); - } - } -}; - -static tl::RegisteredClass config_decl (new LayoutViewPluginDeclaration (), -10, "LayoutViewPlugin"); - } // namespace lay diff --git a/src/laybasic/laybasic/layLayoutView.h b/src/laybasic/laybasic/layLayoutView.h index fc05c566e..0081684b4 100644 --- a/src/laybasic/laybasic/layLayoutView.h +++ b/src/laybasic/laybasic/layLayoutView.h @@ -50,7 +50,6 @@ #include "layPlugin.h" #include "layDisplayState.h" #include "layBookmarkList.h" -#include "layDialogs.h" #include "gsi.h" #include "tlException.h" #include "tlEvents.h" @@ -237,6 +236,14 @@ public: return mp_control_frame; } + /** + * @brief Gets the layer control panel + */ + lay::LayerControlPanel *control_panel () + { + return mp_control_panel; + } + /** * @brief Gets the container with the hierarchy control panel */ @@ -245,6 +252,14 @@ public: return mp_hierarchy_frame; } + /** + * @brief Gets the hierarchy panel + */ + lay::HierarchyControlPanel *hierarchy_panel () + { + return mp_hierarchy_panel; + } + /** * @brief Gets the container with the libraries view */ @@ -1958,6 +1973,14 @@ public: */ virtual void drop_url (const std::string &path_or_url); + /** + * @brief Gets a list of all plugins + */ + const std::vector &plugins () + { + return mp_plugins; + } + /** * @brief Localize a plugin by name * @@ -2582,81 +2605,6 @@ public slots: void pan_right_fast (); void pan_down_fast (); - // menu callbacks - void cm_new_layer (); - void cm_clear_layer (); - void cm_delete_layer (); - void cm_copy_layer (); - void cm_align_cell_origin (); - void cm_edit_layer (); - void cm_lay_convert_to_static (); - void cm_lay_flip_x (); - void cm_lay_flip_y (); - void cm_lay_rot_cw (); - void cm_lay_rot_ccw (); - void cm_lay_free_rot (); - void cm_lay_scale (); - void cm_lay_move (); - void cm_sel_flip_x (); - void cm_sel_flip_y (); - void cm_sel_rot_cw (); - void cm_sel_rot_ccw (); - void cm_sel_free_rot (); - void cm_sel_scale (); - void cm_sel_move (); - void cm_sel_move_to (); - void cm_sel_move_interactive (); - - // forwarded to the layer control panel - void cm_new_tab (); - void cm_rename_tab (); - void cm_remove_tab (); - void cm_select_all (); - void cm_make_valid (); - void cm_make_invalid (); - void cm_hide (); - void cm_hide_all (); - void cm_show (); - void cm_show_all (); - void cm_show_only (); - void cm_rename (); - void cm_delete (); - void cm_insert (); - void cm_group (); - void cm_ungroup (); - void cm_source (); - void cm_sort_by_name (); - void cm_sort_by_ild (); - void cm_sort_by_idl (); - void cm_sort_by_ldi (); - void cm_sort_by_dli (); - void cm_regroup_by_index (); - void cm_regroup_by_datatype (); - void cm_regroup_by_layer (); - void cm_regroup_flatten (); - void cm_expand_all (); - void cm_add_missing (); - void cm_remove_unused (); - void cm_layer_copy (); - void cm_layer_cut (); - void cm_layer_paste (); - - // forwarded to the cell control panel - void cm_cell_user_properties (); - void cm_cell_flatten (); - void cm_cell_rename (); - void cm_cell_replace (); - void cm_cell_delete (); - void cm_cell_select (); - void cm_open_current_cell (); - void cm_cell_hide (); - void cm_cell_show (); - void cm_cell_show_all (); - void cm_cell_copy (); - void cm_cell_cut (); - void cm_cell_paste (); - void cm_cell_convert_to_static (); - // called by children and owner void redraw (); void redraw_layer (unsigned int index); @@ -2869,17 +2817,6 @@ private: std::vector mp_plugins; - db::LayerProperties m_new_layer_props; - db::DVector m_move_dist; - int m_move_to_origin_mode_x, m_move_to_origin_mode_y; - lay::AlignCellOptions m_align_cell_options; - int m_del_cell_mode; - int m_layer_hier_mode; - int m_duplicate_hier_mode; - bool m_clear_before; - int m_copy_cva, m_copy_cvr; - int m_copy_layera, m_copy_layerr; - bool m_visibility_changed; bool m_active_cellview_changed_event_enabled; tl::DeferredMethod dm_prop_changed; @@ -2890,8 +2827,6 @@ private: void do_prop_changed (); void do_redraw (int layer); void do_redraw (); - void do_transform (const db::DCplxTrans &tr); - void transform_layout (const db::DCplxTrans &tr); void background_color (QColor c); void ctx_color (QColor c); @@ -2918,11 +2853,6 @@ private: void init_layer_properties (LayerProperties &props, const LayerPropertiesList &lp_list) const; void merge_dither_pattern (lay::LayerPropertiesList &props); - void do_cm_duplicate (bool interactive); - void do_cm_paste (bool interactive); - void cm_new_cell (); - void cm_reload (); - // overrides Editables method to display a message void signal_selection_changed (); diff --git a/src/laybasic/laybasic/layLayoutViewFunctions.cc b/src/laybasic/laybasic/layLayoutViewFunctions.cc new file mode 100644 index 000000000..5e58747a2 --- /dev/null +++ b/src/laybasic/laybasic/layLayoutViewFunctions.cc @@ -0,0 +1,2249 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "layLayoutViewFunctions.h" +#include "layLayoutView.h" +#include "layCellSelectionForm.h" +#include "layLayoutStatisticsForm.h" +#include "layLayoutPropertiesForm.h" +#include "layHierarchyControlPanel.h" +#include "layLayerControlPanel.h" +#include "layTipDialog.h" +#include "laySelectCellViewForm.h" +#include "layMove.h" +#include "laybasicConfig.h" + +#include "dbClipboard.h" +#include "dbRecursiveShapeIterator.h" +#include "dbLayoutUtils.h" + +#include +#include + +namespace lay +{ + +LayoutViewFunctions::LayoutViewFunctions (db::Manager *manager, LayoutView *view) + : lay::Plugin (view), mp_view (view), mp_manager (manager) +{ + m_del_cell_mode = 0; + m_move_to_origin_mode_x = 0; + m_move_to_origin_mode_y = 0; + m_del_cell_mode = 0; + m_layer_hier_mode = 0; + m_duplicate_hier_mode = 2; + m_clear_before = true; + m_copy_cva = -1; + m_copy_cvr = -1; + m_copy_layera = -1; + m_copy_layerr = -1; + + m_new_layer_props.layer = 1; + m_new_layer_props.datatype = 0; +} + +LayoutViewFunctions::~LayoutViewFunctions () +{ + // .. nothing yet.. +} + +void +LayoutViewFunctions::menu_activated (const std::string &symbol) +{ + if (symbol == "cm_show_properties") { + + view ()->show_properties (view ()); + + } else if (symbol == "cm_delete") { + + view ()->del (); + // because a "delete" might involve objects currently edited, we cancel the edit after we have deleted the object + view ()->cancel (); + view ()->clear_selection (); + + } else if (symbol == "cm_unselect_all") { + view ()->select (db::DBox (), lay::Editable::Reset); + } else if (symbol == "cm_select_all") { + view ()->select (view ()->full_box (), lay::Editable::Replace); + } else if (symbol == "cm_lv_paste") { + cm_layer_paste (); + } else if (symbol == "cm_lv_cut") { + cm_layer_cut (); + } else if (symbol == "cm_lv_copy") { + cm_layer_copy (); + } else if (symbol == "cm_cell_paste") { + cm_cell_paste (); + } else if (symbol == "cm_cell_cut") { + cm_cell_cut (); + } else if (symbol == "cm_cell_copy") { + cm_cell_copy (); + } else if (symbol == "cm_duplicate") { + do_cm_duplicate (false); + } else if (symbol == "cm_duplicate_interactive") { + do_cm_duplicate (true); + } else if (symbol == "cm_copy") { + + view ()->copy (); + view ()->clear_selection (); + + } else if (symbol == "cm_paste") { + do_cm_paste (false); + } else if (symbol == "cm_paste_interactive") { + do_cm_paste (true); + } else if (symbol == "cm_cut") { + + view ()->cut (); + view ()->cancel (); // see del() for reason why cancel is after cut + view ()->clear_selection (); + + } else if (symbol == "cm_zoom_fit_sel") { + view ()->zoom_fit_sel (); + } else if (symbol == "cm_zoom_fit") { + view ()->zoom_fit (); + } else if (symbol == "cm_pan_left") { + view ()->pan_left (); + } else if (symbol == "cm_pan_right") { + view ()->pan_right (); + } else if (symbol == "cm_pan_up") { + view ()->pan_up (); + } else if (symbol == "cm_pan_down") { + view ()->pan_down (); + } else if (symbol == "cm_zoom_in") { + view ()->zoom_in (); + } else if (symbol == "cm_zoom_out") { + view ()->zoom_out (); + } else if (symbol == "cm_select_current_cell") { + + if (view ()->active_cellview_index () >= 0) { + lay::LayoutView::cell_path_type path; + int cvi = view ()->active_cellview_index (); + view ()->current_cell_path (path); + view ()->select_cell_fit (path, cvi); + } + + } else if (symbol == "cm_open_current_cell") { + + if (view ()->active_cellview_index () >= 0) { + cm_open_current_cell (); + } + + } else if (symbol == "cm_select_cell") { + + if (view ()->active_cellview_index () >= 0) { + + lay::CellSelectionForm form (0, view (), "cell_selection_form"); + + if (form.exec () == QDialog::Accepted && + form.selected_cellview_index () >= 0) { + view ()->select_cell (form.selected_cellview ().combined_unspecific_path (), form.selected_cellview_index ()); + view ()->set_current_cell_path (form.selected_cellview_index (), form.selected_cellview ().combined_unspecific_path ()); + view ()->zoom_fit (); + } + + } + + } else if (symbol == "cm_new_cell") { + cm_new_cell (); + } else if (symbol == "cm_adjust_origin") { + if (view ()->active_cellview_index () >= 0) { + cm_align_cell_origin (); + } + } else if (symbol == "cm_cell_convert_to_static") { + if (view ()->active_cellview_index () >= 0) { + cm_cell_convert_to_static (); + } + } else if (symbol == "cm_lay_convert_to_static") { + if (view ()->active_cellview_index () >= 0) { + cm_lay_convert_to_static (); + } + } else if (symbol == "cm_lay_move") { + if (view ()->active_cellview_index () >= 0) { + cm_lay_move (); + } + } else if (symbol == "cm_lay_scale") { + if (view ()->active_cellview_index () >= 0) { + cm_lay_scale (); + } + } else if (symbol == "cm_lay_free_rot") { + if (view ()->active_cellview_index () >= 0) { + cm_lay_free_rot (); + } + } else if (symbol == "cm_lay_rot_ccw") { + if (view ()->active_cellview_index () >= 0) { + cm_lay_rot_ccw (); + } + } else if (symbol == "cm_lay_rot_cw") { + if (view ()->active_cellview_index () >= 0) { + cm_lay_rot_cw (); + } + } else if (symbol == "cm_lay_flip_y") { + if (view ()->active_cellview_index () >= 0) { + cm_lay_flip_y (); + } + } else if (symbol == "cm_lay_flip_x") { + if (view ()->active_cellview_index () >= 0) { + cm_lay_flip_x (); + } + } else if (symbol == "cm_sel_move") { + if (view ()->active_cellview_index () >= 0) { + cm_sel_move (); + } + } else if (symbol == "cm_sel_move_to") { + if (view ()->active_cellview_index () >= 0) { + cm_sel_move_to (); + } + } else if (symbol == "cm_sel_move_interactive") { + if (view ()->active_cellview_index () >= 0) { + cm_sel_move_interactive (); + } + } else if (symbol == "cm_sel_scale") { + if (view ()->active_cellview_index () >= 0) { + cm_sel_scale (); + } + } else if (symbol == "cm_sel_free_rot") { + if (view ()->active_cellview_index () >= 0) { + cm_sel_free_rot (); + } + } else if (symbol == "cm_sel_rot_ccw") { + if (view ()->active_cellview_index () >= 0) { + cm_sel_rot_ccw (); + } + } else if (symbol == "cm_sel_rot_cw") { + if (view ()->active_cellview_index () >= 0) { + cm_sel_rot_cw (); + } + } else if (symbol == "cm_sel_flip_y") { + if (view ()->active_cellview_index () >= 0) { + cm_sel_flip_y (); + } + } else if (symbol == "cm_sel_flip_x") { + if (view ()->active_cellview_index () >= 0) { + cm_sel_flip_x (); + } + } else if (symbol == "cm_edit_layer") { + if (view ()->active_cellview_index () >= 0) { + cm_edit_layer (); + } + } else if (symbol == "cm_delete_layer") { + if (view ()->active_cellview_index () >= 0) { + cm_delete_layer (); + } + } else if (symbol == "cm_clear_layer") { + if (view ()->active_cellview_index () >= 0) { + cm_clear_layer (); + } + } else if (symbol == "cm_copy_layer") { + if (view ()->active_cellview_index () >= 0) { + cm_copy_layer (); + } + } else if (symbol == "cm_new_layer") { + if (view ()->active_cellview_index () >= 0) { + cm_new_layer (); + } + } else if (symbol == "cm_layout_props") { + lay::LayoutPropertiesForm lp_form (view (), view (), "layout_props_form"); + lp_form.exec (); + } else if (symbol == "cm_layout_stats") { + lay::LayoutStatisticsForm lp_form (view (), view (), "layout_props_form"); + lp_form.exec (); + } else if (symbol == "cm_reload") { + cm_reload (); + } else if (symbol == "cm_inc_max_hier") { + int new_to = view ()->get_max_hier_levels () + 1; + view ()->set_hier_levels (std::make_pair (view ()->get_min_hier_levels (), new_to)); + } else if (symbol == "cm_dec_max_hier") { + int new_to = view ()->get_max_hier_levels () > 0 ? view ()->get_max_hier_levels () - 1 : 0; + view ()->set_hier_levels (std::make_pair (std::min (view ()->get_min_hier_levels (), new_to), new_to)); + } else if (symbol == "cm_max_hier") { + view ()->max_hier (); + } else if (symbol == "cm_max_hier_0") { + view ()->set_hier_levels (std::make_pair (std::min (view ()->get_min_hier_levels (), 0), 0)); + } else if (symbol == "cm_max_hier_1") { + view ()->set_hier_levels (std::make_pair (std::min (view ()->get_min_hier_levels (), 0), 1)); + } else if (symbol == "cm_prev_display_state") { + if (view ()->has_prev_display_state ()) { + view ()->prev_display_state (); + } + } else if (symbol == "cm_next_display_state") { + if (view ()->has_next_display_state ()) { + view ()->next_display_state (); + } + } else if (symbol == "cm_redraw") { + view ()->redraw (); + } else if (symbol == "cm_cell_delete") { + cm_cell_delete (); + } else if (symbol == "cm_cell_replace") { + cm_cell_replace (); + } else if (symbol == "cm_cell_rename") { + cm_cell_rename (); + } else if (symbol == "cm_cell_flatten") { + cm_cell_flatten (); + } else if (symbol == "cm_cell_select") { + cm_cell_select (); + } else if (symbol == "cm_cell_hide") { + cm_cell_hide (); + } else if (symbol == "cm_cell_show") { + cm_cell_show (); + } else if (symbol == "cm_cell_show_all") { + cm_cell_show_all (); + } else if (symbol == "cm_cell_user_properties") { + if (view ()->active_cellview_index () >= 0) { + cm_cell_user_properties (); + } + } else if (symbol == "cm_lv_select_all") { + cm_select_all (); + } else if (symbol == "cm_lv_new_tab") { + cm_new_tab (); + } else if (symbol == "cm_lv_rename_tab") { + cm_rename_tab (); + } else if (symbol == "cm_lv_make_invalid") { + cm_make_invalid (); + } else if (symbol == "cm_lv_remove_tab") { + cm_remove_tab (); + } else if (symbol == "cm_lv_make_valid") { + cm_make_valid (); + } else if (symbol == "cm_lv_hide_all") { + cm_hide_all (); + } else if (symbol == "cm_lv_hide") { + cm_hide (); + } else if (symbol == "cm_lv_show_only") { + cm_show_only (); + } else if (symbol == "cm_lv_show_all") { + cm_show_all (); + } else if (symbol == "cm_lv_show") { + cm_show (); + } else if (symbol == "cm_lv_rename") { + cm_rename (); + } else if (symbol == "cm_lv_delete") { + cm_delete (); + } else if (symbol == "cm_lv_insert") { + cm_insert (); + } else if (symbol == "cm_lv_group") { + cm_group (); + } else if (symbol == "cm_lv_ungroup") { + cm_ungroup (); + } else if (symbol == "cm_lv_source") { + cm_source (); + } else if (symbol == "cm_lv_sort_by_name") { + cm_sort_by_name (); + } else if (symbol == "cm_lv_sort_by_ild") { + cm_sort_by_ild (); + } else if (symbol == "cm_lv_sort_by_idl") { + cm_sort_by_idl (); + } else if (symbol == "cm_lv_sort_by_ldi") { + cm_sort_by_ldi (); + } else if (symbol == "cm_lv_sort_by_dli") { + cm_sort_by_dli (); + } else if (symbol == "cm_lv_regroup_by_index") { + cm_regroup_by_index (); + } else if (symbol == "cm_lv_regroup_by_datatype") { + cm_regroup_by_datatype (); + } else if (symbol == "cm_lv_regroup_by_layer") { + cm_regroup_by_layer (); + } else if (symbol == "cm_lv_regroup_flatten") { + cm_regroup_flatten (); + } else if (symbol == "cm_lv_expand_all") { + cm_expand_all (); + } else if (symbol == "cm_lv_add_missing") { + cm_add_missing (); + } else if (symbol == "cm_lv_remove_unused") { + cm_remove_unused (); + } +} + +void +LayoutViewFunctions::cm_cell_user_properties () +{ + if (! view ()->hierarchy_panel ()) { + return; + } + + int cv_index = view ()->active_cellview_index (); + lay::LayoutView::cell_path_type path; + view ()->hierarchy_panel ()->current_cell (cv_index, path); + + if (cv_index >= 0 && path.size () > 0) { + + db::Layout &layout = view ()->cellview (cv_index)->layout (); + db::Cell &cell = layout.cell (path.back ()); + db::properties_id_type prop_id = cell.prop_id (); + + lay::UserPropertiesForm props_form (view ()); + if (props_form.show (view (), cv_index, prop_id)) { + + view ()->transaction (tl::to_string (QObject::tr ("Edit cell's user propertes"))); + cell.prop_id (prop_id); + view ()->commit (); + + } + + } +} + +void +LayoutViewFunctions::cm_cell_replace () +{ + if (! view ()->hierarchy_panel ()) { + return; + } + + int cv_index = view ()->active_cellview_index (); + std::vector paths; + view ()->hierarchy_panel ()->selected_cells (cv_index, paths); + + if (cv_index >= 0 && paths.size () > 0) { + + if (paths.size () > 1) { + throw tl::Exception (tl::to_string (QObject::tr ("Replace cell cannot be used when multiple cells are selected"))); + } + + db::Layout &layout = view ()->cellview (cv_index)->layout (); + + bool needs_to_ask = false; + for (std::vector::const_iterator p = paths.begin (); p != paths.end () && ! needs_to_ask; ++p) { + if (layout.is_valid_cell_index (p->back ()) && ! layout.cell (p->back ()).is_leaf ()) { + needs_to_ask = true; + } + } + + + lay::ReplaceCellOptionsDialog mode_dialog (view ()); + + db::cell_index_type with_cell = paths.front ().back (); + int mode = needs_to_ask ? m_del_cell_mode : 0; + + if (mode_dialog.exec_dialog (view ()->cellview (cv_index), mode, with_cell)) { + + if (needs_to_ask) { + m_del_cell_mode = mode; + } + + if (with_cell != paths.front ().back ()) { + + // remember the current path + lay::LayoutView::cell_path_type cell_path (view ()->cellview (cv_index).combined_unspecific_path ()); + + view ()->clear_selection (); + + view ()->transaction (tl::to_string (QObject::tr ("Replace cells"))); + + // replace instances of the target cell with the new cell + + db::Cell &target_cell = layout.cell (paths.front ().back ()); + + std::vector > parents; + for (db::Cell::parent_inst_iterator pi = target_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { + parents.push_back (std::make_pair (pi->parent_cell_index (), pi->child_inst ())); + } + + for (std::vector >::const_iterator p = parents.begin (); p != parents.end (); ++p) { + db::CellInstArray ia = p->second.cell_inst (); + ia.object ().cell_index (with_cell); + layout.cell (p->first).replace (p->second, ia); + } + + std::set cells_to_delete; + for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { + if (! p->empty () && layout.is_valid_cell_index (p->back ())) { + cells_to_delete.insert (p->back ()); + if (mode == 2) { + layout.cell (p->back ()).collect_called_cells (cells_to_delete); + } + } + } + + // support a propagation use case: + std::set cells_below_replacement_cell; + cells_below_replacement_cell.insert (with_cell); + layout.cell (with_cell).collect_called_cells (cells_below_replacement_cell); + for (std::set::const_iterator c = cells_below_replacement_cell.begin (); c != cells_below_replacement_cell.end (); ++c) { + cells_to_delete.erase (*c); + } + + if (mode == 0 || mode == 2) { + layout.delete_cells (cells_to_delete); + } else if (mode == 1) { + layout.prune_cells (cells_to_delete); + } + + layout.cleanup (); + + view ()->commit (); + + // If one of the cells in the path was deleted, establish a valid path + + bool needs_update = false; + for (size_t i = cell_path.size (); i > 0; ) { + --i; + if (! layout.is_valid_cell_index (cell_path [i])) { + cell_path.erase (cell_path.begin () + i, cell_path.end ()); + needs_update = true; + } + } + + if (needs_update) { + view ()->select_cell (cell_path, cv_index); + } + + } + + } + + } +} + +void +LayoutViewFunctions::cm_lay_convert_to_static () +{ + // end move operations, cancel edit operations + view ()->cancel_edits (); + view ()->clear_selection (); + + int cv_index = view ()->active_cellview_index (); + if (cv_index >= 0) { + + db::Layout &layout = view ()->cellview (cv_index)->layout (); + + view ()->transaction (tl::to_string (QObject::tr ("Convert all cells to static"))); + + std::vector cells; + for (db::Layout::const_iterator c = layout.begin (); c != layout.end (); ++c) { + cells.push_back (c->cell_index ()); + } + + std::map cell_map; + for (std::vector::const_iterator c = cells.begin (); c != cells.end (); ++c) { + if (layout.is_valid_cell_index (*c)) { + db::cell_index_type new_cell = layout.convert_cell_to_static (*c); + if (new_cell != *c) { + cell_map.insert (std::make_pair (*c, new_cell)); + } + } + } + + // rewrite instances + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + for (db::Cell::const_iterator i = c->begin (); ! i.at_end (); ++i) { + std::map::const_iterator cm = cell_map.find (i->cell_index ()); + if (cm != cell_map.end ()) { + db::CellInstArray ci = i->cell_inst (); + ci.object ().cell_index (cm->second); + c->replace (*i, ci); + } + } + } + + layout.cleanup (); + + view ()->commit (); + + } +} + +void +LayoutViewFunctions::cm_cell_convert_to_static () +{ + if (! view ()->hierarchy_panel ()) { + return; + } + + int cv_index = view ()->active_cellview_index (); + std::vector paths; + view ()->hierarchy_panel ()->selected_cells (cv_index, paths); + + if (cv_index >= 0 && paths.size () > 0) { + + db::Layout &layout = view ()->cellview (cv_index)->layout (); + + // remember the current path + lay::LayoutView::cell_path_type cell_path (view ()->cellview (cv_index).combined_unspecific_path ()); + + view ()->clear_selection (); + + view ()->transaction (tl::to_string (QObject::tr ("Convert cells to static"))); + + std::map cell_map; + + for (std::vector::iterator p = paths.begin (); p != paths.end (); ++p) { + if (! p->empty () && layout.is_valid_cell_index (p->back ())) { + db::cell_index_type new_cell = layout.convert_cell_to_static (p->back ()); + if (new_cell != p->back ()) { + cell_map.insert (std::make_pair (p->back (), new_cell)); + p->back () = new_cell; + } + } + } + + // rewrite instances + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + for (db::Cell::const_iterator i = c->begin (); ! i.at_end (); ++i) { + std::map::const_iterator cm = cell_map.find (i->cell_index ()); + if (cm != cell_map.end ()) { + db::CellInstArray ci = i->cell_inst (); + ci.object ().cell_index (cm->second); + c->replace (*i, ci); + } + } + } + + layout.cleanup (); + + view ()->commit (); + + // If one of the cells in the path was deleted, establish a valid path + + bool needs_update = false; + for (size_t i = cell_path.size (); i > 0; ) { + --i; + if (! layout.is_valid_cell_index (cell_path [i])) { + cell_path.erase (cell_path.begin () + i, cell_path.end ()); + needs_update = true; + } + } + + if (needs_update) { + view ()->select_cell (cell_path, cv_index); + } + + } +} + +static void +collect_cells_to_delete (const db::Layout &layout, const db::Cell &cell, std::set &called) +{ + // don't delete proxies - they are deleted later when the layout is cleaned + for (db::Cell::child_cell_iterator cc = cell.begin_child_cells (); ! cc.at_end (); ++cc) { + if (called.find (*cc) == called.end () && !layout.cell (*cc).is_proxy ()) { + called.insert (*cc); + collect_cells_to_delete (layout, layout.cell (*cc), called); + } + } +} + +void +LayoutViewFunctions::cm_cell_delete () +{ + if (! view ()->hierarchy_panel ()) { + return; + } + + int cv_index = view ()->active_cellview_index (); + std::vector paths; + view ()->hierarchy_panel ()->selected_cells (cv_index, paths); + + if (cv_index >= 0 && paths.size () > 0) { + + db::Layout &layout = view ()->cellview (cv_index)->layout (); + + bool needs_to_ask = false; + for (std::vector::const_iterator p = paths.begin (); p != paths.end () && ! needs_to_ask; ++p) { + if (layout.is_valid_cell_index (p->back ()) && ! layout.cell (p->back ()).is_leaf ()) { + needs_to_ask = true; + } + } + + int mode = m_del_cell_mode; + if (! needs_to_ask) { + mode = 0; + } + + lay::DeleteCellModeDialog mode_dialog (view ()); + if (! needs_to_ask || mode_dialog.exec_dialog (mode)) { + + if (needs_to_ask) { + m_del_cell_mode = mode; + } + + // remember the current path + lay::LayoutView::cell_path_type cell_path (view ()->cellview (cv_index).combined_unspecific_path ()); + + view ()->clear_selection (); + + std::set cells_to_delete; + for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { + if (! p->empty () && layout.is_valid_cell_index (p->back ())) { + cells_to_delete.insert (p->back ()); + if (mode == 2) { + collect_cells_to_delete (layout, layout.cell (p->back ()), cells_to_delete); + } + } + } + + view ()->transaction (tl::to_string (QObject::tr ("Delete cells"))); + + if (mode == 0 || mode == 2) { + layout.delete_cells (cells_to_delete); + } else if (mode == 1) { + layout.prune_cells (cells_to_delete); + } + + layout.cleanup (); + + view ()->commit (); + + // If one of the cells in the path was deleted, establish a valid path + + bool needs_update = false; + for (size_t i = cell_path.size (); i > 0; ) { + --i; + if (! layout.is_valid_cell_index (cell_path [i])) { + cell_path.erase (cell_path.begin () + i, cell_path.end ()); + needs_update = true; + } + } + + if (needs_update) { + view ()->select_cell (cell_path, cv_index); + } + + } + + } +} + +void +LayoutViewFunctions::cm_layer_copy () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->copy (); + } +} + +void +LayoutViewFunctions::cm_layer_cut () +{ + if (view ()->control_panel ()) { + db::Transaction trans (manager (), tl::to_string (QObject::tr ("Cut Layers"))); + view ()->control_panel ()->cut (); + } +} + +void +LayoutViewFunctions::cm_layer_paste () +{ + if (view ()->control_panel ()) { + db::Transaction trans (manager (), tl::to_string (QObject::tr ("Paste Layers"))); + view ()->control_panel ()->paste (); + } +} + +void +LayoutViewFunctions::cm_cell_cut () +{ + if (view ()->hierarchy_panel ()) { + // TODO: currently the hierarchy panel's cut function does it's own transaction handling. + // Otherwise the cut function is not working propertly. + view ()->hierarchy_panel ()->cut (); + } +} + +void +LayoutViewFunctions::cm_cell_paste () +{ + if (view ()->hierarchy_panel ()) { + db::Transaction trans (manager (), tl::to_string (QObject::tr ("Paste Cells"))); + view ()->hierarchy_panel ()->paste (); + } +} + +void +LayoutViewFunctions::cm_cell_copy () +{ + if (view ()->hierarchy_panel ()) { + view ()->hierarchy_panel ()->copy (); + } +} + +void +LayoutViewFunctions::cm_cell_flatten () +{ + if (! view ()->hierarchy_panel ()) { + return; + } + + tl_assert (view ()->is_editable ()); + + int cv_index = view ()->active_cellview_index (); + if (cv_index >= 0) { + + const lay::CellView &cv = view ()->cellview (cv_index); + if (cv.is_valid ()) { + + std::vector paths; + view ()->hierarchy_panel ()->selected_cells (cv_index, paths); + if (paths.empty ()) { + throw tl::Exception (tl::to_string (QObject::tr ("No cells selected for flattening"))); + } + + for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { + if (p->size () > 0 && cv->layout ().cell (p->back ()).is_proxy ()) { + throw tl::Exception (tl::to_string (QObject::tr ("Cannot use this function on a PCell or library cell"))); + } + } + + FlattenInstOptionsDialog options_dialog (view ()); + + int flatten_insts_levels = -1; + bool prune = true; + if (options_dialog.exec_dialog (flatten_insts_levels, prune) && flatten_insts_levels != 0) { + + bool supports_undo = true; + + if (manager () && manager ()->is_enabled ()) { + + lay::TipDialog td (QApplication::activeWindow (), + tl::to_string (QObject::tr ("Undo buffering for the following operation can be memory and time consuming.\nChoose \"Yes\" to use undo buffering or \"No\" for no undo buffering. Warning: in the latter case, the undo history will be lost.\n\nChoose undo buffering?")), + "flatten-undo-buffering", + lay::TipDialog::yesnocancel_buttons); + + lay::TipDialog::button_type button = lay::TipDialog::null_button; + td.exec_dialog (button); + if (button == lay::TipDialog::cancel_button) { + return; + } + + supports_undo = (button == lay::TipDialog::yes_button); + + } else { + supports_undo = false; + } + + view ()->cancel_edits (); + view ()->clear_selection (); + + if (manager ()) { + if (! supports_undo) { + manager ()->clear (); + } else { + manager ()->transaction (tl::to_string (QObject::tr ("Flatten cell"))); + } + } + + db::Layout &layout = cv->layout (); + + std::set child_cells; + for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { + if (p->size () > 0) { + layout.cell (p->back ()).collect_called_cells (child_cells); + } + } + + // don't flatten cells which are child cells of the cells to flatten + std::set cells_to_flatten; + for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { + if (p->size () > 0 && child_cells.find (p->back ()) == child_cells.end ()) { + cells_to_flatten.insert (p->back ()); + } + } + + for (std::set::const_iterator c = cells_to_flatten.begin (); c != cells_to_flatten.end (); ++c) { + db::Cell &target_cell = layout.cell (*c); + layout.flatten (target_cell, flatten_insts_levels, prune); + } + + layout.cleanup (); + + if (supports_undo && manager ()) { + manager ()->commit (); + } + + } + + } + + } +} + +void +LayoutViewFunctions::cm_cell_rename () +{ + if (! view ()->hierarchy_panel ()) { + return; + } + + int cv_index = view ()->active_cellview_index (); + lay::LayoutView::cell_path_type path; + view ()->hierarchy_panel ()->current_cell (cv_index, path); + + if (cv_index >= 0 && path.size () > 0) { + + lay::RenameCellDialog name_dialog (view ()); + + db::Layout &layout = view ()->cellview (cv_index)->layout (); + std::string name (layout.cell_name (path.back ())); + if (name_dialog.exec_dialog (layout, name)) { + + view ()->transaction (tl::to_string (QObject::tr ("Rename cell"))); + layout.rename_cell (path.back (), name.c_str ()); + view ()->commit (); + + } + + } +} + +void +LayoutViewFunctions::cm_cell_select () +{ + if (view ()->hierarchy_panel ()) { + view ()->hierarchy_panel ()->cm_cell_select (); + } +} + +void +LayoutViewFunctions::cm_open_current_cell () +{ + view ()->set_current_cell_path (view ()->active_cellview_index (), view ()->cellview (view ()->active_cellview_index ()).combined_unspecific_path ()); +} + +void +LayoutViewFunctions::cm_cell_hide () +{ + if (view ()->hierarchy_panel ()) { + + std::vector paths; + view ()->hierarchy_panel ()->selected_cells (view ()->active_cellview_index (), paths); + + view ()->transaction (tl::to_string (QObject::tr ("Hide cell"))); + + for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { + if (! p->empty ()) { + view ()->hide_cell (p->back (), view ()->active_cellview_index ()); + } + } + + view ()->commit (); + + } +} + +void +LayoutViewFunctions::cm_cell_show () +{ + if (view ()->hierarchy_panel ()) { + + std::vector paths; + view ()->hierarchy_panel ()->selected_cells (view ()->active_cellview_index (), paths); + + view ()->transaction (tl::to_string (QObject::tr ("Show cell"))); + + for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { + if (! p->empty ()) { + view ()->show_cell (p->back (), view ()->active_cellview_index ()); + } + } + + view ()->commit (); + + } +} + +void +LayoutViewFunctions::cm_cell_show_all () +{ + if (view ()->hierarchy_panel ()) { + view ()->transaction (tl::to_string (QObject::tr ("Show all cells"))); + view ()->show_all_cells (); + view ()->commit (); + } +} + +void +LayoutViewFunctions::cm_select_all () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_select_all (); + } +} + +void +LayoutViewFunctions::cm_new_tab () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_new_tab (); + } +} + +void +LayoutViewFunctions::cm_remove_tab () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_remove_tab (); + } +} + +void +LayoutViewFunctions::cm_rename_tab () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_rename_tab (); + } +} + +void +LayoutViewFunctions::cm_make_invalid () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_make_invalid (); + } +} + +void +LayoutViewFunctions::cm_make_valid () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_make_valid (); + } +} + +void +LayoutViewFunctions::cm_hide () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_hide (); + } +} + +void +LayoutViewFunctions::cm_hide_all () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_hide_all (); + } +} + +void +LayoutViewFunctions::cm_show_only () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_show_only (); + } +} + +void +LayoutViewFunctions::cm_show_all () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_show_all (); + } +} + +void +LayoutViewFunctions::cm_show () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_show (); + } +} + +void +LayoutViewFunctions::cm_rename () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_rename (); + } +} + +void +LayoutViewFunctions::cm_delete () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_delete (); + } +} + +void +LayoutViewFunctions::cm_insert () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_insert (); + } +} + +void +LayoutViewFunctions::cm_group () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_group (); + } +} + +void +LayoutViewFunctions::cm_ungroup () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_ungroup (); + } +} + +void +LayoutViewFunctions::cm_source () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_source (); + } +} + +void +LayoutViewFunctions::cm_sort_by_name () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_sort_by_name (); + } +} + +void +LayoutViewFunctions::cm_sort_by_ild () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_sort_by_ild (); + } +} + +void +LayoutViewFunctions::cm_sort_by_idl () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_sort_by_idl (); + } +} + +void +LayoutViewFunctions::cm_sort_by_ldi () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_sort_by_ldi (); + } +} + +void +LayoutViewFunctions::cm_sort_by_dli () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_sort_by_dli (); + } +} + +void +LayoutViewFunctions::cm_regroup_by_index () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_regroup_by_index (); + } +} + +void +LayoutViewFunctions::cm_regroup_by_datatype () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_regroup_by_datatype (); + } +} + +void +LayoutViewFunctions::cm_regroup_by_layer () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_regroup_by_layer (); + } +} + +void +LayoutViewFunctions::cm_regroup_flatten () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_regroup_flatten (); + } +} + +void +LayoutViewFunctions::cm_expand_all () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_expand_all (); + } +} + +void +LayoutViewFunctions::cm_add_missing () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_add_missing (); + } +} + +void +LayoutViewFunctions::cm_remove_unused () +{ + view ()->remove_unused_layers (); +} + +void +LayoutViewFunctions::do_cm_duplicate (bool interactive) +{ + // Do duplicate simply by concatenating copy & paste currently. + // Save the clipboard state before in order to preserve the current content + db::Clipboard saved_clipboard; + db::Clipboard::instance ().swap (saved_clipboard); + + try { + view ()->copy (); + view ()->clear_selection (); + view ()->cancel (); + if (interactive) { + view ()->paste_interactive (); + } else { + view ()->paste (); + } + db::Clipboard::instance ().swap (saved_clipboard); + } catch (...) { + db::Clipboard::instance ().swap (saved_clipboard); + throw; + } +} + +void +LayoutViewFunctions::do_cm_paste (bool interactive) +{ + if (! db::Clipboard::instance ().empty ()) { + view ()->cancel (); + view ()->clear_selection (); + if (interactive) { + view ()->paste_interactive (); + } else { + view ()->paste (); + } + } +} + +void +LayoutViewFunctions::cm_new_cell () +{ + static double s_new_cell_window_size = 2.0; + static std::string s_new_cell_cell_name; + + NewCellPropertiesDialog cell_prop_dia (view ()); + if (cell_prop_dia.exec_dialog (& view ()->cellview (view ()->active_cellview_index ())->layout (), s_new_cell_cell_name, s_new_cell_window_size)) { + + db::cell_index_type new_ci = view ()->new_cell (view ()->active_cellview_index (), s_new_cell_cell_name.c_str ()); + view ()->select_cell (new_ci, view ()->active_cellview_index ()); + + db::DBox zb = db::DBox (-0.5 * s_new_cell_window_size, -0.5 * s_new_cell_window_size, 0.5 * s_new_cell_window_size, 0.5 * s_new_cell_window_size); + if (view ()->get_max_hier_levels () < 1 || view ()->get_min_hier_levels () > 0) { + view ()->zoom_box_and_set_hier_levels (zb, std::make_pair (0, 1)); + } else { + view ()->zoom_box (zb); + } + + } +} + +// TODO: this constant is defined in MainWindow.cc too ... +const int max_dirty_files = 15; + +void +LayoutViewFunctions::cm_reload () +{ + std::vector selected; + + if (view ()->cellviews () > 1) { + + lay::SelectCellViewForm form (0, view (), tl::to_string (QObject::tr ("Select Layouts To Reload"))); + form.select_all (); + + if (form.exec () == QDialog::Accepted) { + selected = form.selected_cellviews (); + } + + } else if (view ()->cellviews () > 0) { + selected.push_back (0); + } + + if (selected.size () > 0) { + + int dirty_layouts = 0; + std::string dirty_files; + + for (std::vector ::const_iterator i = selected.begin (); i != selected.end (); ++i) { + + const lay::CellView &cv = view ()->cellview (*i); + + if (cv->layout ().is_editable () && cv->is_dirty ()) { + ++dirty_layouts; + if (dirty_layouts == max_dirty_files) { + dirty_files += "\n..."; + } else if (dirty_layouts < max_dirty_files) { + if (! dirty_files.empty ()) { + dirty_files += "\n"; + } + dirty_files += cv->name (); + } + } + + } + + bool can_reload = true; + if (dirty_layouts != 0) { + + QMessageBox mbox (view ()); + mbox.setText (tl::to_qstring (tl::to_string (QObject::tr ("The following layouts need saving:\n\n")) + dirty_files + "\n\nPress 'Reload Without Saving' to reload anyhow and discard changes.")); + mbox.setWindowTitle (QObject::tr ("Save Needed")); + mbox.setIcon (QMessageBox::Warning); + QAbstractButton *yes_button = mbox.addButton (QObject::tr ("Reload Without Saving"), QMessageBox::YesRole); + mbox.addButton (QMessageBox::Cancel); + + mbox.exec (); + + can_reload = (mbox.clickedButton() == yes_button); + + } + + if (can_reload) { + + // Actually reload + for (std::vector ::const_iterator i = selected.begin (); i != selected.end (); ++i) { + view ()->reload_layout (*i); + } + + } + + } +} + +void +LayoutViewFunctions::do_transform (const db::DCplxTrans &tr) +{ + // end move operations, cancel edit operations + view ()->cancel_edits (); + view ()->lay::Editables::transform (tr); +} + +void +LayoutViewFunctions::transform_layout (const db::DCplxTrans &tr_mic) +{ + // end move operations, cancel edit operations + view ()->cancel_edits (); + view ()->clear_selection (); + + int cv_index = view ()->active_cellview_index (); + if (cv_index >= 0) { + + db::Layout &layout = view ()->cellview (cv_index)->layout (); + + db::ICplxTrans tr (db::DCplxTrans (1.0 / layout.dbu ()) * tr_mic * db::DCplxTrans (layout.dbu ())); + + bool has_proxy = false; + for (db::Layout::const_iterator c = layout.begin (); ! has_proxy && c != layout.end (); ++c) { + has_proxy = c->is_proxy (); + } + + if (has_proxy && + QMessageBox::question (view (), + QObject::tr ("Transforming PCells Or Library Cells"), + QObject::tr ("The layout contains PCells or library cells or both.\n" + "Any changes to such cells may be lost when their layout is refreshed later.\n" + "Consider using 'Convert all cells to static' before transforming the layout.\n" + "\n" + "Would you like to continue?\n" + "Choose 'Yes' to continue anyway. Choose 'No' to cancel."), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { + return; + } + + view ()->transaction (tl::to_string (QObject::tr ("Transform layout"))); + layout.transform (tr); + view ()->commit (); + + } +} + +void +LayoutViewFunctions::cm_lay_flip_x () +{ + transform_layout (db::DCplxTrans (db::FTrans::m90)); +} + +void +LayoutViewFunctions::cm_lay_flip_y () +{ + transform_layout (db::DCplxTrans (db::FTrans::m0)); +} + +void +LayoutViewFunctions::cm_lay_rot_ccw () +{ + db::DCplxTrans tr (db::DFTrans::r90); + transform_layout (db::DCplxTrans (db::FTrans::r90)); +} + +void +LayoutViewFunctions::cm_lay_rot_cw () +{ + transform_layout (db::DCplxTrans (db::FTrans::r270)); +} + +void +LayoutViewFunctions::cm_lay_free_rot () +{ + bool ok = false; + QString s = QInputDialog::getText (QApplication::activeWindow (), + QObject::tr ("Free rotation"), + QObject::tr ("Rotation angle in degree (counterclockwise)"), + QLineEdit::Normal, QString::fromUtf8 ("0.0"), + &ok); + + if (ok) { + + double angle = 0.0; + tl::from_string (tl::to_string (s), angle); + + transform_layout (db::DCplxTrans (1.0, angle, false, db::DVector ())); + + } +} + +void +LayoutViewFunctions::cm_lay_scale () +{ + bool ok = false; + QString s = QInputDialog::getText (QApplication::activeWindow (), + QObject::tr ("Scaling"), + QObject::tr ("Scaling factor"), + QLineEdit::Normal, QString::fromUtf8 ("1.0"), + &ok); + + if (ok) { + + double scale = 0.0; + tl::from_string (tl::to_string (s), scale); + + transform_layout (db::DCplxTrans (scale)); + + } +} + +void +LayoutViewFunctions::cm_lay_move () +{ + lay::MoveOptionsDialog options (view ()); + if (options.exec_dialog (m_move_dist)) { + transform_layout (db::DCplxTrans (m_move_dist)); + } +} + +void +LayoutViewFunctions::cm_sel_flip_x () +{ + db::DCplxTrans tr (db::DFTrans::m90); + db::DBox sel_bbox (view ()->lay::Editables::selection_bbox ()); + if (! sel_bbox.empty ()) { + tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); + } + do_transform (tr); +} + +void +LayoutViewFunctions::cm_sel_flip_y () +{ + db::DCplxTrans tr (db::DFTrans::m0); + db::DBox sel_bbox (view ()->lay::Editables::selection_bbox ()); + if (! sel_bbox.empty ()) { + tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); + } + do_transform (tr); +} + +void +LayoutViewFunctions::cm_sel_rot_ccw () +{ + db::DCplxTrans tr (db::DFTrans::r90); + db::DBox sel_bbox (view ()->lay::Editables::selection_bbox ()); + if (! sel_bbox.empty ()) { + tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); + } + do_transform (tr); +} + +void +LayoutViewFunctions::cm_sel_rot_cw () +{ + db::DCplxTrans tr (db::DFTrans::r270); + db::DBox sel_bbox (view ()->lay::Editables::selection_bbox ()); + if (! sel_bbox.empty ()) { + tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); + } + do_transform (tr); +} + +void +LayoutViewFunctions::cm_sel_free_rot () +{ + bool ok = false; + QString s = QInputDialog::getText (QApplication::activeWindow (), + QObject::tr ("Free rotation"), + QObject::tr ("Rotation angle in degree (counterclockwise)"), + QLineEdit::Normal, QString::fromUtf8 ("0.0"), + &ok); + + if (ok) { + + double angle = 0.0; + tl::from_string (tl::to_string (s), angle); + + db::DCplxTrans tr = db::DCplxTrans (1.0, angle, false, db::DVector ()); + db::DBox sel_bbox (view ()->lay::Editables::selection_bbox ()); + if (! sel_bbox.empty ()) { + tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); + } + do_transform (tr); + + } +} + +void +LayoutViewFunctions::cm_sel_scale () +{ + bool ok = false; + QString s = QInputDialog::getText (QApplication::activeWindow (), + QObject::tr ("Scaling"), + QObject::tr ("Scaling factor"), + QLineEdit::Normal, QString::fromUtf8 ("1.0"), + &ok); + + if (ok) { + + double scale = 0.0; + tl::from_string (tl::to_string (s), scale); + + db::DCplxTrans tr = db::DCplxTrans (scale); + db::DBox sel_bbox (view ()->lay::Editables::selection_bbox ()); + if (! sel_bbox.empty ()) { + tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); + } + do_transform (tr); + + } +} + +void +LayoutViewFunctions::cm_sel_move_interactive () +{ + if (view ()->move_service ()->begin_move ()) { + view ()->switch_mode (-1); // move mode + } +} + +void +LayoutViewFunctions::cm_sel_move_to () +{ + db::DBox sel_bbox (view ()->lay::Editables::selection_bbox ()); + if (sel_bbox.empty ()) { + throw tl::Exception (tl::to_string (QObject::tr ("Nothing selected to move"))); + } + + double x = sel_bbox.left () + (sel_bbox.width () * (1 + m_move_to_origin_mode_x) * 0.5); + double y = sel_bbox.bottom () + (sel_bbox.height () * (1 + m_move_to_origin_mode_y) * 0.5); + db::DPoint move_target (x, y); + + lay::MoveToOptionsDialog options (view ()); + if (options.exec_dialog (m_move_to_origin_mode_x, m_move_to_origin_mode_y, move_target)) { + + x = sel_bbox.left () + (sel_bbox.width () * (1 + m_move_to_origin_mode_x) * 0.5); + y = sel_bbox.bottom () + (sel_bbox.height () * (1 + m_move_to_origin_mode_y) * 0.5); + + do_transform (db::DCplxTrans (move_target - db::DPoint (x, y))); + + } +} + +void +LayoutViewFunctions::cm_sel_move () +{ + lay::MoveOptionsDialog options (view ()); + if (options.exec_dialog (m_move_dist)) { + do_transform (db::DCplxTrans (m_move_dist)); + } +} + +void +LayoutViewFunctions::cm_copy_layer () +{ + struct { int *cv; int *layer; } specs [] = { + { &m_copy_cva, &m_copy_layera }, + { &m_copy_cvr, &m_copy_layerr } + }; + + for (unsigned int i = 0; i < sizeof (specs) / sizeof (specs[0]); ++i) { + + int &cv = *(specs[i].cv); + int &layer = *(specs[i].layer); + + if (cv >= int (view ()->cellviews ())) { + cv = -1; + } + + int index = view ()->active_cellview_index (); + if (cv < 0) { + cv = index; + } + + if (cv < 0 || ! view ()->cellview (cv)->layout ().is_valid_layer ((unsigned int) layer)) { + layer = -1; + } + + } + + lay::DuplicateLayerDialog dialog (view ()); + if (dialog.exec_dialog (view (), m_copy_cva, m_copy_layera, m_copy_cvr, m_copy_layerr, m_duplicate_hier_mode, m_clear_before)) { + + bool supports_undo = true; + + if (manager () && manager ()->is_enabled ()) { + + lay::TipDialog td (QApplication::activeWindow (), + tl::to_string (QObject::tr ("Undo buffering for the following operation can be memory and time consuming.\nChoose \"Yes\" to use undo buffering or \"No\" for no undo buffering. Warning: in the latter case, the undo history will be lost.\n\nChoose undo buffering?")), + "copy-layer-undo-buffering", + lay::TipDialog::yesnocancel_buttons); + + lay::TipDialog::button_type button = lay::TipDialog::null_button; + td.exec_dialog (button); + if (button == lay::TipDialog::cancel_button) { + return; + } + + supports_undo = (button == lay::TipDialog::yes_button); + + } else { + supports_undo = false; + } + + view ()->cancel (); + + if (manager ()) { + if (! supports_undo) { + manager ()->clear (); + } else { + manager ()->transaction (tl::to_string (QObject::tr ("Duplicate layer"))); + } + } + + try { + + bool same_layout = (&view ()->cellview (m_copy_cvr)->layout () == &view ()->cellview (m_copy_cva)->layout ()); + if (same_layout && m_copy_layera == m_copy_layerr) { + throw tl::Exception (tl::to_string (QObject::tr ("Source and target layer must not be identical for duplicate operation"))); + } + + if (m_duplicate_hier_mode == 0) { + + // clear the result layer for all called cells in flat mode + if (m_clear_before) { + std::set called_cells; + called_cells.insert (view ()->cellview (m_copy_cvr).cell_index ()); + view ()->cellview (m_copy_cvr).cell ()->collect_called_cells (called_cells); + for (std::set::const_iterator c = called_cells.begin (); c != called_cells.end (); ++c) { + view ()->cellview (m_copy_cvr)->layout ().cell (*c).shapes (m_copy_layerr).clear (); + } + } + + db::Cell &target_cell = *view ()->cellview (m_copy_cvr).cell (); + + if (! same_layout) { + + // flat mode (different layouts) + db::PropertyMapper pm (view ()->cellview (m_copy_cvr)->layout (), view ()->cellview (m_copy_cva)->layout ()); + for (db::RecursiveShapeIterator si (view ()->cellview (m_copy_cva)->layout (), *view ()->cellview (m_copy_cva).cell (), m_copy_layera); ! si.at_end (); ++si) { + target_cell.shapes (m_copy_layerr).insert (*si, si.trans (), pm); + } + + } else { + + // flat mode (same layouts) + tl::ident_map pm1; + db::Shapes &res = target_cell.shapes (m_copy_layerr); + + db::Layout &layout = view ()->cellview (m_copy_cvr)->layout (); + try { + + // using update/start_layout and end_changes improves the performance since changing the + // shapes collection will invalidate the layout and cause updates inside the RecursiveShapeIerator + layout.update (); + layout.start_changes (); + for (db::RecursiveShapeIterator si (view ()->cellview (m_copy_cva)->layout (), *view ()->cellview (m_copy_cva).cell (), m_copy_layera); ! si.at_end (); ++si) { + res.insert (*si, si.trans (), pm1); + } + layout.end_changes (); + + } catch (...) { + layout.end_changes (); + throw; + } + + } + + } else if (m_duplicate_hier_mode == 1) { + + db::Cell &cell = *view ()->cellview (m_copy_cva).cell (); + db::Cell &target_cell = *view ()->cellview (m_copy_cvr).cell (); + + if (m_clear_before) { + target_cell.clear (m_copy_layerr); + } + + if (m_copy_cvr == m_copy_cva) { + + // current cell only mode: identical cell + cell.copy (m_copy_layera, m_copy_layerr); + + } else if (! same_layout) { + + // current cell only mode (different layouts) + db::PropertyMapper pm (view ()->cellview (m_copy_cvr)->layout (), view ()->cellview (m_copy_cva)->layout ()); + for (db::Shapes::shape_iterator si = view ()->cellview (m_copy_cva).cell ()->shapes (m_copy_layera).begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + target_cell.shapes (m_copy_layerr).insert (*si, pm); + } + + } else { + + // current cell only mode (same layouts, but different cells) + for (db::Shapes::shape_iterator si = view ()->cellview (m_copy_cva).cell ()->shapes (m_copy_layera).begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + target_cell.shapes (m_copy_layerr).insert (*si); + } + + } + + } else if (m_duplicate_hier_mode == 2) { + + // subcells cell by cell - source and target layout must be identical + std::set called_cells; + view ()->cellview (m_copy_cva).cell ()->collect_called_cells (called_cells); + called_cells.insert (view ()->cellview (m_copy_cva).cell_index ()); + + db::Layout &layout = view ()->cellview (m_copy_cva)->layout (); + for (std::set::const_iterator c = called_cells.begin (); c != called_cells.end (); ++c) { + db::Cell &cell = layout.cell (*c); + if (m_clear_before) { + cell.clear (m_copy_layerr); + } + cell.copy (m_copy_layera, m_copy_layerr); + } + + } + + if (manager () && supports_undo) { + manager ()->commit (); + } + + } catch (...) { + if (manager () && supports_undo) { + manager ()->commit (); + } + throw; + } + + } +} + +void +LayoutViewFunctions::cm_new_layer () +{ + int index = view ()->active_cellview_index (); + + if (index >= 0 && int (view ()->cellviews ()) > index) { + + const lay::CellView &cv = view ()->cellview (index); + + lay::NewLayerPropertiesDialog prop_dia (view ()); + if (prop_dia.exec_dialog (cv, m_new_layer_props)) { + + for (unsigned int l = 0; l < cv->layout ().layers (); ++l) { + if (cv->layout ().is_valid_layer (l) && cv->layout ().get_properties (l).log_equal (m_new_layer_props)) { + throw tl::Exception (tl::to_string (QObject::tr ("A layer with that signature already exists: ")) + m_new_layer_props.to_string ()); + } + } + + view ()->transaction (tl::to_string (QObject::tr ("New layer"))); + + unsigned int l = cv->layout ().insert_layer (m_new_layer_props); + std::vector nl; + nl.push_back (l); + view ()->add_new_layers (nl, index); + view ()->update_content (); + + view ()->commit (); + + } + + } +} + +void +LayoutViewFunctions::cm_align_cell_origin () +{ + int cv_index = view ()->active_cellview_index (); + if (cv_index >= 0) { + + const db::Cell *cell = view ()->cellview (cv_index).cell (); + if (! cell) { + return; + } + if (cell->is_proxy ()) { + throw tl::Exception (tl::to_string (QObject::tr ("Cannot use this function on a PCell or library cell"))); + } + + lay::AlignCellOptionsDialog dialog (view ()); + if (dialog.exec_dialog (m_align_cell_options)) { + + view ()->clear_selection (); + + view ()->transaction (tl::to_string (QObject::tr ("Align cell origin"))); + + db::Box bbox; + + if (m_align_cell_options.visible_only) { + for (lay::LayerPropertiesConstIterator l = view ()->begin_layers (); !l.at_end (); ++l) { + if (! l->has_children () && l->layer_index () >= 0 && l->cellview_index () == cv_index && l->visible (true /*real*/)) { + bbox += cell->bbox (l->layer_index ()); + } + } + } else { + bbox = cell->bbox (); + } + + db::Coord refx, refy; + switch (m_align_cell_options.mode_x) { + case -1: + refx = bbox.left (); + break; + case 1: + refx = bbox.right (); + break; + default: + refx = bbox.center ().x (); + break; + } + switch (m_align_cell_options.mode_y) { + case -1: + refy = bbox.bottom (); + break; + case 1: + refy = bbox.top (); + break; + default: + refy = bbox.center ().y (); + break; + } + + db::Layout &layout = view ()->cellview (cv_index)->layout (); + db::Cell &nc_cell = layout.cell (cell->cell_index ()); + + db::Trans t (db::Vector (-refx + db::coord_traits::rounded (m_align_cell_options.xpos / layout.dbu ()), -refy + db::coord_traits::rounded (m_align_cell_options.ypos / layout.dbu ()))); + + for (unsigned int i = 0; i < layout.layers (); ++i) { + if (layout.is_valid_layer (i)) { + db::Shapes &shapes = nc_cell.shapes (i); + for (db::Shapes::shape_iterator s = shapes.begin (db::ShapeIterator::All); ! s.at_end (); ++s) { + shapes.transform (*s, t); + } + } + } + + for (db::Cell::const_iterator inst = nc_cell.begin (); ! inst.at_end (); ++inst) { + nc_cell.transform (*inst, t); + } + + if (m_align_cell_options.adjust_parents) { + + std::vector > insts_to_modify; + for (db::Cell::parent_inst_iterator pi = nc_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { + insts_to_modify.push_back (std::make_pair (& layout.cell (pi->parent_cell_index ()), pi->child_inst ())); + } + + db::Trans ti (db::Vector (refx, refy)); + for (std::vector >::const_iterator im = insts_to_modify.begin (); im != insts_to_modify.end (); ++im) { + im->first->transform (im->second, db::Trans (db::Vector (im->second.complex_trans ().trans (db::Vector (refx, refy))))); + } + + } + + view ()->commit (); + + } + + } +} + +void +LayoutViewFunctions::cm_edit_layer () +{ + lay::LayerPropertiesConstIterator sel = view ()->current_layer (); + if (sel.is_null ()) { + throw tl::Exception (tl::to_string (QObject::tr ("No layer selected for editing it's properties"))); + } + + int index = sel->cellview_index (); + if (sel->has_children () || index < 0 || int (view ()->cellviews ()) <= index || sel->layer_index () < 0) { + throw tl::Exception (tl::to_string (QObject::tr ("No valid layer selected for editing it's properties"))); + } + + const lay::CellView &cv = view ()->cellview (index); + + db::LayerProperties layer_props = cv->layout ().get_properties ((unsigned int) sel->layer_index ()); + + lay::NewLayerPropertiesDialog prop_dia (view ()); + if (prop_dia.exec_dialog (cv, layer_props)) { + + for (unsigned int l = 0; l < cv->layout ().layers (); ++l) { + if (cv->layout ().is_valid_layer (l) && int (l) != sel->layer_index () && cv->layout ().get_properties (l).log_equal (layer_props)) { + throw tl::Exception (tl::to_string (QObject::tr ("A layer with that signature already exists: ")) + layer_props.to_string ()); + } + } + + view ()->transaction (tl::to_string (QObject::tr ("Edit layer"))); + + cv->layout ().set_properties (sel->layer_index (), layer_props); + + lay::LayerProperties lp (*sel); + lay::ParsedLayerSource s = lp.source (false); + s.layer (layer_props.layer); + s.datatype (layer_props.datatype); + if (! layer_props.name.empty ()) { + s.name (layer_props.name); + } else { + s.clear_name (); + } + lp.set_source (s); + view ()->set_properties (sel, lp); + + view ()->update_content (); + + view ()->commit (); + + } +} + +void +LayoutViewFunctions::cm_delete_layer () +{ + std::vector sel = view ()->selected_layers (); + std::sort (sel.begin (), sel.end (), CompareLayerIteratorBottomUp ()); + + // collect valid layers + std::vector valid_sel; + std::set > valid_layers; + for (std::vector::const_iterator si = sel.begin (); si != sel.end (); ++si) { + int cv_index = (*si)->cellview_index (); + const lay::CellView &cv = view ()->cellview (cv_index); + if (!(*si)->has_children () && cv_index >= 0 && int (view ()->cellviews ()) > cv_index && (*si)->layer_index () >= 0 && cv.is_valid ()) { + valid_sel.push_back (*si); + valid_layers.insert (std::make_pair (&cv->layout (), (*si)->layer_index ())); + } + } + + if (valid_sel.empty ()) { + throw tl::Exception (tl::to_string (QObject::tr ("No or no valid layer selected for deleting them"))); + } + + view ()->cancel_edits (); + view ()->clear_selection (); + + view ()->transaction (tl::to_string (QObject::tr ("Delete layers"))); + + // Hint: delete_layer must come before the layers are actually deleted in because + // for undo this must be the last thing to do (otherwise the layout is not propertly set up) + + for (std::vector::const_iterator si = valid_sel.begin (); si != valid_sel.end (); ++si) { + lay::LayerPropertiesConstIterator lp = *si; + view ()->delete_layer (lp); + } + + for (std::set >::const_iterator li = valid_layers.begin (); li != valid_layers.end(); ++li) { + + unsigned int layer_index = li->second; + db::Layout *layout = li->first; + + for (db::Layout::iterator c = layout->begin (); c != layout->end (); ++c) { + c->shapes (layer_index).clear (); + } + + layout->delete_layer (layer_index); + + } + + view ()->update_content (); + + view ()->commit (); +} + +void +LayoutViewFunctions::cm_clear_layer () +{ + std::vector sel = view ()->selected_layers (); + if (sel.empty ()) { + throw tl::Exception (tl::to_string (QObject::tr ("No layer selected for clearing"))); + } + + lay::ClearLayerModeDialog mode_dialog (view ()); + if (mode_dialog.exec_dialog (m_layer_hier_mode)) { + + view ()->cancel_edits (); + view ()->clear_selection (); + + view ()->transaction (tl::to_string (QObject::tr ("Clear layer"))); + + for (std::vector::const_iterator si = sel.begin (); si != sel.end (); ++si) { + + if (! (*si)->has_children () && (*si)->layer_index () >= 0 && view ()->cellview ((*si)->cellview_index ()).is_valid ()) { + + int layer_index = (*si)->layer_index (); + const lay::CellView &cv = view ()->cellview ((*si)->cellview_index ()); + + if (m_layer_hier_mode == 0) { + cv.cell ()->clear ((unsigned int) layer_index); + } else if (m_layer_hier_mode == 1) { + + cv.cell ()->clear ((unsigned int) layer_index); + + std::set called_cells; + cv.cell ()->collect_called_cells (called_cells); + for (std::set ::const_iterator cc = called_cells.begin (); cc != called_cells.end (); ++cc) { + cv->layout ().cell (*cc).clear ((unsigned int) layer_index); + } + + } else { + cv->layout ().clear_layer ((unsigned int) layer_index); + } + + } + + } + + view ()->commit (); + + } +} + +// ------------------------------------------------------------ +// Declaration of the "plugin" for the menu entries + +class LayoutViewPluginDeclaration + : public lay::PluginDeclaration +{ +public: + virtual void get_menu_entries (std::vector &menu_entries) const + { + std::string at; + + // secret menu entries + at = "@secrets.end"; + menu_entries.push_back (lay::menu_item ("cm_paste_interactive", "paste_interactive:edit", at, tl::to_string (QObject::tr ("Paste Interactive")))); + menu_entries.push_back (lay::menu_item ("cm_duplicate_interactive", "duplicate_interactive:edit", at, tl::to_string (QObject::tr ("Duplicate Interactive")))); + menu_entries.push_back (lay::menu_item ("cm_sel_move_interactive", "sel_move_interactive:edit", at, tl::to_string (QObject::tr ("Move Interactive")))); + + at = "edit_menu.end"; + menu_entries.push_back (lay::menu_item ("cm_undo", "undo:edit", at, tl::to_string (QObject::tr ("Undo(Ctrl+Z)")))); + menu_entries.push_back (lay::menu_item ("cm_redo", "redo:edit", at, tl::to_string (QObject::tr ("Redo(Ctrl+Y)")))); + + menu_entries.push_back (lay::separator ("basic_group", at)); + menu_entries.push_back (lay::submenu ("layout_menu:edit:edit_mode", at, tl::to_string (QObject::tr ("Layout")))); + { + std::string at = "edit_menu.layout_menu.end"; + menu_entries.push_back (lay::menu_item ("cm_lay_flip_x", "lay_flip_x:edit_mode", at, tl::to_string (QObject::tr ("Flip Horizontally")))); + menu_entries.push_back (lay::menu_item ("cm_lay_flip_y", "lay_flip_y:edit_mode", at, tl::to_string (QObject::tr ("Flip Vertically")))); + menu_entries.push_back (lay::menu_item ("cm_lay_rot_cw", "lay_rot_cw:edit_mode", at, tl::to_string (QObject::tr ("Rotate Clockwise")))); + menu_entries.push_back (lay::menu_item ("cm_lay_rot_ccw", "lay_rot_ccw:edit_mode", at, tl::to_string (QObject::tr ("Rotate Counterclockwise")))); + menu_entries.push_back (lay::menu_item ("cm_lay_free_rot", "lay_free_rot:edit_mode", at, tl::to_string (QObject::tr ("Rotation By Angle")))); + menu_entries.push_back (lay::menu_item ("cm_lay_scale", "lay_scale:edit_mode", at, tl::to_string (QObject::tr ("Scale")))); + menu_entries.push_back (lay::menu_item ("cm_lay_move", "lay_move:edit_mode", at, tl::to_string (QObject::tr ("Move By")))); + menu_entries.push_back (lay::separator ("cellop_group", at)); + menu_entries.push_back (lay::menu_item ("cm_lay_convert_to_static", "lay_convert_to_static:edit_mode", at, tl::to_string (QObject::tr ("Convert All Cells To Static")))); + } + + menu_entries.push_back (lay::submenu ("cell_menu:edit:edit_mode", at, tl::to_string (QObject::tr ("Cell")))); + { + std::string at = "edit_menu.cell_menu.end"; + menu_entries.push_back (lay::menu_item ("cm_new_cell", "new_cell:edit:edit_mode", at, tl::to_string (QObject::tr ("New Cell")))); + menu_entries.push_back (lay::menu_item ("cm_cell_delete", "delete_cell:edit:edit_mode", at, tl::to_string (QObject::tr ("Delete Cell")))); + menu_entries.push_back (lay::menu_item ("cm_cell_rename", "rename_cell:edit:edit_mode", at, tl::to_string (QObject::tr ("Rename Cell")))); + menu_entries.push_back (lay::menu_item ("cm_cell_replace", "replace_cell:edit:edit_mode", at, tl::to_string (QObject::tr ("Replace Cell")))); + menu_entries.push_back (lay::menu_item ("cm_cell_flatten", "flatten_cell:edit:edit_mode", at, tl::to_string (QObject::tr ("Flatten Cell")))); + menu_entries.push_back (lay::separator ("ops_group", at)); + menu_entries.push_back (lay::menu_item ("cm_adjust_origin", "adjust_cell_origin:edit:edit_mode", at, tl::to_string (QObject::tr ("Adjust Origin")))); + menu_entries.push_back (lay::menu_item ("cm_cell_convert_to_static", "convert_cell_to_static:edit_mode", at, tl::to_string (QObject::tr ("Convert Cell To Static")))); + menu_entries.push_back (lay::separator ("props_group", at)); + menu_entries.push_back (lay::menu_item ("cm_cell_user_properties", "user_properties", at, tl::to_string (QObject::tr ("User Properties")))); + } + + menu_entries.push_back (lay::submenu ("layer_menu:edit:edit_mode", at, tl::to_string (QObject::tr ("Layer")))); + { + std::string at = "edit_menu.layer_menu.end"; + menu_entries.push_back (lay::menu_item ("cm_new_layer", "new_layer:edit:edit_mode", at, tl::to_string (QObject::tr ("New Layer")))); + menu_entries.push_back (lay::menu_item ("cm_clear_layer", "clear_layer:edit:edit_mode", at, tl::to_string (QObject::tr ("Clear Layer")))); + menu_entries.push_back (lay::menu_item ("cm_delete_layer", "delete_layer:edit:edit_mode", at, tl::to_string (QObject::tr ("Delete Layer")))); + menu_entries.push_back (lay::menu_item ("cm_copy_layer", "copy_layer:edit:edit_mode", at, tl::to_string (QObject::tr ("Copy Layer")))); + menu_entries.push_back (lay::menu_item ("cm_edit_layer", "edit_layer:edit:edit_mode", at, tl::to_string (QObject::tr ("Edit Layer Specification")))); + } + + menu_entries.push_back (lay::submenu ("selection_menu:edit", at, tl::to_string (QObject::tr ("Selection")))); + { + std::string at = "edit_menu.selection_menu.end"; + menu_entries.push_back (lay::menu_item ("cm_sel_flip_x", "sel_flip_x", at, tl::to_string (QObject::tr ("Flip Horizontally")))); + menu_entries.push_back (lay::menu_item ("cm_sel_flip_y", "sel_flip_y", at, tl::to_string (QObject::tr ("Flip Vertically")))); + menu_entries.push_back (lay::menu_item ("cm_sel_rot_cw", "sel_rot_cw", at, tl::to_string (QObject::tr ("Rotate Clockwise")))); + menu_entries.push_back (lay::menu_item ("cm_sel_rot_ccw", "sel_rot_ccw", at, tl::to_string (QObject::tr ("Rotate Counterclockwise")))); + menu_entries.push_back (lay::menu_item ("cm_sel_free_rot", "sel_free_rot", at, tl::to_string (QObject::tr ("Rotation By Angle")))); + menu_entries.push_back (lay::menu_item ("cm_sel_scale", "sel_scale", at, tl::to_string (QObject::tr ("Scale")))); + menu_entries.push_back (lay::menu_item ("cm_sel_move", "sel_move", at, tl::to_string (QObject::tr ("Move By")))); + menu_entries.push_back (lay::menu_item ("cm_sel_move_to", "sel_move_to", at, tl::to_string (QObject::tr ("Move To")))); + } + + menu_entries.push_back (lay::separator ("utils_group", at)); + menu_entries.push_back (lay::submenu ("utils_menu:edit:edit_mode", at, tl::to_string (QObject::tr ("Utilities")))); + + menu_entries.push_back (lay::separator ("misc_group", at)); + menu_entries.push_back (lay::menu_item ("cm_delete", "delete:edit", at, tl::to_string (QObject::tr ("Delete(Del)")))); + menu_entries.push_back (lay::menu_item ("cm_show_properties", "show_properties:edit", at, tl::to_string (QObject::tr ("Properties(Q)")))); + + menu_entries.push_back (lay::separator ("cpc_group", at)); + menu_entries.push_back (lay::menu_item ("cm_copy", "copy:edit", at, tl::to_string (QObject::tr ("Copy(Ctrl+C)")))); + menu_entries.push_back (lay::menu_item ("cm_cut", "cut:edit", at, tl::to_string (QObject::tr ("Cut(Ctrl+X)")))); + menu_entries.push_back (lay::menu_item ("cm_paste", "paste:edit", at, tl::to_string (QObject::tr ("Paste(Ctrl+V)")))); + menu_entries.push_back (lay::menu_item ("cm_duplicate", "duplicate:edit", at, tl::to_string (QObject::tr ("Duplicate(Ctrl+B)")))); + + menu_entries.push_back (lay::separator ("modes_group", at)); + menu_entries.push_back (lay::submenu ("mode_menu", at, tl::to_string (QObject::tr ("Mode")))); + + menu_entries.push_back (lay::submenu ("select_menu", at, tl::to_string (QObject::tr ("Select")))); + { + std::string at = "edit_menu.select_menu.end"; + menu_entries.push_back (lay::menu_item ("cm_select_all", "select_all", at, tl::to_string (QObject::tr ("Select All")))); + menu_entries.push_back (lay::menu_item ("cm_unselect_all", "unselect_all", at, tl::to_string (QObject::tr ("Unselect All")))); + menu_entries.push_back (lay::separator ("edit_select_basic_group", at)); + menu_entries.push_back (lay::menu_item ("lv:enable_all", "enable_all", at, tl::to_string (QObject::tr ("Enable All")))); + menu_entries.push_back (lay::menu_item ("lv:disable_all", "disable_all", at, tl::to_string (QObject::tr ("Disable All")))); + menu_entries.push_back (lay::separator ("edit_select_individual_group", at)); + }; + + menu_entries.push_back (lay::separator ("cancel_group", at)); + menu_entries.push_back (lay::menu_item ("cm_cancel", "cancel", at, tl::to_string (QObject::tr ("Cancel(Esc)")))); + + at = "bookmark_menu.end"; + menu_entries.push_back (lay::submenu ("goto_bookmark_menu", at, tl::to_string (QObject::tr ("Goto Bookmark")))); + menu_entries.push_back (lay::menu_item ("cm_bookmark_view", "bookmark_view", at, tl::to_string (QObject::tr ("Bookmark This View")))); + + menu_entries.push_back (lay::separator ("bookmark_mgm_group", at)); + menu_entries.push_back (lay::menu_item ("cm_manage_bookmarks", "manage_bookmarks", at, tl::to_string (QObject::tr ("Manage Bookmarks")))); + menu_entries.push_back (lay::menu_item ("cm_load_bookmarks", "load_bookmarks", at, tl::to_string (QObject::tr ("Load Bookmarks")))); + menu_entries.push_back (lay::menu_item ("cm_save_bookmarks", "save_bookmarks", at, tl::to_string (QObject::tr ("Save Bookmarks")))); + + at = "zoom_menu.end"; + menu_entries.push_back (lay::submenu ("global_trans", at, tl::to_string (QObject::tr ("Global Transformation")))); + { + std::string at = "zoom_menu.global_trans.end"; + menu_entries.push_back (lay::config_menu_item ("r0", at, tl::to_string (QObject::tr ("\\(r0\\)<:/r0.png>")), cfg_global_trans, "?r0 *1 0,0")); + menu_entries.push_back (lay::config_menu_item ("r90", at, tl::to_string (QObject::tr ("\\(r90\\)<:/r90.png>")), cfg_global_trans, "?r90 *1 0,0")); + menu_entries.push_back (lay::config_menu_item ("r180", at, tl::to_string (QObject::tr ("\\(r180\\)<:/r180.png>")), cfg_global_trans, "?r180 *1 0,0")); + menu_entries.push_back (lay::config_menu_item ("r270", at, tl::to_string (QObject::tr ("\\(r270\\)<:/r270.png>")), cfg_global_trans, "?r270 *1 0,0")); + menu_entries.push_back (lay::config_menu_item ("m0", at, tl::to_string (QObject::tr ("\\(m0\\)<:/m0.png>")), cfg_global_trans, "?m0 *1 0,0")); + menu_entries.push_back (lay::config_menu_item ("m45", at, tl::to_string (QObject::tr ("\\(m45\\)<:/m45.png>")), cfg_global_trans, "?m45 *1 0,0")); + menu_entries.push_back (lay::config_menu_item ("m90", at, tl::to_string (QObject::tr ("\\(m90\\)<:/m90.png>")), cfg_global_trans, "?m90 *1 0,0")); + menu_entries.push_back (lay::config_menu_item ("m135", at, tl::to_string (QObject::tr ("\\(m135\\)<:/m135.png>")), cfg_global_trans, "?m135 *1 0,0")); + } + + menu_entries.push_back (lay::separator ("hier_group", at)); + menu_entries.push_back (lay::menu_item ("cm_max_hier", "max_hier", at, tl::to_string (QObject::tr ("Full Hierarchy(*)")))); + menu_entries.push_back (lay::menu_item ("cm_max_hier_0", "max_hier_0", at, tl::to_string (QObject::tr ("Box Only(0)")))); + menu_entries.push_back (lay::menu_item ("cm_max_hier_1", "max_hier_1", at, tl::to_string (QObject::tr ("Top Level Only(1)")))); + menu_entries.push_back (lay::menu_item ("cm_inc_max_hier", "inc_max_hier", at, tl::to_string (QObject::tr ("Increment Hierarchy(+)")))); + menu_entries.push_back (lay::menu_item ("cm_dec_max_hier", "dec_max_hier", at, tl::to_string (QObject::tr ("Decrement Hierarchy(-)")))); + + menu_entries.push_back (lay::separator ("zoom_group", at)); + menu_entries.push_back (lay::menu_item ("cm_zoom_fit", "zoom_fit", at, tl::to_string (QObject::tr ("Zoom Fit(F2)")))); + menu_entries.push_back (lay::menu_item ("cm_zoom_fit_sel", "zoom_fit_sel", at, tl::to_string (QObject::tr ("Zoom Fit Selection(Shift+F2)")))); + menu_entries.push_back (lay::menu_item ("cm_zoom_in", "zoom_in", at, tl::to_string (QObject::tr ("Zoom In(Return)")))); + menu_entries.push_back (lay::menu_item ("cm_zoom_out", "zoom_out", at, tl::to_string (QObject::tr ("Zoom Out(Shift+Return)")))); + /* disabled because that interferes with the use of the arrow keys for moving the selection + MenuLayoutEntry::separator ("pan_group"); + menu_entries.push_back (lay::menu_item ("cm_pan_up", "pan_up", at, tl::to_string (QObject::tr ("Pan Up(Up)")))); + menu_entries.push_back (lay::menu_item ("cm_pan_down", "pan_down", at, tl::to_string (QObject::tr ("Pan Down(Down)")))); + menu_entries.push_back (lay::menu_item ("cm_pan_left", "pan_left", at, tl::to_string (QObject::tr ("Pan Left(Left)")))); + menu_entries.push_back (lay::menu_item ("cm_pan_right", "pan_right", at, tl::to_string (QObject::tr ("Pan Right(Right)")))); + */ + + menu_entries.push_back (lay::separator ("redraw_group", at)); + menu_entries.push_back (lay::menu_item ("cm_redraw", "redraw", at, tl::to_string (QObject::tr ("Redraw")))); + menu_entries.push_back (lay::separator ("state_group", at)); + menu_entries.push_back (lay::menu_item_copy ("cm_prev_display_state", "prev_display_state", at, "@toolbar.prev_display_state")); + menu_entries.push_back (lay::menu_item_copy ("cm_next_display_state", "next_display_state", at, "@toolbar.next_display_state")); + + menu_entries.push_back (lay::separator ("select_group", at)); + menu_entries.push_back (lay::menu_item ("cm_select_cell", "select_cell:edit", at, tl::to_string (QObject::tr ("Select Cell")))); + menu_entries.push_back (lay::menu_item ("cm_select_current_cell", "select_current_cell", at, tl::to_string (QObject::tr ("Show As New Top(Ctrl+S)")))); + menu_entries.push_back (lay::menu_item ("cm_goto_position", "goto_position", at, tl::to_string (QObject::tr ("Goto Position(Ctrl+G)")))); + + // Add a hook for inserting new items after the modes + menu_entries.push_back (lay::separator ("end_modes", "@toolbar.end")); + + } + + bool menu_activated (const std::string &symbol) const + { + if (symbol == "lv:enable_all") { + + for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { + cls->set_editable_enabled (true); + } + return true; + + } else if (symbol == "lv:disable_all") { + + for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { + cls->set_editable_enabled (false); + } + return true; + + } else { + return false; + } + } + + void implements_primary_mouse_modes (std::vector > > &modes) + { + std::vector mode_titles; + lay::LayoutView::intrinsic_mouse_modes (&mode_titles); + + int mode_id = 0; + for (std::vector ::const_iterator t = mode_titles.begin (); t != mode_titles.end (); ++t, --mode_id) { + // modes: pair(title, pair(insert_pos, id)) + modes.push_back (std::make_pair (*t, std::make_pair ("edit_menu.mode_menu.end;@toolbar.end_modes", mode_id))); + } + } + + lay::Plugin *create_plugin (db::Manager *manager, Dispatcher *, LayoutView *view) const + { + return new LayoutViewFunctions (manager, view); + } +}; + +static tl::RegisteredClass config_decl (new LayoutViewPluginDeclaration (), -10, "LayoutViewPlugin"); + +} // namespace lay diff --git a/src/laybasic/laybasic/layLayoutViewFunctions.h b/src/laybasic/laybasic/layLayoutViewFunctions.h new file mode 100644 index 000000000..15b2f3f56 --- /dev/null +++ b/src/laybasic/laybasic/layLayoutViewFunctions.h @@ -0,0 +1,171 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_layLayoutViewFunctions +#define HDR_layLayoutViewFunctions + +#include "laybasicCommon.h" + +#include "layPlugin.h" +#include "layDialogs.h" // For AlignCellOptions + +#include "dbTrans.h" +#include "dbLayerProperties.h" + +namespace lay { + +class LayoutView; + +/** + * @brief The layout view's functions implementation + */ +class LAYBASIC_PUBLIC LayoutViewFunctions + : public lay::Plugin +{ +public: + /** + * @brief Constructor + */ + LayoutViewFunctions (db::Manager *manager, lay::LayoutView *view); + + /** + * @brief Destructor + */ + ~LayoutViewFunctions (); + + // Plugin interface implementation + void menu_activated (const std::string &symbol); + + // menu callbacks + void cm_new_layer (); + void cm_clear_layer (); + void cm_delete_layer (); + void cm_copy_layer (); + void cm_align_cell_origin (); + void cm_edit_layer (); + void cm_lay_convert_to_static (); + void cm_lay_flip_x (); + void cm_lay_flip_y (); + void cm_lay_rot_cw (); + void cm_lay_rot_ccw (); + void cm_lay_free_rot (); + void cm_lay_scale (); + void cm_lay_move (); + void cm_sel_flip_x (); + void cm_sel_flip_y (); + void cm_sel_rot_cw (); + void cm_sel_rot_ccw (); + void cm_sel_free_rot (); + void cm_sel_scale (); + void cm_sel_move (); + void cm_sel_move_to (); + void cm_sel_move_interactive (); + + // forwarded to the layer control panel + void cm_new_tab (); + void cm_rename_tab (); + void cm_remove_tab (); + void cm_select_all (); + void cm_make_valid (); + void cm_make_invalid (); + void cm_hide (); + void cm_hide_all (); + void cm_show (); + void cm_show_all (); + void cm_show_only (); + void cm_rename (); + void cm_delete (); + void cm_insert (); + void cm_group (); + void cm_ungroup (); + void cm_source (); + void cm_sort_by_name (); + void cm_sort_by_ild (); + void cm_sort_by_idl (); + void cm_sort_by_ldi (); + void cm_sort_by_dli (); + void cm_regroup_by_index (); + void cm_regroup_by_datatype (); + void cm_regroup_by_layer (); + void cm_regroup_flatten (); + void cm_expand_all (); + void cm_add_missing (); + void cm_remove_unused (); + void cm_layer_copy (); + void cm_layer_cut (); + void cm_layer_paste (); + + // forwarded to the cell control panel + void cm_cell_user_properties (); + void cm_cell_flatten (); + void cm_cell_rename (); + void cm_cell_replace (); + void cm_cell_delete (); + void cm_cell_select (); + void cm_open_current_cell (); + void cm_cell_hide (); + void cm_cell_show (); + void cm_cell_show_all (); + void cm_cell_copy (); + void cm_cell_cut (); + void cm_cell_paste (); + void cm_cell_convert_to_static (); + +protected: + lay::LayoutView *view () + { + return mp_view; + } + + db::Manager *manager () + { + return mp_manager; + } + + void do_cm_duplicate (bool interactive); + void do_cm_paste (bool interactive); + void cm_new_cell (); + void cm_reload (); + + void do_transform (const db::DCplxTrans &tr); + void transform_layout (const db::DCplxTrans &tr); + +private: + lay::LayoutView *mp_view; + db::Manager *mp_manager; + db::LayerProperties m_new_layer_props; + db::DVector m_move_dist; + int m_move_to_origin_mode_x, m_move_to_origin_mode_y; + lay::AlignCellOptions m_align_cell_options; + int m_del_cell_mode; + int m_layer_hier_mode; + int m_duplicate_hier_mode; + bool m_clear_before; + int m_copy_cva, m_copy_cvr; + int m_copy_layera, m_copy_layerr; +}; + +} + +#endif + diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index 2e3e2b4c3..95d6f4f00 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -184,7 +184,8 @@ SOURCES = \ layDispatcher.cc \ laySelectCellViewForm.cc \ layLayoutStatisticsForm.cc \ - gsiDeclLayNetlistBrowserDialog.cc + gsiDeclLayNetlistBrowserDialog.cc \ + layLayoutViewFunctions.cc HEADERS = \ gtf.h \ @@ -285,7 +286,8 @@ HEADERS = \ layGenericSyntaxHighlighter.h \ layDispatcher.h \ laySelectCellViewForm.h \ - layLayoutStatisticsForm.h + layLayoutStatisticsForm.h \ + layLayoutViewFunctions.h INCLUDEPATH += $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC $$LYM_INC DEPENDPATH += $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC $$LYM_INC From f810eae7a0ed7d9ae4089ed5f4d6e7a5d995afa1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 2 Aug 2020 17:41:22 +0200 Subject: [PATCH 028/113] New feature: repeat selection 1. "Select Next Item" will repeat the selection on the last single-click location or select the current transient selected (essentially like a single click) This feature is by default bound to "Space" 2. "Select Next Item too" will do the same, but add to the selection This feature is by default bound to "Shift+Space" --- src/laybasic/laybasic/layLayoutViewFunctions.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/laybasic/laybasic/layLayoutViewFunctions.cc b/src/laybasic/laybasic/layLayoutViewFunctions.cc index 5e58747a2..c7198ac11 100644 --- a/src/laybasic/laybasic/layLayoutViewFunctions.cc +++ b/src/laybasic/laybasic/layLayoutViewFunctions.cc @@ -84,6 +84,10 @@ LayoutViewFunctions::menu_activated (const std::string &symbol) view ()->select (db::DBox (), lay::Editable::Reset); } else if (symbol == "cm_select_all") { view ()->select (view ()->full_box (), lay::Editable::Replace); + } else if (symbol == "cm_select_next_item") { + view ()->repeat_selection (lay::Editable::Replace); + } else if (symbol == "cm_select_next_item_add") { + view ()->repeat_selection (lay::Editable::Add); } else if (symbol == "cm_lv_paste") { cm_layer_paste (); } else if (symbol == "cm_lv_cut") { @@ -2058,6 +2062,8 @@ public: menu_entries.push_back (lay::menu_item ("cm_paste_interactive", "paste_interactive:edit", at, tl::to_string (QObject::tr ("Paste Interactive")))); menu_entries.push_back (lay::menu_item ("cm_duplicate_interactive", "duplicate_interactive:edit", at, tl::to_string (QObject::tr ("Duplicate Interactive")))); menu_entries.push_back (lay::menu_item ("cm_sel_move_interactive", "sel_move_interactive:edit", at, tl::to_string (QObject::tr ("Move Interactive")))); + menu_entries.push_back (lay::menu_item ("cm_select_next_item", "select_next_item:edit", at, tl::to_string (QObject::tr ("Select Next Item(Space)")))); + menu_entries.push_back (lay::menu_item ("cm_select_next_item_add", "select_next_item_add:edit", at, tl::to_string (QObject::tr ("Select Next Item too(Shift+Space)")))); at = "edit_menu.end"; menu_entries.push_back (lay::menu_item ("cm_undo", "undo:edit", at, tl::to_string (QObject::tr ("Undo(Ctrl+Z)")))); From 1887fd715aef2ae10646b2c88554c747f635599b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 3 Aug 2020 10:34:16 +0200 Subject: [PATCH 029/113] First new implementation of "tap" and some enhancements/bugfixes/refactoring - Layer icons can be obtained now - Icon dimension is arbitrary - Scaling icons for layer list - More robust event handling in case of pop-up menus (selector) - "Tap" will noe pop up a menu with all layers below the cursor. When selected, the current layer is switched. --- src/edt/edt/edtMainService.cc | 67 +++- src/laybasic/laybasic/layBitmap.cc | 33 +- src/laybasic/laybasic/layBitmap.h | 11 + src/laybasic/laybasic/layFinder.cc | 89 ++--- src/laybasic/laybasic/layFinder.h | 39 ++- src/laybasic/laybasic/layLayerControlPanel.cc | 35 +- src/laybasic/laybasic/layLayerTreeModel.cc | 304 ++++++++++-------- src/laybasic/laybasic/layLayerTreeModel.h | 10 + src/laybasic/laybasic/laySelector.cc | 73 +++-- src/laybasic/laybasic/laySelector.h | 7 +- src/laybasic/laybasic/layViewObject.cc | 40 ++- src/laybasic/laybasic/layViewObject.h | 31 ++ 12 files changed, 470 insertions(+), 269 deletions(-) diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index a5068438c..1fd2d3340 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -30,7 +30,11 @@ #include "tlExceptions.h" #include "layLayoutView.h" #include "layDialogs.h" +#include "laySelector.h" #include "layCellSelectionForm.h" +#include "layFinder.h" +#include "layLayerProperties.h" +#include "layLayerTreeModel.h" #include "tlProgress.h" #include "edtPlugin.h" #include "edtMainService.h" @@ -41,6 +45,8 @@ #include "edtEditorOptionsPages.h" #include +#include +#include namespace edt { @@ -1941,21 +1947,60 @@ MainService::cm_make_array () void MainService::cm_tap () { - tl_assert (view ()->is_editable ()); - check_no_guiding_shapes (); + if (! view ()->view_object_widget ()->mouse_in_window ()) { + return; + } - std::vector edt_services = view ()->get_plugins (); + lay::ShapeFinder finder (true /*point mode*/, false /*all hierarchy levels*/, db::ShapeIterator::All, 0); - // get (common) cellview index of the selected shapes - for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { - for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) { - const lay::CellView &cv = view ()->cellview (s->cv_index ()); - if (cv.is_valid () && ! s->is_cell_inst ()) { - view ()->set_current_layer (s->cv_index (), cv->layout ().get_properties (s->layer ())); - return; - } + // capture all objects in point mode (default: capture one only) + finder.set_catch_all (true); + + // go through all visible layers of all cellviews + db::DPoint pt = view ()->view_object_widget ()->mouse_position_um (); + finder.find (view (), db::DBox (pt, pt)); + + std::set > layers_in_selection; + + for (lay::ShapeFinder::iterator f = finder.begin (); f != finder.end (); ++f) { + // ignore guiding shapes + if (f->layer () != view ()->cellview (f->cv_index ())->layout ().guiding_shape_layer ()) { + layers_in_selection.insert (std::make_pair (f->cv_index (), f->layer ())); } } + + std::vector tapped_layers; + for (lay::LayerPropertiesConstIterator lp = view ()->begin_layers (view ()->current_layer_list ()); ! lp.at_end (); ++lp) { + const lay::LayerPropertiesNode *ln = lp.operator-> (); + if (layers_in_selection.find (std::make_pair ((unsigned int) ln->cellview_index (), (unsigned int) ln->layer_index ())) != layers_in_selection.end ()) { + tapped_layers.push_back (lp); + } + } + + if (tapped_layers.empty ()) { + return; + } + + // List the layers under the cursor as pop up a menu + + std::auto_ptr menu (new QMenu (view ())); + menu->show (); + + int icon_size = menu->style ()->pixelMetric (QStyle::PM_ButtonIconSize); + + QPoint mp = view ()->view_object_widget ()->mapToGlobal (view ()->view_object_widget ()->mouse_position ()); + + for (std::vector::const_iterator l = tapped_layers.begin (); l != tapped_layers.end (); ++l) { + QAction *a = menu->addAction (lay::LayerTreeModel::icon_for_layer (*l, view (), icon_size, icon_size, 0, true), tl::to_qstring ((*l)->source (true).to_string ())); + a->setData (int (l - tapped_layers.begin ())); + } + + QAction *action = menu->exec (mp); + if (action) { + int index = action->data ().toInt (); + lay::LayerPropertiesConstIterator iter = tapped_layers [index]; + view ()->set_current_layer (iter); + } } void diff --git a/src/laybasic/laybasic/layBitmap.cc b/src/laybasic/laybasic/layBitmap.cc index 1c9c76336..ad857de2e 100644 --- a/src/laybasic/laybasic/layBitmap.cc +++ b/src/laybasic/laybasic/layBitmap.cc @@ -380,7 +380,38 @@ Bitmap::fill (unsigned int y, unsigned int x1, unsigned int x2) } } -struct PosCompareF +void +Bitmap::clear (unsigned int y, unsigned int x1, unsigned int x2) +{ + unsigned int b1 = x1 / 32; + + uint32_t *sl = scanline (y); + sl += b1; + + unsigned int b = x2 / 32 - b1; + if (b == 0) { + + *sl &= ~masks [x2 % 32] | masks [x1 % 32]; + + } else if (b > 0) { + + *sl++ &= masks [x1 % 32]; + while (b > 1) { + *sl++ = 0; + b--; + } + + unsigned int m = masks [x2 % 32]; + // Hint: if x2==width and width%32==0, sl must not be accessed. This is guaranteed by + // checking if m != 0. + if (m) { + *sl &= ~m; + } + + } +} + +struct PosCompareF { bool operator() (const RenderEdge &a, const RenderEdge &b) const { diff --git a/src/laybasic/laybasic/layBitmap.h b/src/laybasic/laybasic/layBitmap.h index e225134e5..07dc3bb2a 100644 --- a/src/laybasic/laybasic/layBitmap.h +++ b/src/laybasic/laybasic/layBitmap.h @@ -246,6 +246,17 @@ public: */ void fill (unsigned int y, unsigned int x1, unsigned int x2); + /** + * @brief Clears the given part of the scanline + * + * Same as fill(), but resets the bits. + * + * @param y The scanline + * @param x1 The start coordinate + * @param x2 The end coordinate + */ + void clear (unsigned int y, unsigned int x1, unsigned int x2); + /** * @brief Merges the "from" bitmap into this * diff --git a/src/laybasic/laybasic/layFinder.cc b/src/laybasic/laybasic/layFinder.cc index 835b5c1ac..cd5d8215b 100644 --- a/src/laybasic/laybasic/layFinder.cc +++ b/src/laybasic/laybasic/layFinder.cc @@ -62,7 +62,7 @@ static int inst_point_sel_tests = 10000; Finder::Finder (bool point_mode, bool top_level_sel) : m_min_level (0), m_max_level (0), - mp_layout (0), mp_view (0), m_cv_index (0), m_point_mode (point_mode), m_top_level_sel (top_level_sel) + mp_layout (0), mp_view (0), m_cv_index (0), m_point_mode (point_mode), m_catch_all (false), m_top_level_sel (top_level_sel) { m_distance = std::numeric_limits::max (); } @@ -558,41 +558,32 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const if (match) { + lay::ObjectInstPath found; + found.set_cv_index (m_cv_index); + found.set_topcell (m_topcell); + found.assign_path (path ().begin (), path ().end ()); + found.set_layer (*l); + found.set_shape (*shape); + if (mp_excludes) { // with an exclude list first create the selection item so we can check // if it's part of the exclude set. - lay::ObjectInstPath found; - found.set_cv_index (m_cv_index); - found.set_topcell (m_topcell); - found.assign_path (path ().begin (), path ().end ()); - found.set_layer (*l); - found.set_shape (*shape); - // in point mode just store the found object that has the least "distance" and is // not in the exclude set - if (mp_excludes->find (found) == mp_excludes->end () && closer (d)) { + match = (mp_excludes->find (found) == mp_excludes->end ()); - if (m_founds.empty ()) { - m_founds.push_back (found); - } else { - m_founds.front () = found; - } - } + } - } else if (closer (d)) { + if (match && (catch_all () || closer (d))) { // in point mode just store that found that has the least "distance" - if (m_founds.empty ()) { - m_founds.push_back (lay::ObjectInstPath ()); + if (m_founds.empty () || catch_all ()) { + m_founds.push_back (found); } - m_founds.back ().set_cv_index (m_cv_index); - m_founds.back ().set_topcell (m_topcell); - m_founds.back ().assign_path (path ().begin (), path ().end ()); - m_founds.back ().set_layer (*l); - m_founds.back ().set_shape (*shape); + m_founds.back () = found; } @@ -848,52 +839,38 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d if (match) { + lay::ObjectInstPath found; + found.set_cv_index (m_cv_index); + found.set_topcell (m_topcell); + found.assign_path (path ().begin (), path ().end ()); + + // add the selected instance as the last element of the path + db::InstElement el; + el.inst_ptr = *inst; + if (! m_full_arrays) { + el.array_inst = p; + } + found.add_path (el); + if (mp_excludes) { // with an exclude list first create the selection item so we can check // if it's part of the exclude set. - lay::ObjectInstPath found; - found.set_cv_index (m_cv_index); - found.set_topcell (m_topcell); - found.assign_path (path ().begin (), path ().end ()); - - // add the selected instance as the last element of the path - db::InstElement el; - el.inst_ptr = *inst; - if (! m_full_arrays) { - el.array_inst = p; - } - found.add_path (el); - // in point mode just store the found object that has the least "distance" and is // not in the exclude set - if (mp_excludes->find (found) == mp_excludes->end () && closer (d)) { - if (m_founds.empty ()) { - m_founds.push_back (found); - } else { - m_founds.front () = found; - } - } + match = (mp_excludes->find (found) == mp_excludes->end ()); - } else if (closer (d)) { + } + + if (match && (catch_all () || closer (d))) { // in point mode just store that found that has the least "distance" - if (m_founds.empty ()) { + if (m_founds.empty () || catch_all ()) { m_founds.push_back (lay::ObjectInstPath ()); } - m_founds.back ().set_cv_index (m_cv_index); - m_founds.back ().set_topcell (m_topcell); - m_founds.back ().assign_path (path ().begin (), path ().end ()); - - // add the selected instance as the last element of the path - db::InstElement el; - el.inst_ptr = *inst; - if (! m_full_arrays) { - el.array_inst = p; - } - m_founds.back ().add_path (el); + m_founds.back () = found; } diff --git a/src/laybasic/laybasic/layFinder.h b/src/laybasic/laybasic/layFinder.h index c183f5346..98684c80c 100644 --- a/src/laybasic/laybasic/layFinder.h +++ b/src/laybasic/laybasic/layFinder.h @@ -58,13 +58,42 @@ public: * * The point_mode is true, if the finder is supposed to operate in "point mode". * In point mode, the center of the search region is the reference point. In - * non-point mode, every relevant found inside the search region should be - * recorded. + * non-point mode, every relevant found inside the search region will be + * recorded (also see point_mode method). * The base class implementation just stores this flag and provides a read * accessor with the point_mode () method. */ Finder (bool point_mode, bool top_level_sel); + /** + * @brief Gets a flag indicating whether point mode is enabled + * If point mode is enabled in the constructor, the first will check for objects overlapping the + * point (rather than being inside the box) and by default select a single object only. + * See also "set_catch_all". + */ + bool point_mode () const + { + return m_point_mode; + } + + /** + * @brief Gets a flag indicating the capture all founds even in point mode + */ + bool catch_all () const + { + return m_catch_all; + } + + /** + * @brief Sets a flag indicating the capture all founds even in point mode + * By default, in point mode only the closest found is returned. To catch all + * founds in point mode too, set this flag to true. + */ + void set_catch_all (bool f) + { + m_catch_all = f; + } + /** * @brief Destructor (just provided to please the compiler) */ @@ -87,11 +116,6 @@ protected: return m_layers; } - bool point_mode () const - { - return m_point_mode; - } - const std::vector &path () const { return m_path; @@ -166,6 +190,7 @@ private: std::vector m_layers; double m_distance; bool m_point_mode; + bool m_catch_all; bool m_top_level_sel; db::box_convert m_box_convert; db::box_convert m_cell_box_convert; diff --git a/src/laybasic/laybasic/layLayerControlPanel.cc b/src/laybasic/laybasic/layLayerControlPanel.cc index be8eda762..28ab6a8bc 100644 --- a/src/laybasic/laybasic/layLayerControlPanel.cc +++ b/src/laybasic/laybasic/layLayerControlPanel.cc @@ -60,36 +60,6 @@ namespace lay { -// -------------------------------------------------------------------- -// LCPTreeItemDelegate declaration & implementation - -/** - * @brief A layer tree widget helper class - * - * A specialization of the ItemDelegate that bypasses the computation - * of sizeHint and returns the pixmap's size directly for higher - * performance. - */ - -class LCPItemDelegate : public QItemDelegate -{ -public: - LCPItemDelegate (QWidget *parent) - : QItemDelegate (parent) - { } - -private: - virtual QSize - sizeHint (const QStyleOptionViewItem &style, const QModelIndex &index) const - { - if (index.column () == 0) { - return QSize (40, 16); - } else { - return QItemDelegate::sizeHint (style, index); - } - } -}; - // -------------------------------------------------------------------- // LCPTreeWidget declaration & implementation @@ -98,7 +68,6 @@ LCPTreeWidget::LCPTreeWidget (QWidget *parent, lay::LayerTreeModel *model, const { setObjectName (QString::fromUtf8 (name)); setModel (model); - setItemDelegate (new LCPItemDelegate (this)); #if QT_VERSION >= 0x040200 setAllColumnsShowFocus (true); #endif @@ -326,7 +295,10 @@ LayerControlPanel::LayerControlPanel (lay::LayoutView *view, db::Manager *manage mp_model = new lay::LayerTreeModel (this, view); mp_layer_list = new LCPTreeWidget (this, mp_model, "layer_tree"); + mp_layer_list->setUniformRowHeights (true); mp_model->set_font (mp_layer_list->font ()); + mp_layer_list->setIconSize (mp_model->icon_size ()); + /* * At least with Qt 4.2.x setting uniform row heights has a strange side effect: * If a range is selected and the first selection is scrolled out of view, the @@ -343,7 +315,6 @@ LayerControlPanel::LayerControlPanel (lay::LayoutView *view, db::Manager *manage mp_layer_list->header ()->hide (); mp_layer_list->setSelectionMode (QTreeView::ExtendedSelection); mp_layer_list->setRootIsDecorated (false); - mp_layer_list->setIconSize (QSize (32, 16)); // Custom resize mode makes the columns as narrow as possible #if QT_VERSION >= 0x050000 mp_layer_list->header ()->setSectionResizeMode (QHeaderView::ResizeToContents); diff --git a/src/laybasic/laybasic/layLayerTreeModel.cc b/src/laybasic/laybasic/layLayerTreeModel.cc index 3a6ce2f77..c622bf5e4 100644 --- a/src/laybasic/laybasic/layLayerTreeModel.cc +++ b/src/laybasic/laybasic/layLayerTreeModel.cc @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -558,6 +559,161 @@ LayerTreeModel::empty_within_view_predicate (const QModelIndex &index) const } } +LAYBASIC_PUBLIC +QIcon +LayerTreeModel::icon_for_layer (const lay::LayerPropertiesConstIterator &iter, lay::LayoutView *view, unsigned int w, unsigned int h, unsigned int di_off, bool no_state) +{ + h = std::max ((unsigned int) 16, h); + w = std::max ((unsigned int) 16, w); + + lay::color_t def_color = 0x808080; + lay::color_t fill_color = iter->has_fill_color (true) ? iter->eff_fill_color (true) : def_color; + lay::color_t frame_color = iter->has_frame_color (true) ? iter->eff_frame_color (true) : def_color; + + QImage image (w, h, QImage::Format_ARGB32); + image.fill (view->background_color ().rgb ()); + + // upper scanline is a dummy one + uint32_t *sl0 = (uint32_t *) image.scanLine (0); + uint32_t transparent = QColor (Qt::transparent).rgba (); + for (size_t i = 0; i < w; ++i) { + *sl0++ = transparent; + } + + // TODO: adjust the resolution according to the oversampling mode + lay::Bitmap fill (w, h, 1.0); + lay::Bitmap frame (w, h, 1.0); + lay::Bitmap text (w, h, 1.0); + lay::Bitmap vertex (w, h, 1.0); + + unsigned int wp = w - 1; + + if (! no_state && ! iter->visible (true)) { + + wp = w / 4; + + // Show the arrow if it is invisible also locally. + if (! iter->visible (false)) { + + unsigned int aw = h / 4; + unsigned int ap = w / 2 - 1; + for (unsigned int i = 0; i <= aw; ++i) { + text.fill (h / 2 - 1 - i, ap, ap + aw - i + 1); + text.fill (h / 2 - 1 + i, ap, ap + aw - i + 1); + } + + } + + } + + if (! no_state && view->no_stipples ()) { + // Show a partial stipple pattern only for "no stipple" mode + for (unsigned int i = 1; i < h - 2; ++i) { + fill.fill (i, w - 1 - w / 4, w); + } + } else { + for (unsigned int i = 1; i < h - 2; ++i) { + fill.fill (i, w - 1 - wp, w); + } + } + + int lw = iter->width (true); + if (lw < 0) { + // default line width is 0 for parents and 1 for leafs + lw = iter->has_children () ? 0 : 1; + } + + int p0 = lw / 2; + p0 = std::max (0, std::min (int (w / 4 - 1), p0)); + + int p1 = (lw - 1) / 2; + p1 = std::max (0, std::min (int (w / 4 - 1), p1)); + + int p0x = p0, p1x = p1; + unsigned int ddx = 0; + unsigned int ddy = h - 2 - p1 - p0; + if (iter->xfill (true)) { + ddx = wp - p0 - p1 - 1; + } + unsigned int d = ddx / 2; + + frame.fill (p0, w - 1 - (wp - p1), w); + frame.fill (h - 2 - p1, w - 1 - (wp - p1), w); + + for (unsigned int i = p0; i < h - 2; ++i) { + + frame.fill (i, w - 1 - p0, w - p0); + frame.fill (i, w - 1 - (wp - p1), w - (wp - p1)); + frame.fill (i, w - 1 - p0x, w - p0x); + frame.fill (i, w - 1 - (wp - p1x), w - (wp - p1x)); + + while (d < ddx) { + d += ddy; + frame.fill (i, w - 1 - p0x, w - p0x); + frame.fill (i, w - 1 - (wp - p1x), w - (wp - p1x)); + ++p0x; + ++p1x; + } + + if (d >= ddx) { + d -= ddx; + } + + } + + if (! no_state && ! iter->valid (true)) { + + unsigned int bp = w - 1 - ((w * 7) / 8 - 1); + unsigned int be = bp + h / 2; + unsigned int bw = h / 4 - 1; + unsigned int by = h / 2 - 1; + + for (unsigned int i = 0; i < bw + 2; ++i) { + fill.clear (by - i, bp - 1, be); + fill.clear (by + i, bp - 1, be); + } + + for (unsigned int i = 0; i < bw; ++i) { + text.fill (by - i, bp + bw - i - 1, bp + bw - i + 1); + text.fill (by - i - 1, bp + bw - i - 1, bp + bw - i + 1); + text.fill (by - i, bp + bw + i, bp + bw + i + 2); + text.fill (by - i - 1, bp + bw + i, bp + bw + i + 2); + text.fill (by + i, bp + bw - i - 1, bp + bw - i + 1); + text.fill (by + i + 1, bp + bw - i - 1, bp + bw - i + 1); + text.fill (by + i, bp + bw + i, bp + bw + i + 2); + text.fill (by + i + 1, bp + bw + i, bp + bw + i + 2); + } + + } + + vertex.fill (h / 2 - 1, w - 1 - wp / 2, w - wp / 2); + + lay::ViewOp::Mode mode = lay::ViewOp::Copy; + + // create fill + single_bitmap_to_image (lay::ViewOp (fill_color, mode, 0, iter->eff_dither_pattern (true), di_off), fill, &image, view->dither_pattern (), view->line_styles (), w, h); + // create frame + if (lw == 0) { + single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0 /*solid line*/, 2 /*dotted*/, 0), frame, &image, view->dither_pattern (), view->line_styles (), w, h); + } else { + single_bitmap_to_image (lay::ViewOp (frame_color, mode, iter->eff_line_style (true), 0, 0, lay::ViewOp::Rect, lw), frame, &image, view->dither_pattern (), view->line_styles (), w, h); + } + // create text + single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0, 0, 0), text, &image, view->dither_pattern (), view->line_styles (), w, h); + // create vertex + single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0, 0, 0, lay::ViewOp::Cross, iter->marked (true) ? 9/*mark size*/ : 0), vertex, &image, view->dither_pattern (), view->line_styles (), w, h); + + QPixmap pixmap = QPixmap::fromImage (image); // Qt 4.6.0 workaround + return QIcon (pixmap); +} + +QSize +LayerTreeModel::icon_size () const +{ + unsigned int is = ((QFontInfo (m_font).pixelSize () + 15) / 16) * 16; + return QSize (is * 2, is); +} + QVariant LayerTreeModel::data (const QModelIndex &index, int role) const { @@ -568,6 +724,16 @@ LayerTreeModel::data (const QModelIndex &index, int role) const return QVariant (); + } else if (role == Qt::SizeHintRole) { + + if (index.column () == 0) { + // NOTE: for some reason, the widget clips the icon when inside a tree and needs a some what bigger width .. + QSize is = icon_size (); + return QVariant (is + QSize (is.width () / 4, 0)); + } else { + return QVariant (); + } + } else if (role == Qt::DisplayRole || role == Qt::EditRole) { if (index.column () == 1) { @@ -593,142 +759,10 @@ LayerTreeModel::data (const QModelIndex &index, int role) const } } - unsigned int w = 32; - unsigned int h = 16; - + QSize is = icon_size (); + if (animate_visible) { - - lay::color_t def_color = 0x808080; - lay::color_t fill_color = iter->has_fill_color (true) ? iter->eff_fill_color (true) : def_color; - lay::color_t frame_color = iter->has_frame_color (true) ? iter->eff_frame_color (true) : def_color; - - QImage image (w, h, QImage::Format_RGB32); - image.fill (m_background_color.rgb ()); - - // TODO: adjust the resolution according to the oversampling mode - lay::Bitmap fill (w, h, 1.0); - lay::Bitmap frame (w, h, 1.0); - lay::Bitmap text (w, h, 1.0); - lay::Bitmap vertex (w, h, 1.0); - - unsigned int mask_w = 31; - unsigned int mask_all = 0xfffffffe; - unsigned int mask_left = 0x80000000; - unsigned int mask_right = 0x00000002; - unsigned int mask_center = 0x00010000; - - if (! iter->visible (true)) { - - mask_w = 8; - mask_all = 0xff800000; - mask_left = 0x80000000; - mask_right = 0x00800000; - mask_center = 0x08000000; - - // Show the arrow if it is invisible also locally. - if (! iter->visible (false)) { - text.scanline (4) [0] = 0x00008000 << 1; - text.scanline (5) [0] = 0x00018000 << 1; - text.scanline (6) [0] = 0x00038000 << 1; - text.scanline (7) [0] = 0x00078000 << 1; - text.scanline (8) [0] = 0x00038000 << 1; - text.scanline (9) [0] = 0x00018000 << 1; - text.scanline (10) [0] = 0x00008000 << 1; - } - - } - - if (mp_view->no_stipples ()) { - // Show a partial stipple pattern only for "no stipple" mode - for (unsigned int i = 1; i < h - 2; ++i) { - fill.scanline (i) [0] = 0xff800000; - } - } else { - for (unsigned int i = 1; i < h - 2; ++i) { - fill.scanline (i) [0] = mask_all; - } - } - - int lw = iter->width (true); - if (lw < 0) { - // default line width is 0 for parents and 1 for leafs - lw = iter->has_children () ? 0 : 1; - } - - int p0 = lw / 2; - int p1 = (lw - 1) / 2; - if (p0 < 0) { - p0 = 0; - } else if (p0 > 7) { - p0 = 7; - } - if (p1 < 0) { - p1 = 0; - } else if (p1 > 7) { - p1 = 7; - } - - int p0x = p0, p1x = p1; - unsigned int ddx = 0; - unsigned int ddy = h - 2 - p1 - p0; - if (iter->xfill (true)) { - ddx = mask_w - p0 - p1 - 1; - } - unsigned int d = ddx / 2; - - frame.scanline (p0) [0] = mask_all << p1; - for (unsigned int i = p0; i < h - 2; ++i) { - frame.scanline (i) [0] |= (mask_left >> p0) | (mask_right << p1); - frame.scanline (i) [0] |= (mask_left >> p0x) | (mask_right << p1x); - while (d < ddx) { - d += ddy; - frame.scanline (i) [0] |= (mask_left >> p0x) | (mask_right << p1x); - ++p0x; - ++p1x; - } - if (d >= ddx) { - d -= ddx; - } - } - frame.scanline (h - 2 - p1) [0] = mask_all << p1; - - if (! iter->valid (true)) { - - text.scanline (4) [0] |= 0x00000c60; - text.scanline (5) [0] |= 0x00000ee0; - text.scanline (6) [0] |= 0x000007c0; - text.scanline (7) [0] |= 0x00000380; - text.scanline (8) [0] |= 0x000007c0; - text.scanline (9) [0] |= 0x00000ee0; - text.scanline (10) [0] |= 0x00000c60; - - for (unsigned int i = 3; i < 12; ++i) { - fill.scanline (i) [0] &= ~0x00001ff0; - frame.scanline (i) [0] &= ~0x00001ff0; - } - - } - - vertex.scanline (h / 2 - 1) [0] = mask_center; - - lay::ViewOp::Mode mode = lay::ViewOp::Copy; - - // create fill - single_bitmap_to_image (lay::ViewOp (fill_color, mode, 0, iter->eff_dither_pattern (true), di_off), fill, &image, mp_view->dither_pattern (), mp_view->line_styles (), w, h); - // create frame - if (lw == 0) { - single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0 /*solid line*/, 2 /*dotted*/, 0), frame, &image, mp_view->dither_pattern (), mp_view->line_styles (), w, h); - } else { - single_bitmap_to_image (lay::ViewOp (frame_color, mode, iter->eff_line_style (true), 0, 0, lay::ViewOp::Rect, lw), frame, &image, mp_view->dither_pattern (), mp_view->line_styles (), w, h); - } - // create text - single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0, 0, 0), text, &image, mp_view->dither_pattern (), mp_view->line_styles (), w, h); - // create vertex - single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0, 0, 0, lay::ViewOp::Cross, iter->marked (true) ? 9/*mark size*/ : 0), vertex, &image, mp_view->dither_pattern (),mp_view->line_styles (), w, h); - - QPixmap pixmap = QPixmap::fromImage (image); // Qt 4.6.0 workaround - return QVariant (QIcon (pixmap)); - + return QVariant (icon_for_layer (iter, mp_view, is.width (), is.height (), di_off)); } else { return QVariant (QIcon ()); } diff --git a/src/laybasic/laybasic/layLayerTreeModel.h b/src/laybasic/laybasic/layLayerTreeModel.h index b52d7cdaf..852184ab1 100644 --- a/src/laybasic/laybasic/layLayerTreeModel.h +++ b/src/laybasic/laybasic/layLayerTreeModel.h @@ -104,6 +104,16 @@ public: virtual QModelIndex index (int row, int column, const QModelIndex &parent) const; virtual QModelIndex parent (const QModelIndex &index) const; + /** + * @brief Provides an icon for a given layer style + */ + static QIcon icon_for_layer (const lay::LayerPropertiesConstIterator &iter, lay::LayoutView *view, unsigned int w, unsigned int h, unsigned int di_offset, bool no_state = false); + + /** + * @brief Gets the preferred icon size + */ + QSize icon_size () const; + /** * @brief Convert a lay::LayerPropertiesConstIterator to a QModelIndex */ diff --git a/src/laybasic/laybasic/laySelector.cc b/src/laybasic/laybasic/laySelector.cc index ffe76e411..d1e5d1425 100644 --- a/src/laybasic/laybasic/laySelector.cc +++ b/src/laybasic/laybasic/laySelector.cc @@ -74,10 +74,7 @@ void SelectionService::deactivated () { mp_view->clear_transient_selection (); - if (mp_box) { - delete mp_box; - mp_box = 0; - } + reset_box (); } void @@ -102,7 +99,20 @@ SelectionService::timeout () mp_view->transient_select (m_hover_point); } -bool +void +SelectionService::reset_box () +{ + if (mp_box) { + + widget ()->ungrab_mouse (this); + + delete mp_box; + mp_box = 0; + + } +} + +bool SelectionService::wheel_event (int /*delta*/, bool /*horizonal*/, const db::DPoint & /*p*/, unsigned int /*buttons*/, bool /*prio*/) { return false; @@ -115,19 +125,31 @@ SelectionService::enter_event (bool /*prio*/) return false; } -bool -SelectionService::leave_event (bool /*prio*/) +bool +SelectionService::leave_event (bool prio) { m_mouse_in_window = false; + hover_reset (); + + if (prio) { + reset_box (); + } + return false; } bool -SelectionService::mouse_move_event (const db::DPoint &p, unsigned int /*buttons*/, bool prio) +SelectionService::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) { if (prio) { + m_current_position = p; + + if ((buttons & LeftButton) == 0) { + reset_box (); + } + if (mp_box) { m_p2 = p; mp_box->set_points (m_p1, m_p2); @@ -138,6 +160,7 @@ SelectionService::mouse_move_event (const db::DPoint &p, unsigned int /*buttons* } } + return false; } @@ -145,6 +168,11 @@ bool SelectionService::mouse_double_click_event (const db::DPoint & /*p*/, unsigned int buttons, bool prio) { hover_reset (); + + if (prio) { + reset_box (); + } + if (prio && (buttons & lay::LeftButton) != 0) { mp_view->show_properties (QApplication::activeWindow ()); return true; @@ -157,11 +185,18 @@ bool SelectionService::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio) { hover_reset (); - if (prio && ! mp_box && (buttons & lay::LeftButton) != 0) { - mp_view->stop_redraw (); // TODO: how to restart if selection is aborted? - m_buttons = buttons; - begin (p); - return true; + + if (prio) { + + reset_box (); + + if ((buttons & lay::LeftButton) != 0) { + mp_view->stop_redraw (); // TODO: how to restart if selection is aborted? + m_buttons = buttons; + begin (p); + return true; + } + } return false; @@ -170,7 +205,11 @@ SelectionService::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool SelectionService::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio) { - if (prio && mp_view && widget ()->mouse_event_viewport ().contains (p) && (buttons & lay::LeftButton) != 0) { + if (prio) { + reset_box (); + } + + if (prio && mp_view && widget ()->mouse_event_viewport ().contains (p) && (buttons & lay::LeftButton) != 0) { lay::Editable::SelectionMode mode = lay::Editable::Replace; bool shift = ((buttons & lay::ShiftButton) != 0); @@ -211,12 +250,10 @@ bool SelectionService::mouse_release_event (const db::DPoint & /*p*/, unsigned int /*buttons*/, bool prio) { hover_reset (); + if (prio && mp_box) { - widget ()->ungrab_mouse (this); - - delete mp_box; - mp_box = 0; + reset_box (); if (mp_view) { diff --git a/src/laybasic/laybasic/laySelector.h b/src/laybasic/laybasic/laySelector.h index ddf60c6f8..e302d70f4 100644 --- a/src/laybasic/laybasic/laySelector.h +++ b/src/laybasic/laybasic/laySelector.h @@ -25,6 +25,8 @@ #ifndef HDR_laySelector #define HDR_laySelector +#include "laybasicCommon.h" + #include "layViewObject.h" #include "layEditable.h" @@ -37,7 +39,7 @@ class RubberBox; class LayoutView; class LayoutCanvas; -class SelectionService +class LAYBASIC_PUBLIC SelectionService : public QObject, public lay::ViewService { @@ -77,6 +79,7 @@ private: virtual void deactivated (); db::DPoint m_p1, m_p2; + db::DPoint m_current_position; lay::LayoutView *mp_view; lay::RubberBox *mp_box; unsigned int m_color; @@ -86,6 +89,8 @@ private: bool m_hover_wait; db::DPoint m_hover_point; bool m_mouse_in_window; + + void reset_box (); }; } diff --git a/src/laybasic/laybasic/layViewObject.cc b/src/laybasic/laybasic/layViewObject.cc index cc68a61f8..ef2a7dbfb 100644 --- a/src/laybasic/laybasic/layViewObject.cc +++ b/src/laybasic/laybasic/layViewObject.cc @@ -272,6 +272,7 @@ ViewObjectWidget::ViewObjectWidget (QWidget *parent, const char *name) m_mouse_pressed_state (false), m_mouse_buttons (0), m_in_mouse_move (false), + m_mouse_inside (false), m_cursor (lay::Cursor::none), m_default_cursor (lay::Cursor::none) { @@ -363,6 +364,14 @@ ViewObjectWidget::set_default_cursor (lay::Cursor::cursor_shape cursor) } } +void +ViewObjectWidget::ensure_entered () +{ + if (! m_mouse_inside) { + enterEvent (0); + } +} + void ViewObjectWidget::begin_mouse_event (lay::Cursor::cursor_shape cursor) { @@ -434,7 +443,7 @@ BEGIN_PROTECTED const DragDropDataBase *dd = get_drag_drop_data (event->mimeData ()); if (dd) { - db::DPoint p = m_trans.inverted () * db::DPoint (event->pos ().x (), height () - 1 - event->pos ().y ()); + db::DPoint p = pixel_to_um (event->pos ()); bool done = drag_enter_event (p, dd); service_iterator svc = begin_services (); @@ -479,7 +488,7 @@ BEGIN_PROTECTED const DragDropDataBase *dd = get_drag_drop_data (event->mimeData ()); if (dd) { - db::DPoint p = m_trans.inverted () * db::DPoint (event->pos ().x (), height () - 1 - event->pos ().y ()); + db::DPoint p = pixel_to_um (event->pos ()); bool done = drag_move_event (p, dd); service_iterator svc = begin_services (); @@ -503,7 +512,7 @@ BEGIN_PROTECTED const DragDropDataBase *dd = get_drag_drop_data (event->mimeData ()); if (dd) { - db::DPoint p = m_trans.inverted () * db::DPoint (event->pos ().x (), height () - 1 - event->pos ().y ()); + db::DPoint p = pixel_to_um (event->pos ()); bool done = drop_event (p, dd); service_iterator svc = begin_services (); @@ -523,6 +532,7 @@ void ViewObjectWidget::mouseMoveEvent (QMouseEvent *e) { BEGIN_PROTECTED + ensure_entered (); m_mouse_pos = e->pos (); m_mouse_buttons = qt_to_buttons (e->buttons (), e->modifiers ()); do_mouse_move (); @@ -543,7 +553,7 @@ ViewObjectWidget::do_mouse_move () bool done = false; - db::DPoint p = m_trans.inverted () * db::DPoint (m_mouse_pressed.x (), height () - 1 - m_mouse_pressed.y ()); + db::DPoint p = pixel_to_um (m_mouse_pressed); for (std::list::iterator g = m_grabbed.begin (); !done && g != m_grabbed.end (); ) { std::list::iterator gg = g; @@ -578,7 +588,7 @@ ViewObjectWidget::do_mouse_move () bool done = false; - db::DPoint p = m_trans.inverted () * db::DPoint (m_mouse_pos.x (), height () - 1 - m_mouse_pos.y ()); + db::DPoint p = pixel_to_um (m_mouse_pos); for (std::list::iterator g = m_grabbed.begin (); !done && g != m_grabbed.end (); ) { std::list::iterator gg = g; @@ -614,6 +624,7 @@ void ViewObjectWidget::mouseDoubleClickEvent (QMouseEvent *e) { BEGIN_PROTECTED + ensure_entered (); begin_mouse_event (lay::Cursor::none); setFocus (); @@ -626,7 +637,7 @@ BEGIN_PROTECTED unsigned int buttons = qt_to_buttons (e->buttons (), e->modifiers ()); - db::DPoint p = m_trans.inverted () * db::DPoint (e->pos ().x (), height () - 1 - e->pos ().y ()); + db::DPoint p = pixel_to_um (e->pos ()); for (std::list::iterator g = m_grabbed.begin (); !done && g != m_grabbed.end (); ) { std::list::iterator gg = g; @@ -659,6 +670,8 @@ void ViewObjectWidget::enterEvent (QEvent * /*event*/) { BEGIN_PROTECTED + m_mouse_inside = true; + begin_mouse_event (); bool done = false; @@ -723,12 +736,15 @@ BEGIN_PROTECTED end_mouse_event (); END_PROTECTED + + m_mouse_inside = false; } void ViewObjectWidget::wheelEvent (QWheelEvent *e) { BEGIN_PROTECTED + ensure_entered (); begin_mouse_event (); e->ignore (); @@ -738,7 +754,7 @@ BEGIN_PROTECTED unsigned int buttons = qt_to_buttons (e->buttons (), e->modifiers ()); bool horizonal = (e->orientation () == Qt::Horizontal); - db::DPoint p = m_trans.inverted () * db::DPoint (e->pos ().x (), height () - 1 - e->pos ().y ()); + db::DPoint p = pixel_to_um (e->pos ()); for (std::list::iterator g = m_grabbed.begin (); !done && g != m_grabbed.end (); ) { std::list::iterator gg = g; @@ -770,6 +786,7 @@ END_PROTECTED void ViewObjectWidget::mousePressEvent (QMouseEvent *e) { + ensure_entered (); setFocus (); m_mouse_pos = e->pos (); @@ -784,12 +801,13 @@ void ViewObjectWidget::mouseReleaseEvent (QMouseEvent *e) { BEGIN_PROTECTED + ensure_entered (); begin_mouse_event (); bool done = false; m_mouse_pos = e->pos (); - db::DPoint p = m_trans.inverted () * db::DPoint (e->pos ().x (), height () - 1 - e->pos ().y ()); + db::DPoint p = pixel_to_um (e->pos ()); for (std::list::iterator g = m_grabbed.begin (); !done && g != m_grabbed.end (); ) { std::list::iterator gg = g; @@ -838,6 +856,12 @@ END_PROTECTED m_mouse_pressed_state = false; } +db::DPoint +ViewObjectWidget::pixel_to_um (const QPoint &pt) const +{ + return m_trans.inverted () * db::DPoint (pt.x (), height () - 1 - pt.y ()); +} + void ViewObjectWidget::mouse_event_trans (const db::DCplxTrans &trans) { diff --git a/src/laybasic/laybasic/layViewObject.h b/src/laybasic/laybasic/layViewObject.h index 816241d2b..4522857cf 100644 --- a/src/laybasic/laybasic/layViewObject.h +++ b/src/laybasic/laybasic/layViewObject.h @@ -962,6 +962,35 @@ public: return m_view_objects_dismissed; } + /** + * @brief Gets the current mouse position + */ + QPoint mouse_position () const + { + return m_mouse_pos; + } + + /** + * @brief Gets the current mouse position in micrometer units + */ + db::DPoint mouse_position_um () const + { + return pixel_to_um (m_mouse_pos); + } + + /** + * @brief Translates a screen coordinate in micrometer coordinates + */ + db::DPoint pixel_to_um (const QPoint &pt) const; + + /** + * @brief Gets a flag indicating whether the mouse is inside the window + */ + bool mouse_in_window () const + { + return m_mouse_inside; + } + protected: /** * @brief Qt focus event handler @@ -1052,8 +1081,10 @@ private: bool m_mouse_pressed_state; unsigned int m_mouse_buttons; bool m_in_mouse_move; + bool m_mouse_inside; lay::Cursor::cursor_shape m_cursor, m_default_cursor; + void ensure_entered (); void do_mouse_move (); void begin_mouse_event (lay::Cursor::cursor_shape cursor = lay::Cursor::keep); void end_mouse_event (); From 5bd73bb8bfda7e834fc974b50ee19e94dd618e24 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 3 Aug 2020 20:20:25 +0200 Subject: [PATCH 030/113] 'tap' now changes the current layer while editing and also initiates polygons, paths and boxes. --- src/edt/edt/edtMainService.cc | 7 +++++++ src/edt/edt/edtService.cc | 19 +++++++++++++++---- src/edt/edt/edtService.h | 19 +++++++++++++++++++ src/edt/edt/edtServiceImpl.cc | 15 +++++++++++++-- src/edt/edt/edtServiceImpl.h | 8 +++++--- 5 files changed, 59 insertions(+), 9 deletions(-) diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index 1fd2d3340..95274dad8 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -1997,9 +1997,16 @@ MainService::cm_tap () QAction *action = menu->exec (mp); if (action) { + int index = action->data ().toInt (); lay::LayerPropertiesConstIterator iter = tapped_layers [index]; view ()->set_current_layer (iter); + + edt::Service *es = dynamic_cast (view ()->view_object_widget ()->active_service ()); + if (es) { + es->tap (pt); + } + } } diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index 0e5986b5c..f8dd7ee84 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -710,8 +710,7 @@ Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) // in this mode, ignore exceptions here since it is rather annoying to have messages popping // up then. try { - do_begin_edit (p); - m_editing = true; + begin_edit (p); } catch (...) { set_edit_marker (0); } @@ -744,8 +743,7 @@ Service::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio view ()->cancel (); // cancel any pending edit operations and clear the selection set_edit_marker (0); - do_begin_edit (p); - m_editing = true; + begin_edit (p); } else { if (do_mouse_click (p)) { @@ -1432,6 +1430,19 @@ Service::move_markers (const db::DTrans &t) } } +void +Service::begin_edit (const db::DPoint &p) +{ + do_begin_edit (p); + m_editing = true; +} + +void +Service::tap (const db::DPoint & /*initial*/) +{ + // .. nothing here .. +} + void Service::selection_to_view () { diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h index cb4812f79..6e93219cb 100644 --- a/src/edt/edt/edtService.h +++ b/src/edt/edt/edtService.h @@ -44,6 +44,10 @@ #include #include +namespace lay { + class LayerPropertiesConstIterator; +} + namespace edt { class Service; @@ -344,6 +348,11 @@ public: */ virtual void edit_cancel (); + /** + * @brief Triggered by tap - gives the new layer and if required the initial point + */ + virtual void tap (const db::DPoint &initial); + /** * @brief Delete the selected rulers * @@ -383,6 +392,11 @@ protected: */ void selection_to_view (); + /** + * @brief starts editing at the given point. + */ + void begin_edit (const db::DPoint &p); + /** * @brief Reimplemented by the specific implementation of the shape editors * @@ -528,6 +542,11 @@ protected: return m_max_shapes_of_instances; } + bool editing () const + { + return m_editing; + } + private: // The layout view that the editor service is attached to lay::LayoutView *mp_view; diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index 691bff357..8e73c2775 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -69,17 +69,18 @@ void ShapeEditService::get_edit_layer () { lay::LayerPropertiesConstIterator cl = view ()->current_layer (); + if (cl.is_null ()) { throw tl::Exception (tl::to_string (QObject::tr ("Please select a layer first"))); } if (! cl->visible (true)) { lay::TipDialog td (QApplication::activeWindow (), - tl::to_string (QObject::tr ("You are about to draw on a hidden layer. The result won't be visible.")), + tl::to_string (QObject::tr ("You are about to draw on a hidden layer. The result won't be visible.")), "drawing-on-invisible-layer"); td.exec_dialog (); } - + int cv_index = cl->cellview_index (); const lay::CellView &cv = view ()->cellview (cv_index); int layer = cl->layer_index (); @@ -128,6 +129,16 @@ ShapeEditService::get_edit_layer () } } +void +ShapeEditService::tap (const db::DPoint &initial) +{ + if (editing ()) { + get_edit_layer (); + } else { + begin_edit (initial); + } +} + /** * @brief Deliver a good interpolation between two points m and p * diff --git a/src/edt/edt/edtServiceImpl.h b/src/edt/edt/edtServiceImpl.h index 60955035b..3fdad1ef2 100644 --- a/src/edt/edt/edtServiceImpl.h +++ b/src/edt/edt/edtServiceImpl.h @@ -32,6 +32,7 @@ namespace lay { class CellView; + class LayerPropertiesConstIterator; } namespace edt @@ -57,10 +58,11 @@ protected: db::Cell &cell () const { return *mp_cell; } db::Layout &layout () const { return *mp_layout; } - void do_mouse_move_inactive (const db::DPoint &p); + virtual void do_mouse_move_inactive (const db::DPoint &p); + virtual void tap (const db::DPoint &initial); + + virtual bool configure (const std::string &name, const std::string &value); - bool configure (const std::string &name, const std::string &value); - protected: std::pair interpolate (const db::DPoint &m, const db::DPoint &o, const db::DPoint &p) const; void deliver_shape (const db::Polygon &poly); From 4389196000f2d407b404f6e56e4009bcbd717f24 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 3 Aug 2020 21:50:55 +0200 Subject: [PATCH 031/113] Change layer now also acts on the transient selection if nothing else is selected. --- src/edt/edt/edtMainService.cc | 191 +++++++++++++++++++++++++++------- 1 file changed, 153 insertions(+), 38 deletions(-) diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index 95274dad8..1c6d582ea 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -2010,6 +2010,129 @@ MainService::cm_tap () } } + +/** + * @brief An iterator for the selected objects of all edt services in a layout view + */ +class EDT_PUBLIC SelectionIterator +{ +public: + typedef lay::ObjectInstPath value_type; + typedef const lay::ObjectInstPath &reference; + typedef const lay::ObjectInstPath *pointer; + + /** + * @brief Creates a new iterator iterating over all selected edt objects from the given view + * + * If "including_transient" is true, the transient selection will be used as fallback. + */ + SelectionIterator (lay::LayoutView *view, bool including_transient = true) + : m_transient_mode (false) + { + mp_edt_services = view->get_plugins (); + + m_current_service = mp_edt_services.begin (); + if (m_current_service != mp_edt_services.end ()) { + m_current_object = (*m_current_service)->selection ().begin (); + } + + next (); + + if (at_end () && including_transient) { + + m_transient_mode = true; + + m_current_service = mp_edt_services.begin (); + if (m_current_service != mp_edt_services.end ()) { + m_current_object = (*m_current_service)->transient_selection ().begin (); + } + + next (); + + } + } + + /** + * @brief Returns a value indicating whether the transient selection is taken + */ + bool is_transient () const + { + return m_transient_mode; + } + + /** + * @brief Increments the iterator + */ + void operator++ () + { + inc (); + next (); + } + + /** + * @brief Dereferencing + */ + const lay::ObjectInstPath &operator* () const + { + tl_assert (! at_end ()); + return *m_current_object; + } + + /** + * @brief Arrow operator + */ + const lay::ObjectInstPath *operator-> () const + { + return & operator* (); + } + + /** + * @brief Returns a value indicating whether the iterator has finished + */ + bool at_end () const + { + return m_current_service == mp_edt_services.end (); + } + +private: + void inc () + { + tl_assert (! at_end ()); + ++m_current_object; + } + + void next () + { + if (at_end ()) { + return; + } + + const edt::Service::objects *sel = m_transient_mode ? &(*m_current_service)->transient_selection () : &(*m_current_service)->selection (); + + while (m_current_object == sel->end ()) { + + ++m_current_service; + + if (m_current_service != mp_edt_services.end ()) { + + sel = m_transient_mode ? &(*m_current_service)->transient_selection () : &(*m_current_service)->selection (); + m_current_object = sel->begin (); + + } else { + break; + } + + } + } + +private: + std::vector mp_edt_services; + std::vector::const_iterator m_current_service; + edt::Service::obj_iterator m_current_object; + bool m_transient_mode; +}; + + void MainService::cm_change_layer () { @@ -2018,16 +2141,12 @@ MainService::cm_change_layer () int cv_index = -1; - std::vector edt_services = view ()->get_plugins (); - // get (common) cellview index of the selected shapes - for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { - for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) { - if (cv_index >= 0 && cv_index != int (s->cv_index ())) { - throw tl::Exception (tl::to_string (QObject::tr ("Selections originate from different layouts - cannot switch layer in this case."))); - } - cv_index = int (s->cv_index ()); + for (SelectionIterator s (view ()); ! s.at_end (); ++s) { + if (cv_index >= 0 && cv_index != int (s->cv_index ())) { + throw tl::Exception (tl::to_string (QObject::tr ("Selections originate from different layouts - cannot switch layer in this case."))); } + cv_index = int (s->cv_index ()); } if (cv_index >= 0) { @@ -2089,46 +2208,42 @@ MainService::cm_change_layer () // Insert and delete the shape. This exploits the fact, that a shape can be erased multiple times - // this is important since the selection potentially contains the same shape multiple times. - for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { + for (SelectionIterator s (view ()); ! s.at_end (); ++s) { - for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) { + if (!s->is_cell_inst () && int (s->layer ()) != layer) { - if (!s->is_cell_inst () && int (s->layer ()) != layer) { + db::Cell &cell = layout.cell (s->cell_index ()); + if (cell.shapes (s->layer ()).is_valid (s->shape ())) { + cell.shapes (layer).insert (s->shape ()); + cell.shapes (s->layer ()).erase_shape (s->shape ()); + } - db::Cell &cell = layout.cell (s->cell_index ()); - if (cell.shapes (s->layer ()).is_valid (s->shape ())) { - cell.shapes (layer).insert (s->shape ()); - cell.shapes (s->layer ()).erase_shape (s->shape ()); - } + } else if (s->is_cell_inst ()) { - } else if (s->is_cell_inst ()) { + // If the selected object is a PCell instance, and there is exactly one visible, writable layer type parameter, change this one - // If the selected object is a PCell instance, and there is exactly one visible, writable layer type parameter, change this one + db::Instance inst = s->back ().inst_ptr; + db::Cell &cell = layout.cell (s->cell_index ()); - db::Instance inst = s->back ().inst_ptr; - db::Cell &cell = layout.cell (s->cell_index ()); + if (cell.is_valid (inst)) { - if (cell.is_valid (inst)) { + const db::PCellDeclaration *pcell_decl = layout.pcell_declaration_for_pcell_variant (inst.cell_index ()); + if (pcell_decl) { - const db::PCellDeclaration *pcell_decl = layout.pcell_declaration_for_pcell_variant (inst.cell_index ()); - if (pcell_decl) { - - size_t layer_par_index = 0; - int n_layer_par = 0; - for (std::vector::const_iterator d = pcell_decl->parameter_declarations ().begin (); d != pcell_decl->parameter_declarations ().end () && n_layer_par < 2; ++d) { - if (d->get_type () == db::PCellParameterDeclaration::t_layer && !d->is_hidden () && !d->is_readonly ()) { - ++n_layer_par; - layer_par_index = size_t (d - pcell_decl->parameter_declarations ().begin ()); - } - } - - if (n_layer_par == 1) { - std::vector parameters = cell.get_pcell_parameters (inst); - tl_assert (layer_par_index < parameters.size ()); - parameters [layer_par_index] = layout.get_properties (layer); - cell.change_pcell_parameters (inst, parameters); + size_t layer_par_index = 0; + int n_layer_par = 0; + for (std::vector::const_iterator d = pcell_decl->parameter_declarations ().begin (); d != pcell_decl->parameter_declarations ().end () && n_layer_par < 2; ++d) { + if (d->get_type () == db::PCellParameterDeclaration::t_layer && !d->is_hidden () && !d->is_readonly ()) { + ++n_layer_par; + layer_par_index = size_t (d - pcell_decl->parameter_declarations ().begin ()); } + } + if (n_layer_par == 1) { + std::vector parameters = cell.get_pcell_parameters (inst); + tl_assert (layer_par_index < parameters.size ()); + parameters [layer_par_index] = layout.get_properties (layer); + cell.change_pcell_parameters (inst, parameters); } } From 8e4e5cde128f062dd50b81d02dbb8f8e95e9db62 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 3 Aug 2020 23:48:49 +0200 Subject: [PATCH 032/113] WIP: distribute selected objects feature --- src/edt/edt/AlignOptionsDialog.ui | 299 ++++++------ src/edt/edt/DistributeOptionsDialog.ui | 638 +++++++++++++++++++++++++ src/edt/edt/edt.pro | 3 +- src/edt/edt/edtDialogs.cc | 87 ++++ src/edt/edt/edtDialogs.h | 17 + src/edt/edt/edtMainService.cc | 234 +++++---- src/edt/edt/edtMainService.h | 15 + src/edt/edt/edtPlugin.cc | 1 + src/edt/edt/edtUtils.cc | 70 +++ src/edt/edt/edtUtils.h | 72 +++ 10 files changed, 1162 insertions(+), 274 deletions(-) create mode 100644 src/edt/edt/DistributeOptionsDialog.ui diff --git a/src/edt/edt/AlignOptionsDialog.ui b/src/edt/edt/AlignOptionsDialog.ui index 13d218095..c2002f0db 100644 --- a/src/edt/edt/AlignOptionsDialog.ui +++ b/src/edt/edt/AlignOptionsDialog.ui @@ -1,7 +1,8 @@ - + + AlignOptionsDialog - - + + 0 0 @@ -9,26 +10,26 @@ 392 - + Alignment Options - + - - + + Horizontal alignment - - - - + + + + - - + + :/align_none.png:/align_none.png - + 32 32 @@ -36,16 +37,16 @@ - - - + + + - - + + :/align_left.png:/align_left.png - + 32 32 @@ -53,16 +54,16 @@ - - - + + + - - + + :/align_hcenter.png:/align_hcenter.png - + 32 32 @@ -70,16 +71,16 @@ - - - + + + - - + + :/align_right.png:/align_right.png - + 32 32 @@ -87,12 +88,12 @@ - - - + + + Qt::Horizontal - + 243 20 @@ -100,55 +101,55 @@ - - - + + + none - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + left - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + center - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + right - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + Qt::Horizontal - + QSizePolicy::Fixed - + 20 20 @@ -156,15 +157,15 @@ - - - + + + Qt::Horizontal - + QSizePolicy::Fixed - + 20 20 @@ -172,15 +173,15 @@ - - - + + + Qt::Horizontal - + QSizePolicy::Fixed - + 20 20 @@ -192,21 +193,21 @@ - - + + Vertical alignment - - - - + + + + - - + + :/align_none.png:/align_none.png - + 32 32 @@ -214,16 +215,16 @@ - - - + + + - - + + :/align_top.png:/align_top.png - + 32 32 @@ -231,16 +232,16 @@ - - - + + + - - + + :/align_vcenter.png:/align_vcenter.png - + 32 32 @@ -248,12 +249,12 @@ - - - + + + Qt::Horizontal - + 243 34 @@ -261,56 +262,56 @@ - - - + + + none - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + top - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + center - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + bottom - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + - - + + :/align_bottom.png:/align_bottom.png - + 32 32 @@ -318,15 +319,15 @@ - - - + + + Qt::Horizontal - + QSizePolicy::Fixed - + 20 20 @@ -334,15 +335,15 @@ - - - + + + Qt::Horizontal - + QSizePolicy::Fixed - + 20 20 @@ -350,15 +351,15 @@ - - - + + + Qt::Horizontal - + QSizePolicy::Fixed - + 20 20 @@ -370,21 +371,21 @@ - - + + Layers for alignment of instances - - - - + + + + Use visible layers only - - - + + + Use all layers @@ -393,11 +394,11 @@ - - + + Qt::Vertical - + 488 16 @@ -406,11 +407,11 @@ - - + + Qt::Horizontal - + QDialogButtonBox::Cancel|QDialogButtonBox::Ok @@ -431,7 +432,7 @@ buttonBox - + @@ -440,11 +441,11 @@ AlignOptionsDialog accept() - + 248 254 - + 157 274 @@ -456,11 +457,11 @@ AlignOptionsDialog reject() - + 316 260 - + 286 274 diff --git a/src/edt/edt/DistributeOptionsDialog.ui b/src/edt/edt/DistributeOptionsDialog.ui new file mode 100644 index 000000000..0d2bf52e7 --- /dev/null +++ b/src/edt/edt/DistributeOptionsDialog.ui @@ -0,0 +1,638 @@ + + + DistributeOptionsDialog + + + + 0 + 0 + 716 + 574 + + + + Distribution Options + + + + + + Horizontal distribution + + + true + + + + + + Alignment + + + + + + + right + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + center + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 243 + 20 + + + + + + + + + + + + :/align_left.png:/align_left.png + + + + 32 + 32 + + + + + + + + + + + + :/align_right.png:/align_right.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + + + + + + + + + :/align_hcenter.png:/align_hcenter.png + + + + 32 + 32 + + + + + + + + left + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Pitch + + + + + + + + 0 + 0 + + + + + + + + µm + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Space + + + + + + + + 0 + 0 + + + + + + + + µm + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + The pitch specifies the offset at which the objects are placed relative to each other. The space is the minimum distance between the objects. + + + true + + + + + + + + + + + 0 + 200 + + + + Vertical distribution + + + true + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + top + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + :/align_vcenter.png:/align_vcenter.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + + 243 + 34 + + + + + + + + Qt::Horizontal + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Pitch + + + + + + + + 0 + 0 + + + + + + + + µm + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Space + + + + + + + + 0 + 0 + + + + + + + + µm + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + center + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + bottom + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + :/align_bottom.png:/align_bottom.png + + + + 32 + 32 + + + + + + + + The pitch specifies the offset at which the objects are placed relative to each other. The space is the minimum distance between the objects. + + + true + + + + + + + + + + + :/align_top.png:/align_top.png + + + + 32 + 32 + + + + + + + + + + + Layers for distribution of instances + + + + + + Use visible layers only + + + + + + + Use all layers + + + + + + + + + + Qt::Vertical + + + + 488 + 16 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + all_layers_rb + visible_layers_rb + h_left_rb + h_center_rb + h_right_rb + v_top_rb + v_center_rb + v_bottom_rb + buttonBox + + + + + + + buttonBox + accepted() + DistributeOptionsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + DistributeOptionsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/edt/edt/edt.pro b/src/edt/edt/edt.pro index f72811940..b57f0144e 100644 --- a/src/edt/edt/edt.pro +++ b/src/edt/edt/edt.pro @@ -42,7 +42,8 @@ FORMS = \ PolygonPropertiesPage.ui \ RoundCornerOptionsDialog.ui \ TextPropertiesPage.ui \ - PCellParametersDialog.ui + PCellParametersDialog.ui \ + DistributeOptionsDialog.ui SOURCES = \ edtConfig.cc \ diff --git a/src/edt/edt/edtDialogs.cc b/src/edt/edt/edtDialogs.cc index 0fbc02113..63ac14fdc 100644 --- a/src/edt/edt/edtDialogs.cc +++ b/src/edt/edt/edtDialogs.cc @@ -326,6 +326,93 @@ AlignOptionsDialog::exec_dialog (lay::LayoutView * /*view*/, int &hmode, int &vm } } +// -------------------------------------------------------------------------------- +// DistributeOptionsDialog implementation + +DistributeOptionsDialog::DistributeOptionsDialog (QWidget *parent) + : QDialog (parent) +{ + setObjectName (QString::fromUtf8 ("change_layer_options_dialog")); + + Ui::DistributeOptionsDialog::setupUi (this); +} + +DistributeOptionsDialog::~DistributeOptionsDialog () +{ + // .. nothing yet .. +} + +bool +DistributeOptionsDialog::exec_dialog (lay::LayoutView *view, bool &hdistribute, int &hmode, double &hpitch, double &hspace, bool &vdistribute, int &vmode, double &vpitch, double &vspace, bool &visible_layers) +{ + QRadioButton *hmode_buttons [] = { (QRadioButton *) 0, this->h_left_rb, this->h_center_rb, this->h_right_rb }; + QRadioButton *vmode_buttons [] = { (QRadioButton *) 0, this->v_top_rb, this->v_center_rb, this->v_bottom_rb }; + QRadioButton *layers_buttons [] = { this->all_layers_rb, this->visible_layers_rb }; + + this->h_distribute->setChecked (hdistribute); + for (int i = 1; i < 4; ++i) { + hmode_buttons [i]->setChecked (hmode == i); + } + + this->h_space->setText (tl::to_qstring (tl::micron_to_string (hspace))); + this->h_pitch->setText (tl::to_qstring (tl::micron_to_string (hpitch))); + + this->v_distribute->setChecked (vdistribute); + for (int i = 1; i < 4; ++i) { + vmode_buttons [i]->setChecked (vmode == i); + } + + this->v_space->setText (tl::to_qstring (tl::micron_to_string (vspace))); + this->v_pitch->setText (tl::to_qstring (tl::micron_to_string (vpitch))); + + for (int i = 0; i < 2; ++i) { + layers_buttons [i]->setChecked (int (visible_layers) == i); + } + + if (QDialog::exec ()) { + + hdistribute = this->h_distribute->isChecked (); + hmode = -1; + for (int i = 1; i < 4; ++i) { + if (hmode_buttons [i]->isChecked ()) { + hmode = i; + } + } + + hspace = 0.0; + tl::from_string (tl::to_string (this->h_space->text ()), hspace); + + hpitch = 0.0; + tl::from_string (tl::to_string (this->h_pitch->text ()), hpitch); + + vdistribute = this->v_distribute->isChecked (); + vmode = -1; + for (int i = 1; i < 4; ++i) { + if (vmode_buttons [i]->isChecked ()) { + vmode = i; + } + } + + vspace = 0.0; + tl::from_string (tl::to_string (this->v_space->text ()), vspace); + + vpitch = 0.0; + tl::from_string (tl::to_string (this->v_pitch->text ()), vpitch); + + visible_layers = false; + for (int i = 0; i < 2; ++i) { + if (layers_buttons [i]->isChecked ()) { + visible_layers = (i != 0); + } + } + + return true; + + } else { + return false; + } +} + // -------------------------------------------------------------------------------- // MakeCellOptionsDialog implementation diff --git a/src/edt/edt/edtDialogs.h b/src/edt/edt/edtDialogs.h index 4beddf680..15fc2fdf3 100644 --- a/src/edt/edt/edtDialogs.h +++ b/src/edt/edt/edtDialogs.h @@ -37,6 +37,7 @@ #include "ui_InstantiationForm.h" #include "ui_ChangeLayerOptionsDialog.h" #include "ui_AlignOptionsDialog.h" +#include "ui_DistributeOptionsDialog.h" #include "ui_CopyModeDialog.h" #include "ui_MakeCellOptionsDialog.h" #include "ui_MakeArrayOptionsDialog.h" @@ -127,6 +128,22 @@ public: bool exec_dialog (lay::LayoutView *view, int &hmode, int &vmode, bool &visible_layers); }; +/** + * @brief Distribute function options dialog + */ +class DistributeOptionsDialog + : public QDialog, + public Ui::DistributeOptionsDialog +{ +Q_OBJECT + +public: + DistributeOptionsDialog (QWidget *parent); + virtual ~DistributeOptionsDialog (); + + bool exec_dialog (lay::LayoutView *view, bool &hdistribute, int &hmode, double &hpitch, double &hspace, bool &vdistribute, int &vmode, double &vpitch, double &vspace, bool &visible_layers); +}; + /** * @brief Options dialog for the "make cell" function */ diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index 1c6d582ea..89df404ac 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -64,12 +64,18 @@ MainService::MainService (db::Manager *manager, lay::LayoutView *view, lay::Disp m_flatten_insts_levels (std::numeric_limits::max ()), m_flatten_prune (false), m_align_hmode (0), m_align_vmode (0), m_align_visible_layers (false), + m_hdistribute (true), + m_distribute_hmode (1), m_distribute_hpitch (0.0), m_distribute_hspace (0.0), + m_vdistribute (true), + m_distribute_vmode (1), m_distribute_vpitch (0.0), m_distribute_vspace (0.0), + m_distribute_visible_layers (false), m_origin_mode_x (-1), m_origin_mode_y (-1), m_origin_visible_layers_for_bbox (false), m_array_a (0.0, 1.0), m_array_b (1.0, 0.0), m_array_na (1), m_array_nb (1), m_router (0.0), m_rinner (0.0), m_npoints (64), m_undo_before_apply (true), mp_round_corners_dialog (0), mp_align_options_dialog (0), + mp_distribute_options_dialog (0), mp_flatten_inst_options_dialog (0), mp_make_cell_options_dialog (0), mp_make_array_options_dialog (0) @@ -100,6 +106,15 @@ MainService::align_options_dialog () return mp_align_options_dialog; } +edt::DistributeOptionsDialog * +MainService::distribute_options_dialog () +{ + if (! mp_distribute_options_dialog) { + mp_distribute_options_dialog = new edt::DistributeOptionsDialog (view ()); + } + return mp_distribute_options_dialog; +} + lay::FlattenInstOptionsDialog * MainService::flatten_inst_options_dialog () { @@ -138,6 +153,8 @@ MainService::menu_activated (const std::string &symbol) cm_edit_options (); } else if (symbol == "edt::sel_align") { cm_align (); + } else if (symbol == "edt::sel_distribute") { + cm_distribute (); } else if (symbol == "edt::sel_tap") { cm_tap (); } else if (symbol == "edt::sel_round_corners") { @@ -1841,7 +1858,99 @@ MainService::cm_align () } } -void +void +MainService::cm_distribute () +{ + tl_assert (view ()->is_editable ()); + check_no_guiding_shapes (); + + std::vector edt_services = view ()->get_plugins (); + + if (! distribute_options_dialog ()->exec_dialog (view (), m_hdistribute, m_distribute_hmode, m_distribute_hpitch, m_distribute_hspace, + m_vdistribute, m_distribute_vmode, m_distribute_vpitch, m_distribute_vspace, + m_align_visible_layers)) { + return; + } + + db::DBox prim_box; + bool has_secondary = false; + + // get (common) bbox index of the primary selection + for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { + for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) { + + if (s->seq () == 0) { + + const db::Layout &layout = view ()->cellview (s->cv_index ())->layout (); + db::CplxTrans tr = db::CplxTrans (layout.dbu ()) * s->trans (); + + if (! s->is_cell_inst ()) { + prim_box += tr * s->shape ().bbox (); + } else { + prim_box += inst_bbox (tr, view (), s->cv_index (), s->back (), m_distribute_visible_layers); + } + + } else { + has_secondary = true; + } + + } + } + + if (! prim_box.empty ()) { + + view ()->cancel_edits (); + manager ()->transaction (tl::to_string (QObject::tr ("Alignment"))); + + + + // do the alignment + for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { + + // create a transformation vector that describes each shape's transformation + std::vector tv; + tv.reserve ((*es)->selection ().size ()); + + for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) { + + db::DVector v; + +// @@@ + if (s->seq () > 0 || !has_secondary) { + + db::Layout &layout = view ()->cellview (s->cv_index ())->layout (); + db::CplxTrans tr = db::CplxTrans (layout.dbu ()) * s->trans (); + + if (! s->is_cell_inst ()) { + + db::DBox box = tr * s->shape ().bbox (); + v = compute_alignment_vector (prim_box, box, m_distribute_hmode, m_distribute_vmode); + + } else { + + db::DBox box = inst_bbox (tr, view (), s->cv_index (), s->back (), m_distribute_visible_layers); + v = compute_alignment_vector (prim_box, box, m_distribute_hmode, m_distribute_vmode); + + } + + } +// @@@ + + tv.push_back (db::DCplxTrans (db::DTrans (v))); + + } + + // use the "transform" method to transform the shapes and instances (with individual transformations) + (*es)->transform (db::DCplxTrans () /*dummy*/, &tv); + + } + + manager ()->commit (); + + } +} + +void MainService::cm_make_array () { size_t n = 0; @@ -2010,129 +2119,6 @@ MainService::cm_tap () } } - -/** - * @brief An iterator for the selected objects of all edt services in a layout view - */ -class EDT_PUBLIC SelectionIterator -{ -public: - typedef lay::ObjectInstPath value_type; - typedef const lay::ObjectInstPath &reference; - typedef const lay::ObjectInstPath *pointer; - - /** - * @brief Creates a new iterator iterating over all selected edt objects from the given view - * - * If "including_transient" is true, the transient selection will be used as fallback. - */ - SelectionIterator (lay::LayoutView *view, bool including_transient = true) - : m_transient_mode (false) - { - mp_edt_services = view->get_plugins (); - - m_current_service = mp_edt_services.begin (); - if (m_current_service != mp_edt_services.end ()) { - m_current_object = (*m_current_service)->selection ().begin (); - } - - next (); - - if (at_end () && including_transient) { - - m_transient_mode = true; - - m_current_service = mp_edt_services.begin (); - if (m_current_service != mp_edt_services.end ()) { - m_current_object = (*m_current_service)->transient_selection ().begin (); - } - - next (); - - } - } - - /** - * @brief Returns a value indicating whether the transient selection is taken - */ - bool is_transient () const - { - return m_transient_mode; - } - - /** - * @brief Increments the iterator - */ - void operator++ () - { - inc (); - next (); - } - - /** - * @brief Dereferencing - */ - const lay::ObjectInstPath &operator* () const - { - tl_assert (! at_end ()); - return *m_current_object; - } - - /** - * @brief Arrow operator - */ - const lay::ObjectInstPath *operator-> () const - { - return & operator* (); - } - - /** - * @brief Returns a value indicating whether the iterator has finished - */ - bool at_end () const - { - return m_current_service == mp_edt_services.end (); - } - -private: - void inc () - { - tl_assert (! at_end ()); - ++m_current_object; - } - - void next () - { - if (at_end ()) { - return; - } - - const edt::Service::objects *sel = m_transient_mode ? &(*m_current_service)->transient_selection () : &(*m_current_service)->selection (); - - while (m_current_object == sel->end ()) { - - ++m_current_service; - - if (m_current_service != mp_edt_services.end ()) { - - sel = m_transient_mode ? &(*m_current_service)->transient_selection () : &(*m_current_service)->selection (); - m_current_object = sel->begin (); - - } else { - break; - } - - } - } - -private: - std::vector mp_edt_services; - std::vector::const_iterator m_current_service; - edt::Service::obj_iterator m_current_object; - bool m_transient_mode; -}; - - void MainService::cm_change_layer () { diff --git a/src/edt/edt/edtMainService.h b/src/edt/edt/edtMainService.h index 743d8a9f4..76a733ee5 100644 --- a/src/edt/edt/edtMainService.h +++ b/src/edt/edt/edtMainService.h @@ -51,6 +51,7 @@ class RoundCornerOptionsDialog; class MakeCellOptionsDialog; class MakeArrayOptionsDialog; class AlignOptionsDialog; +class DistributeOptionsDialog; // ------------------------------------------------------------- @@ -153,6 +154,11 @@ public: */ void cm_align (); + /** + * @brief Distribute the selected shapes and instances + */ + void cm_distribute (); + /** * @brief Flatten instances */ @@ -205,6 +211,13 @@ private: int m_align_hmode; int m_align_vmode; bool m_align_visible_layers; + bool m_hdistribute; + int m_distribute_hmode; + double m_distribute_hpitch, m_distribute_hspace; + bool m_vdistribute; + int m_distribute_vmode; + double m_distribute_vpitch, m_distribute_vspace; + bool m_distribute_visible_layers; std::string m_make_cell_name; int m_origin_mode_x, m_origin_mode_y; bool m_origin_visible_layers_for_bbox; @@ -215,6 +228,7 @@ private: bool m_undo_before_apply; edt::RoundCornerOptionsDialog *mp_round_corners_dialog; edt::AlignOptionsDialog *mp_align_options_dialog; + edt::DistributeOptionsDialog *mp_distribute_options_dialog; lay::FlattenInstOptionsDialog *mp_flatten_inst_options_dialog; edt::MakeCellOptionsDialog *mp_make_cell_options_dialog; edt::MakeArrayOptionsDialog *mp_make_array_options_dialog; @@ -223,6 +237,7 @@ private: void check_no_guiding_shapes (); edt::RoundCornerOptionsDialog *round_corners_dialog (); edt::AlignOptionsDialog *align_options_dialog (); + edt::DistributeOptionsDialog *distribute_options_dialog (); lay::FlattenInstOptionsDialog *flatten_inst_options_dialog (); edt::MakeCellOptionsDialog *make_cell_options_dialog (); edt::MakeArrayOptionsDialog *make_array_options_dialog (); diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 53e7f2753..e7bd816c3 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -225,6 +225,7 @@ public: menu_entries.push_back (lay::menu_item ("edt::sel_change_layer", "change_layer:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Change Layer")))); menu_entries.push_back (lay::menu_item ("edt::sel_tap", "tap:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Tap")) + "(T)")); menu_entries.push_back (lay::menu_item ("edt::sel_align", "align:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Align")))); + menu_entries.push_back (lay::menu_item ("edt::sel_distribute", "distribute:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Distribute")))); menu_entries.push_back (lay::menu_item ("edt::sel_round_corners", "round_corners:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Round Corners")))); menu_entries.push_back (lay::menu_item ("edt::sel_size", "size:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Size Shapes")))); menu_entries.push_back (lay::menu_item ("edt::sel_union", "union:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Merge Shapes")))); diff --git a/src/edt/edt/edtUtils.cc b/src/edt/edt/edtUtils.cc index d99f85ab5..7cc997bf7 100644 --- a/src/edt/edt/edtUtils.cc +++ b/src/edt/edt/edtUtils.cc @@ -26,12 +26,82 @@ #include "dbLibrary.h" #include "edtUtils.h" +#include "edtService.h" + #include "layCellView.h" #include "layLayoutView.h" +#include "layEditable.h" #include "tlException.h" namespace edt { +// ------------------------------------------------------------- +// SelectionIterator implementation + +SelectionIterator::SelectionIterator (lay::LayoutView *view, bool including_transient) + : m_transient_mode (false) +{ + mp_edt_services = view->get_plugins (); + + m_current_service = mp_edt_services.begin (); + if (m_current_service != mp_edt_services.end ()) { + m_current_object = (*m_current_service)->selection ().begin (); + } + + next (); + + if (at_end () && including_transient) { + + m_transient_mode = true; + + m_current_service = mp_edt_services.begin (); + if (m_current_service != mp_edt_services.end ()) { + m_current_object = (*m_current_service)->transient_selection ().begin (); + } + + next (); + + } +} + +bool +SelectionIterator::at_end () const +{ + return m_current_service == mp_edt_services.end (); +} + +void +SelectionIterator::inc () +{ + tl_assert (! at_end ()); + ++m_current_object; +} + +void +SelectionIterator::next () +{ + if (at_end ()) { + return; + } + + const edt::Service::objects *sel = m_transient_mode ? &(*m_current_service)->transient_selection () : &(*m_current_service)->selection (); + + while (m_current_object == sel->end ()) { + + ++m_current_service; + + if (m_current_service != mp_edt_services.end ()) { + + sel = m_transient_mode ? &(*m_current_service)->transient_selection () : &(*m_current_service)->selection (); + m_current_object = sel->begin (); + + } else { + break; + } + + } +} + // ------------------------------------------------------------- // TransformationsVariants implementation // for a lay::LayoutView diff --git a/src/edt/edt/edtUtils.h b/src/edt/edt/edtUtils.h index db41dbd0c..a4a512e7a 100644 --- a/src/edt/edt/edtUtils.h +++ b/src/edt/edt/edtUtils.h @@ -27,9 +27,12 @@ #include #include #include +#include #include +#include "layObjectInstPath.h" + #include "dbInstElement.h" #include "dbClipboardData.h" #include "dbClipboard.h" @@ -42,6 +45,8 @@ namespace lay namespace edt { +class Service; + /** * @brief Fetch PCell parameters from a cell and merge the guiding shapes into them * @@ -79,6 +84,73 @@ private: std::map < std::pair, std::vector > m_per_cv_and_layer_tv; }; +/** + * @brief An iterator for the selected objects of all edt services in a layout view + */ +class SelectionIterator +{ +public: + typedef lay::ObjectInstPath value_type; + typedef const lay::ObjectInstPath &reference; + typedef const lay::ObjectInstPath *pointer; + + /** + * @brief Creates a new iterator iterating over all selected edt objects from the given view + * + * If "including_transient" is true, the transient selection will be used as fallback. + */ + SelectionIterator (lay::LayoutView *view, bool including_transient = true); + + /** + * @brief Returns a value indicating whether the transient selection is taken + */ + bool is_transient () const + { + return m_transient_mode; + } + + /** + * @brief Increments the iterator + */ + void operator++ () + { + inc (); + next (); + } + + /** + * @brief Dereferencing + */ + const lay::ObjectInstPath &operator* () const + { + tl_assert (! at_end ()); + return *m_current_object; + } + + /** + * @brief Arrow operator + */ + const lay::ObjectInstPath *operator-> () const + { + return & operator* (); + } + + /** + * @brief Returns a value indicating whether the iterator has finished + */ + bool at_end () const; + +private: + void inc (); + void next (); + +private: + std::vector mp_edt_services; + std::vector::const_iterator m_current_service; + std::set::const_iterator m_current_object; + bool m_transient_mode; +}; + } // namespace edt #endif From 28520697a38c4d104b61a966eda3bc3ba7ebad55 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 8 Aug 2020 00:14:48 +0200 Subject: [PATCH 033/113] WIP: distribute feature. --- src/edt/edt/edt.pro | 6 +- src/edt/edt/edtDistribute.cc | 27 ++ src/edt/edt/edtDistribute.h | 413 +++++++++++++++++++++++ src/edt/edt/edtMainService.cc | 104 +++--- src/edt/unit_tests/edtDistributeTests.cc | 98 ++++++ src/edt/unit_tests/unit_tests.pro | 1 + src/tl/tl/tlIntervalMap.h | 17 + 7 files changed, 619 insertions(+), 47 deletions(-) create mode 100644 src/edt/edt/edtDistribute.cc create mode 100644 src/edt/edt/edtDistribute.h create mode 100644 src/edt/unit_tests/edtDistributeTests.cc diff --git a/src/edt/edt/edt.pro b/src/edt/edt/edt.pro index b57f0144e..c9f440dca 100644 --- a/src/edt/edt/edt.pro +++ b/src/edt/edt/edt.pro @@ -21,7 +21,8 @@ HEADERS = \ edtServiceImpl.h \ edtUtils.h \ edtCommon.h \ - edtPCellParametersDialog.h + edtPCellParametersDialog.h \ + edtDistribute.h FORMS = \ AlignOptionsDialog.ui \ @@ -60,7 +61,8 @@ SOURCES = \ edtServiceImpl.cc \ edtUtils.cc \ gsiDeclEdt.cc \ - edtPCellParametersDialog.cc + edtPCellParametersDialog.cc \ + edtDistribute.cc INCLUDEPATH += $$TL_INC $$GSI_INC $$LAYBASIC_INC $$DB_INC DEPENDPATH += $$TL_INC $$GSI_INC $$LAYBASIC_INC $$DB_INC diff --git a/src/edt/edt/edtDistribute.cc b/src/edt/edt/edtDistribute.cc new file mode 100644 index 000000000..1b3631cb2 --- /dev/null +++ b/src/edt/edt/edtDistribute.cc @@ -0,0 +1,27 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "edtDistribute.h" + + +// .. nothing yet (all in header) .. + diff --git a/src/edt/edt/edtDistribute.h b/src/edt/edt/edtDistribute.h new file mode 100644 index 000000000..89b725dec --- /dev/null +++ b/src/edt/edt/edtDistribute.h @@ -0,0 +1,413 @@ +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_edtDistribute +#define HDR_edtDistribute + +#include "dbBox.h" + +#include "dbTypes.h" +#include "tlIntervalMap.h" + +namespace edt +{ + +/** + * @brief Gets the box position by reference position + */ +template +typename Box::coord_type box_position (const Box &box, int ref) +{ + if (horizontally) { + if (ref < 0) { + return box.left (); + } else if (ref == 0) { + return box.center ().x (); + } else { + return box.right (); + } + } else { + if (ref < 0) { + return box.bottom (); + } else if (ref == 0) { + return box.center ().y (); + } else { + return box.top (); + } + } +} + +/** + * @brief Compares boxes by their reference position + */ +template +class box_compare +{ +public: + typedef typename Box::coord_type coord_type; + + box_compare (int ref) + : m_ref (ref) + { + // .. nothing yet .. + } + + bool operator() (const std::pair &a, const std::pair &b) const + { + coord_type ca = box_position (a.first, m_ref); + coord_type cb = box_position (b.first, m_ref); + + if (! db::coord_traits::equal (ca, cb)) { + return db::coord_traits::less (ca, cb); + } else { + coord_type ca2 = box_position (a.first, m_ref); + coord_type cb2 = box_position (b.first, m_ref); + return db::coord_traits::less (ca2, cb2); + } + } + +private: + int m_ref; +}; + +/** + * @brief Does some heuristic binning of coordinates + */ +template +void do_bin (typename std::vector >::const_iterator b, typename std::vector >::const_iterator e, int ref, std::vector > &bins) +{ + typedef typename Box::coord_type coord_type; + + // determine maximum distance between adjacent coordinates + + coord_type max_dist = 0; + for (typename std::vector >::const_iterator i = b + 1; i != e; ++i) { + max_dist = std::max (max_dist, box_position (i->first, ref) - box_position ((i - 1)->first, ref)); + } + + // heuristically, everything that has a distance of less than 1/3 of the maximum distance falls into one bin + + coord_type bin_start = box_position (b->first, ref); + bins.push_back (std::vector ()); + bins.back ().push_back (b->second); + + coord_type thr = max_dist / 3; + + for (typename std::vector >::const_iterator i = b + 1; i != e; ++i) { + coord_type c = box_position (i->first, ref); + if (c - bin_start > thr) { + // start a new bin + bins.push_back (std::vector ()); + bin_start = c; + } + bins.back ().push_back (i->second); + } +} + +template +struct max_coord_join_op +{ + void operator() (Coord &a, const Coord &b) const + { + a = std::max (a, b); + } +}; + +/** + * @brief Computes the actual row/columns positions starting from 0 + * The positions are the "low" side positions of the boxes (ref = -1) + */ +template +void compute_positions (int ref, typename Box::coord_type pitch, typename Box::coord_type space, std::vector > &bins, Objects &objects) +{ + typedef typename Box::coord_type coord_type; + + tl::interval_map limits; + + coord_type min, max; + bool first = true; + for (typename Objects::const_iterator o = objects.begin (); o != objects.end (); ++o) { + coord_type b1 = box_position (o->first, -1); + coord_type b2 = box_position (o->first, 1); + if (first) { + min = b1; + max = b2; + } else { + min = std::min (min, b1); + max = std::max (max, b1); + } + } + + max_coord_join_op join_op; + + limits.add (min, max, (coord_type) 0, join_op); + + // Determines the next column/row's position as the minimum position which is compatible with + // the space constraint. + + for (std::vector >::const_iterator b = bins.begin (); b != bins.end (); ++b) { + + coord_type min_pos = 1; + + for (std::vector::const_iterator i = b->begin (); i != b->end (); ++i) { + + const Box &box = objects [*i].first; + + coord_type b1 = box_position (box, -1); + coord_type b2 = box_position (box, 1); + + coord_type start = box_position (box, -1); + coord_type ref_pos = box_position (box, ref); + + for (typename tl::interval_map::const_iterator j = limits.find (b1); j != limits.end () && db::coord_traits::less (j->first.first, b2); ++j) { + min_pos = std::max (min_pos, j->second + space + (ref_pos - start)); + } + + } + + if (pitch > 0) { + min_pos = db::coord_traits::rounded (ceil (double (min_pos) / double (pitch) - 1e-10) * double (pitch)); + } + + for (std::vector::const_iterator i = b->begin (); i != b->end (); ++i) { + + Box &box = objects [*i].first; + + coord_type b1 = box_position (box, -1); + coord_type b2 = box_position (box, 1); + + coord_type ref_pos = box_position (box, ref); + coord_type end_pos = box_position (box, 1); + + if (horizontally) { + box.move (db::vector (min_pos - ref_pos, 0)); + } else { + box.move (db::vector (0, min_pos - ref_pos)); + } + + limits.add (b1, b2, min_pos + (end_pos - ref_pos), join_op); + + } + + } +} + +/** + * @brief Implements an algorithm for 2d-distributing rectangular objects + */ +template +class distributed_placer +{ +public: + typedef typename Box::coord_type coord_type; + typedef std::vector > objects; + typedef typename objects::const_iterator iterator; + + /** + * @brief Constructor + */ + distributed_placer () + { + // .. nothing yet .. + } + + /** + * @brief Reserves space for n objects + */ + void reserve (size_t n) + { + m_objects.reserve (n); + } + + /** + * @brief Inserts a new object + */ + void insert (const Box &box, const Value &value) + { + tl_assert (! box.empty ()); + m_objects.push_back (std::make_pair (box, value)); + } + + /** + * @brief Stored objects iterator: begin + */ + iterator begin () const + { + return m_objects.begin (); + } + + /** + * @brief Stored objects iterator: end + */ + iterator end () const + { + return m_objects.end (); + } + + /** + * @brief Distributes the stored objects in vertical direction only + * + * @param ref The reference location (-1: bottom, 0: center, 1: top) + * @param pitch The distribution pitch (grid) or 0 for no pitch + * @param space The minimum space between the objects + */ + void distribute_v (int ref, coord_type pitch, coord_type space) + { + do_distribute_1d (ref, pitch, space); + } + + /** + * @brief Distributes the stored objects in horizontal direction only + * + * @param ref The reference location (-1: left, 0: center, 1: right) + * @param pitch The distribution pitch (grid) or 0 for no pitch + * @param space The minimum space between the objects + */ + void distribute_h (int ref, coord_type pitch, coord_type space) + { + do_distribute_1d (ref, pitch, space); + } + + /** + * @brief Distributes the stored objects in horizontal and vertical direction + * + * @param href The horizontal reference location (-1: left, 0: center, 1: right) + * @param hpitch The horizontal distribution pitch (grid) or 0 for no pitch + * @param hspace The horizontal minimum space between the objects + * @param vref The vertical reference location (-1: bottom, 0: center, 1: top) + * @param vpitch The vertical distribution pitch (grid) or 0 for no pitch + * @param vspace The vertical minimum space between the objects + */ + void distribute_matrix (int href, coord_type hpitch, coord_type hspace, int vref, coord_type vpitch, coord_type vspace) + { + if (m_objects.size () < 2) { + return; + } + + // The algorithm is this: + // 1.) Bin the boxes according to their positions in horizontal and vertical direction. + // This forms the potential columns and rows + // 2.) Compute the actual column and row positions by applying space and pitch constraints: + // horizontally first, then vertically (TODO: we could try both ways and test which one is better) + + std::vector > indexed_boxes; + indexed_boxes.reserve (m_objects.size ()); + + Box all; + for (typename objects::iterator i = m_objects.begin (); i != m_objects.end (); ++i) { + all += i->first; + } + + size_t n = 0; + for (typename objects::iterator i = m_objects.begin (); i != m_objects.end (); ++i, ++n) { + indexed_boxes.push_back (std::make_pair (i->first, n)); + } + + std::vector > hbins, vbins; + + std::sort (indexed_boxes.begin (), indexed_boxes.end (), box_compare (href)); + do_bin (indexed_boxes.begin (), indexed_boxes.end (), href, hbins); + + std::sort (indexed_boxes.begin (), indexed_boxes.end (), box_compare (vref)); + do_bin (indexed_boxes.begin (), indexed_boxes.end (), vref, vbins); + + compute_positions (href, hpitch, hspace, hbins, m_objects); + compute_positions (vref, vpitch, vspace, vbins, m_objects); + + // Final adjustments + + Box new_all; + for (typename objects::iterator i = m_objects.begin (); i != m_objects.end (); ++i, ++n) { + new_all += i->first; + } + + coord_type dh = box_position (all, href) - box_position (new_all, href); + coord_type dv = box_position (all, vref) - box_position (new_all, vref); + db::vector mv (dh, dv); + + for (typename objects::iterator i = m_objects.begin (); i != m_objects.end (); ++i) { + i->first.move (mv); + } + } + +private: + objects m_objects; + + template + void do_distribute_1d (int ref, coord_type pitch, coord_type space) + { + if (m_objects.size () < 2) { + return; + } + + Box all; + for (typename objects::const_iterator i = m_objects.begin () + 1; i != m_objects.end (); ++i) { + all += i->first; + } + + std::sort (m_objects.begin (), m_objects.end (), box_compare (ref)); + + Box current = m_objects.front ().first; + coord_type p0 = box_position (current, ref); + + for (typename objects::iterator i = m_objects.begin () + 1; i != m_objects.end (); ++i) { + + coord_type p = box_position (i->first, -1); + coord_type offset = box_position (i->first, ref) - p; + coord_type pnew = box_position (current, 1) + space; + + if (db::coord_traits::less (0, pitch)) { + pnew = coord_type (ceil (double (pnew + offset - p0) / double (pitch) - 1e-10)) * pitch - offset; + } + + db::vector mv; + if (horizontally) { + mv = db::vector (pnew - p, 0); + } else { + mv = db::vector (0, pnew - p); + } + + i->first.move (mv); + current = i->first; + + } + + // final adjustment + Box new_all = m_objects.front ().first + m_objects.back ().first; + + db::vector mv; + coord_type d = box_position (all, ref) - box_position (new_all, ref); + if (horizontally) { + mv = db::vector (d, 0); + } else { + mv = db::vector (0, d); + } + + for (typename objects::iterator i = m_objects.begin (); i != m_objects.end (); ++i) { + i->first.move (mv); + } + } +}; + +} + +#endif + diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index 89df404ac..614e1f4e4 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -43,6 +43,7 @@ #include "edtConfig.h" #include "edtDialogs.h" #include "edtEditorOptionsPages.h" +#include "edtDistribute.h" #include #include @@ -1872,73 +1873,86 @@ MainService::cm_distribute () return; } - db::DBox prim_box; - bool has_secondary = false; + if (! m_hdistribute && ! m_vdistribute) { + return; + } - // get (common) bbox index of the primary selection + // count the items + size_t n = 0; for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) { + ++n; + } + } - if (s->seq () == 0) { + std::vector > objects_for_service; + std::vector transformations; + + { + + std::vector org_boxes; + org_boxes.reserve (n); + + edt::distributed_placer placer; + placer.reserve (n); + + size_t i = 0; + + for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { + + objects_for_service.push_back (std::make_pair (i, i)); + + for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) { const db::Layout &layout = view ()->cellview (s->cv_index ())->layout (); db::CplxTrans tr = db::CplxTrans (layout.dbu ()) * s->trans (); + db::DBox box; if (! s->is_cell_inst ()) { - prim_box += tr * s->shape ().bbox (); + box = tr * s->shape ().bbox (); } else { - prim_box += inst_bbox (tr, view (), s->cv_index (), s->back (), m_distribute_visible_layers); + box = inst_bbox (tr, view (), s->cv_index (), s->back (), m_distribute_visible_layers); } - } else { - has_secondary = true; + org_boxes.push_back (box); + placer.insert (box, i); + + ++i; + } + objects_for_service.back ().second = i; + } + + if (m_hdistribute && m_vdistribute) { + placer.distribute_matrix (int (m_distribute_hmode - 2), m_distribute_hpitch, m_distribute_hspace, + 2 - int (m_distribute_vmode), m_distribute_vpitch, m_distribute_vspace); + } else if (m_hdistribute) { + placer.distribute_h (int (m_distribute_hmode - 2), m_distribute_hpitch, m_distribute_hspace); + } else if (m_vdistribute) { + placer.distribute_v (2 - int (m_distribute_vmode), m_distribute_vpitch, m_distribute_vspace); + } + + transformations.resize (org_boxes.size ()); + + for (edt::distributed_placer::iterator i = placer.begin (); i != placer.end (); ++i) { + transformations[i->second] = db::DCplxTrans (i->first.p1 () - org_boxes[i->second].p1 ()); + } + } - if (! prim_box.empty ()) { - + { view ()->cancel_edits (); - manager ()->transaction (tl::to_string (QObject::tr ("Alignment"))); + manager ()->transaction (tl::to_string (QObject::tr ("Distribution"))); - - - // do the alignment + // do the distribution for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { + size_t ie = es - edt_services.begin (); + // create a transformation vector that describes each shape's transformation - std::vector tv; - tv.reserve ((*es)->selection ().size ()); - - for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) { - - db::DVector v; - -// @@@ - if (s->seq () > 0 || !has_secondary) { - - db::Layout &layout = view ()->cellview (s->cv_index ())->layout (); - db::CplxTrans tr = db::CplxTrans (layout.dbu ()) * s->trans (); - - if (! s->is_cell_inst ()) { - - db::DBox box = tr * s->shape ().bbox (); - v = compute_alignment_vector (prim_box, box, m_distribute_hmode, m_distribute_vmode); - - } else { - - db::DBox box = inst_bbox (tr, view (), s->cv_index (), s->back (), m_distribute_visible_layers); - v = compute_alignment_vector (prim_box, box, m_distribute_hmode, m_distribute_vmode); - - } - - } -// @@@ - - tv.push_back (db::DCplxTrans (db::DTrans (v))); - - } + std::vector tv (transformations.begin () + objects_for_service [ie].first, transformations.begin () + objects_for_service [ie].second); // use the "transform" method to transform the shapes and instances (with individual transformations) (*es)->transform (db::DCplxTrans () /*dummy*/, &tv); diff --git a/src/edt/unit_tests/edtDistributeTests.cc b/src/edt/unit_tests/edtDistributeTests.cc new file mode 100644 index 000000000..aa5927a81 --- /dev/null +++ b/src/edt/unit_tests/edtDistributeTests.cc @@ -0,0 +1,98 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "tlUnitTest.h" + +#include "edtDistribute.h" + +template +static std::string plc2string (const edt::distributed_placer &plc) +{ + std::string s; + for (typename edt::distributed_placer::iterator i = plc.begin (); i != plc.end (); ++i) { + if (! s.empty ()) { + s += ","; + } + s += tl::to_string (i->first); + s += "["; + s += tl::to_string (i->second); + s += "]"; + } + return s; +} + +TEST(1) +{ + edt::distributed_placer placer; + + placer.insert (db::Box (1000, 0, 1100, 200), 0); + placer.insert (db::Box (2000, 0, 2100, 500), 1); + placer.insert (db::Box (0, -100, 100, 100), 2); + placer.insert (db::Box (1000, 100, 1050, 250), 3); + placer.insert (db::Box (1050, -50, 1100, 150), 4); + + edt::distributed_placer p; + + p = placer; + p.distribute_h (-1, 0, 100); + + EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(200,0;300,200)[0],(400,100;450,250)[3],(550,-50;600,150)[4],(700,0;800,500)[1]"); + + p = placer; + p.distribute_h (-1, 100, 0); + + EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(100,0;200,200)[0],(200,100;250,250)[3],(300,-50;350,150)[4],(400,0;500,500)[1]"); + + p = placer; + p.distribute_h (-1, 0, 0); + + EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(100,0;200,200)[0],(200,100;250,250)[3],(250,-50;300,150)[4],(300,0;400,500)[1]"); + + p = placer; + p.distribute_h (1, 0, 100); + + EXPECT_EQ (plc2string (p), "(1300,-100;1400,100)[2],(1500,100;1550,250)[3],(1650,-50;1700,150)[4],(1800,0;1900,200)[0],(2000,0;2100,500)[1]"); + + p = placer; + p.distribute_v (-1, 0, 100); + + EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(1050,200;1100,400)[4],(1000,500;1100,700)[0],(2000,800;2100,1300)[1],(1000,1400;1050,1550)[3]"); +} + + +TEST(2) +{ + edt::distributed_placer placer; + + placer.insert (db::Box (-5, 1, 95, 101), 0); + placer.insert (db::Box (1, 95, 101, 195), 1); + placer.insert (db::Box (110, 105, 210, 205), 2); + placer.insert (db::Box (101, 0, 201, 100), 3); + + edt::distributed_placer p; + + p = placer; + p.distribute_matrix (-1, 0, 0, -1, 0, 0); + + EXPECT_EQ (plc2string (p), "(-5,0;95,100)[0],(-5,100;95,200)[1],(95,100;195,200)[2],(95,0;195,100)[3]"); +} + diff --git a/src/edt/unit_tests/unit_tests.pro b/src/edt/unit_tests/unit_tests.pro index 630f1cd9b..14be20d9e 100644 --- a/src/edt/unit_tests/unit_tests.pro +++ b/src/edt/unit_tests/unit_tests.pro @@ -8,6 +8,7 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ edtBasicTests.cc \ + edtDistributeTests.cc INCLUDEPATH += $$EDT_INC $$TL_INC $$LAYBASIC_INC $$DB_INC $$GSI_INC DEPENDPATH += $$EDT_INC $$TL_INC $$LAYBASIC_INC $$DB_INC $$GSI_INC diff --git a/src/tl/tl/tlIntervalMap.h b/src/tl/tl/tlIntervalMap.h index edfb819d9..0f84b29b9 100644 --- a/src/tl/tl/tlIntervalMap.h +++ b/src/tl/tl/tlIntervalMap.h @@ -270,6 +270,23 @@ public: } } + /** + * @brief Returns the iterator for a given index + * + * This will return the iterator for the interval which contains the index. If there is no such interval, this method + * will return end(). + * + * If there is no interval for the given index (not set), the returned iterator will not be end(), but it's + * start value will not be less or equal to the index. + * + * @param i The index to search for + * @return The iterator to the corresponding interval or end() if there is no such interval + */ + const_iterator find (I i) const + { + return std::lower_bound (m_index_map.begin (), m_index_map.end (), i, iv_compare_f ()); + } + /** * @brief Do a brief check if the structure is sorted */ From 9842ff8f1bfc8da6daf34d91a420cd9ed8dc38dd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 8 Aug 2020 14:11:59 +0200 Subject: [PATCH 034/113] WIP: new implementation of distribute algorithm - the previous one wasn't really smart. --- src/edt/edt/DistributeOptionsDialog.ui | 629 +++++++++++++---------- src/edt/edt/edtDialogs.cc | 6 +- src/edt/edt/edtDistribute.h | 220 +++++++- src/edt/edt/edtMainService.cc | 13 +- src/edt/unit_tests/edtDistributeTests.cc | 71 ++- 5 files changed, 633 insertions(+), 306 deletions(-) diff --git a/src/edt/edt/DistributeOptionsDialog.ui b/src/edt/edt/DistributeOptionsDialog.ui index 0d2bf52e7..74929ebe1 100644 --- a/src/edt/edt/DistributeOptionsDialog.ui +++ b/src/edt/edt/DistributeOptionsDialog.ui @@ -6,7 +6,7 @@ 0 0 - 716 + 689 574 @@ -23,147 +23,17 @@ true - - + + - Alignment + The pitch specifies the offset at which the objects are placed relative to each other. The space is the minimum distance between the objects. + + + true - - - - right - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - center - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - 243 - 20 - - - - - - - - - - - - :/align_left.png:/align_left.png - - - - 32 - 32 - - - - - - - - - - - - :/align_right.png:/align_right.png - - - - 32 - 32 - - - - - - - - Qt::Horizontal - - - - - - - - - - - :/align_hcenter.png:/align_hcenter.png - - - - 32 - 32 - - - - - - - - left - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - + QFrame::NoFrame @@ -264,16 +134,6 @@ - - - - The pitch specifies the offset at which the objects are placed relative to each other. The space is the minimum distance between the objects. - - - true - - - @@ -282,7 +142,7 @@ 0 - 200 + 0 @@ -291,87 +151,8 @@ true - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - top - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - :/align_vcenter.png:/align_vcenter.png - - - - 32 - 32 - - - - - - - - Qt::Horizontal - - - - 243 - 34 - - - - - - - - Qt::Horizontal - - - - + + QFrame::NoFrame @@ -472,44 +253,7 @@ - - - - center - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - bottom - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - :/align_bottom.png:/align_bottom.png - - - - 32 - 32 - - - - - + The pitch specifies the offset at which the objects are placed relative to each other. The space is the minimum distance between the objects. @@ -519,7 +263,227 @@ - + + + + + + + Horizonal alignment + + + + + + + + + + :/align_none.png:/align_none.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + :/align_left.png:/align_left.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + :/align_hcenter.png:/align_hcenter.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + :/align_right.png:/align_right.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + + 245 + 20 + + + + + + + + none + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + left + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + center + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + right + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Vertical alignment + + + + + + + + + + :/align_none.png:/align_none.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + @@ -536,13 +500,132 @@ + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + :/align_vcenter.png:/align_vcenter.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + :/align_bottom.png:/align_bottom.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + + 245 + 20 + + + + + + + + none + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + top + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + center + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + bottom + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + - Layers for distribution of instances + For the computation of cell instance bounding boxes ... @@ -590,12 +673,6 @@ all_layers_rb visible_layers_rb - h_left_rb - h_center_rb - h_right_rb - v_top_rb - v_center_rb - v_bottom_rb buttonBox diff --git a/src/edt/edt/edtDialogs.cc b/src/edt/edt/edtDialogs.cc index 63ac14fdc..21a0f6393 100644 --- a/src/edt/edt/edtDialogs.cc +++ b/src/edt/edt/edtDialogs.cc @@ -343,10 +343,10 @@ DistributeOptionsDialog::~DistributeOptionsDialog () } bool -DistributeOptionsDialog::exec_dialog (lay::LayoutView *view, bool &hdistribute, int &hmode, double &hpitch, double &hspace, bool &vdistribute, int &vmode, double &vpitch, double &vspace, bool &visible_layers) +DistributeOptionsDialog::exec_dialog (lay::LayoutView * /*view*/, bool &hdistribute, int &hmode, double &hpitch, double &hspace, bool &vdistribute, int &vmode, double &vpitch, double &vspace, bool &visible_layers) { - QRadioButton *hmode_buttons [] = { (QRadioButton *) 0, this->h_left_rb, this->h_center_rb, this->h_right_rb }; - QRadioButton *vmode_buttons [] = { (QRadioButton *) 0, this->v_top_rb, this->v_center_rb, this->v_bottom_rb }; + QRadioButton *hmode_buttons [] = { this->h_none_rb, this->h_left_rb, this->h_center_rb, this->h_right_rb }; + QRadioButton *vmode_buttons [] = { this->v_none_rb, this->v_top_rb, this->v_center_rb, this->v_bottom_rb }; QRadioButton *layers_buttons [] = { this->all_layers_rb, this->visible_layers_rb }; this->h_distribute->setChecked (hdistribute); diff --git a/src/edt/edt/edtDistribute.h b/src/edt/edt/edtDistribute.h index 89b725dec..8f40dd024 100644 --- a/src/edt/edt/edtDistribute.h +++ b/src/edt/edt/edtDistribute.h @@ -150,6 +150,7 @@ void compute_positions (int ref, typename Box::coord_type pitch, typename Box::c if (first) { min = b1; max = b2; + first = false; } else { min = std::min (min, b1); max = std::max (max, b1); @@ -163,9 +164,19 @@ void compute_positions (int ref, typename Box::coord_type pitch, typename Box::c // Determines the next column/row's position as the minimum position which is compatible with // the space constraint. + coord_type prev_bin_end = 0; + bool prev_bin_end_set = false; + for (std::vector >::const_iterator b = bins.begin (); b != bins.end (); ++b) { coord_type min_pos = 1; + coord_type bin_end = prev_bin_end; + bool bin_end_set = prev_bin_end_set; + + coord_type max_limit = 0; + for (typename tl::interval_map::const_iterator j = limits.begin (); j != limits.end (); ++j) { + max_limit = std::max (max_limit, j->second); + } for (std::vector::const_iterator i = b->begin (); i != b->end (); ++i) { @@ -176,13 +187,32 @@ void compute_positions (int ref, typename Box::coord_type pitch, typename Box::c coord_type start = box_position (box, -1); coord_type ref_pos = box_position (box, ref); + coord_type end_pos = box_position (box, 1); + + bin_end = bin_end_set ? std::max (end_pos, bin_end) : end_pos; + bin_end_set = true; + + if (prev_bin_end_set && db::coord_traits::less (start, prev_bin_end)) { + + // for boxes overlapping into the previous bin try to shift them into the previous bin + for (typename tl::interval_map::const_iterator j = limits.find (b1); j != limits.end () && db::coord_traits::less (j->first.first, b2); ++j) { + if (db::coord_traits::less (b1, j->first.second)) { + min_pos = std::max (min_pos, j->second + space + (ref_pos - start)); + } + } + + } else { + + // otherwise separate the bins + min_pos = std::max (min_pos, max_limit + space + (ref_pos - start)); - for (typename tl::interval_map::const_iterator j = limits.find (b1); j != limits.end () && db::coord_traits::less (j->first.first, b2); ++j) { - min_pos = std::max (min_pos, j->second + space + (ref_pos - start)); } } + prev_bin_end = bin_end; + prev_bin_end_set = bin_end_set; + if (pitch > 0) { min_pos = db::coord_traits::rounded (ceil (double (min_pos) / double (pitch) - 1e-10) * double (pitch)); } @@ -194,16 +224,29 @@ void compute_positions (int ref, typename Box::coord_type pitch, typename Box::c coord_type b1 = box_position (box, -1); coord_type b2 = box_position (box, 1); + coord_type start = box_position (box, -1); coord_type ref_pos = box_position (box, ref); coord_type end_pos = box_position (box, 1); - if (horizontally) { - box.move (db::vector (min_pos - ref_pos, 0)); - } else { - box.move (db::vector (0, min_pos - ref_pos)); + // because multiple objects may fall into one bin we need to look up again: + coord_type pos = min_pos; + for (typename tl::interval_map::const_iterator j = limits.find (b1); j != limits.end () && db::coord_traits::less (j->first.first, b2); ++j) { + if (db::coord_traits::less (b1, j->first.second)) { + pos = std::max (pos, j->second + space + (ref_pos - start)); + } } - limits.add (b1, b2, min_pos + (end_pos - ref_pos), join_op); + if (pitch > 0 && ! db::coord_traits::equal (pos, min_pos)) { + pos = db::coord_traits::rounded (ceil (double (pos) / double (pitch) - 1e-10) * double (pitch)); + } + + if (horizontally) { + box.move (db::vector (pos - ref_pos, 0)); + } else { + box.move (db::vector (0, pos - ref_pos)); + } + + limits.add (b1, b2, pos + (end_pos - ref_pos), join_op); } @@ -266,24 +309,26 @@ public: * @brief Distributes the stored objects in vertical direction only * * @param ref The reference location (-1: bottom, 0: center, 1: top) + * @param refp The alignment in the other (horizontal) direction (-1: left, 0: center, 1: right, other: leave as is) * @param pitch The distribution pitch (grid) or 0 for no pitch * @param space The minimum space between the objects */ - void distribute_v (int ref, coord_type pitch, coord_type space) + void distribute_v (int ref, int refp, coord_type pitch, coord_type space) { - do_distribute_1d (ref, pitch, space); + do_distribute_1d (ref, refp, pitch, space); } /** * @brief Distributes the stored objects in horizontal direction only * * @param ref The reference location (-1: left, 0: center, 1: right) + * @param refp The alignment in the other (vertical) direction (-1: bottom, 0: center, 1: top, other: leave as is) * @param pitch The distribution pitch (grid) or 0 for no pitch * @param space The minimum space between the objects */ - void distribute_h (int ref, coord_type pitch, coord_type space) + void distribute_h (int ref, int refp, coord_type pitch, coord_type space) { - do_distribute_1d (ref, pitch, space); + do_distribute_1d (ref, refp, pitch, space); } /** @@ -305,8 +350,8 @@ public: // The algorithm is this: // 1.) Bin the boxes according to their positions in horizontal and vertical direction. // This forms the potential columns and rows - // 2.) Compute the actual column and row positions by applying space and pitch constraints: - // horizontally first, then vertically (TODO: we could try both ways and test which one is better) + // 2.) Compute the row and column widths and heights as the maximum of their content + // 3.) position the objects inside these cells std::vector > indexed_boxes; indexed_boxes.reserve (m_objects.size ()); @@ -329,10 +374,134 @@ public: std::sort (indexed_boxes.begin (), indexed_boxes.end (), box_compare (vref)); do_bin (indexed_boxes.begin (), indexed_boxes.end (), vref, vbins); - compute_positions (href, hpitch, hspace, hbins, m_objects); - compute_positions (vref, vpitch, vspace, vbins, m_objects); + std::vector > > cells; - // Final adjustments + cells.resize (hbins.size ()); + for (size_t i = 0; i < hbins.size (); ++i) { + cells [i].resize (vbins.size ()); + } + + { + + std::vector hbin_for_index; + hbin_for_index.resize (indexed_boxes.size (), size_t (0)); + for (std::vector >::const_iterator i = hbins.begin (); i != hbins.end (); ++i) { + for (std::vector::const_iterator j = i->begin (); j != i->end (); ++j) { + hbin_for_index [*j] = i - hbins.begin (); + } + } + + for (std::vector >::const_iterator i = vbins.begin (); i != vbins.end (); ++i) { + for (std::vector::const_iterator j = i->begin (); j != i->end (); ++j) { + cells [hbin_for_index [*j]][i - vbins.begin ()].push_back (*j); + } + } + + } + + std::vector cell_widths, cell_heights; + cell_widths.resize (hbins.size (), 0); + cell_heights.resize (vbins.size (), 0); + + for (std::vector > >::const_iterator i = cells.begin (); i != cells.end (); ++i) { + + for (std::vector >::const_iterator j = i->begin (); j != i->end (); ++j) { + + coord_type wcell = 0, hcell = 0; + + for (std::vector::const_iterator k = j->begin (); k != j->end (); ++k) { + + coord_type w = m_objects [*k].first.width () + hspace; + if (hpitch > 0) { + w = db::coord_traits::rounded (ceil (double (w) / double (hpitch) - 1e-10) * double (hpitch)); + } + // NOTE: intra-cell objects are distributed horizontally + wcell += w; + + coord_type h = m_objects [*k].first.height () + vspace; + if (vpitch > 0) { + h = db::coord_traits::rounded (ceil (double (h) / double (vpitch) - 1e-10) * double (vpitch)); + } + hcell = std::max (hcell, h); + + } + + cell_widths [i - cells.begin ()] = std::max (cell_widths [i - cells.begin ()], wcell); + cell_heights [j - i->begin ()] = std::max (cell_heights [j - i->begin ()], hcell); + + } + + } + + std::vector cell_xpos, cell_ypos; + cell_xpos.reserve (cell_widths.size ()); + cell_ypos.reserve (cell_heights.size ()); + + coord_type x = 0, y = 0; + for (typename std::vector::const_iterator i = cell_widths.begin (); i != cell_widths.end (); ++i) { + cell_xpos.push_back (x); + x += *i; + } + for (typename std::vector::const_iterator i = cell_heights.begin (); i != cell_heights.end (); ++i) { + cell_ypos.push_back (y); + y += *i; + } + + for (std::vector > >::const_iterator i = cells.begin (); i != cells.end (); ++i) { + + for (std::vector >::const_iterator j = i->begin (); j != i->end (); ++j) { + + coord_type wcell = 0; + + for (std::vector::const_iterator k = j->begin (); k != j->end (); ++k) { + + coord_type w = m_objects [*k].first.width () + hspace; + if (hpitch > 0) { + w = db::coord_traits::rounded (ceil (double (w) / double (hpitch) - 1e-10) * double (hpitch)); + } + // NOTE: intra-cell objects are distributed horizontally + wcell += w; + + } + + coord_type x = cell_xpos [i - cells.begin ()]; + if (href == 0) { + x += (cell_widths [i - cells.begin ()] - wcell) / 2; + } else if (href > 0) { + x += (cell_widths [i - cells.begin ()] - wcell); + } + + for (std::vector::const_iterator k = j->begin (); k != j->end (); ++k) { + + coord_type w = m_objects [*k].first.width () + hspace; + if (hpitch > 0) { + w = db::coord_traits::rounded (ceil (double (w) / double (hpitch) - 1e-10) * double (hpitch)); + } + + coord_type h = m_objects [*k].first.height () + vspace; + if (vpitch > 0) { + h = db::coord_traits::rounded (ceil (double (h) / double (vpitch) - 1e-10) * double (vpitch)); + } + + coord_type y = cell_ypos [j - i->begin ()]; + if (vref == 0) { + y += (cell_heights [j - i->begin ()] - h) / 2; + } else if (href > 0) { + y += (cell_heights [j - i->begin ()] - h); + } + + m_objects [*k].first.move (db::point (x, y) - m_objects [*k].first.p1 ()); + + // NOTE: intra-cell objects are distributed horizontally + x += w; + + } + + } + + } + + // Final adjustments - align the whole matrix with the original bounding box Box new_all; for (typename objects::iterator i = m_objects.begin (); i != m_objects.end (); ++i, ++n) { @@ -352,7 +521,7 @@ private: objects m_objects; template - void do_distribute_1d (int ref, coord_type pitch, coord_type space) + void do_distribute_1d (int ref, int refp, coord_type pitch, coord_type space) { if (m_objects.size () < 2) { return; @@ -402,7 +571,24 @@ private: } for (typename objects::iterator i = m_objects.begin (); i != m_objects.end (); ++i) { + i->first.move (mv); + + if (refp >= -1 && refp <= 1) { + + coord_type dp = box_position (all, refp) - box_position (i->first, refp); + + db::vector mvp; + if (horizontally) { + mvp = db::vector (0, dp); + } else { + mvp = db::vector (dp, 0); + } + + i->first.move (mvp); + + } + } } }; diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index 614e1f4e4..71b210ac7 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -1869,7 +1869,7 @@ MainService::cm_distribute () if (! distribute_options_dialog ()->exec_dialog (view (), m_hdistribute, m_distribute_hmode, m_distribute_hpitch, m_distribute_hspace, m_vdistribute, m_distribute_vmode, m_distribute_vpitch, m_distribute_vspace, - m_align_visible_layers)) { + m_distribute_visible_layers)) { return; } @@ -1925,13 +1925,16 @@ MainService::cm_distribute () } + int href = int (m_distribute_hmode - 2); + int vref = 2 - int (m_distribute_vmode); + if (m_hdistribute && m_vdistribute) { - placer.distribute_matrix (int (m_distribute_hmode - 2), m_distribute_hpitch, m_distribute_hspace, - 2 - int (m_distribute_vmode), m_distribute_vpitch, m_distribute_vspace); + placer.distribute_matrix (href, m_distribute_hpitch, m_distribute_hspace, + vref, m_distribute_vpitch, m_distribute_vspace); } else if (m_hdistribute) { - placer.distribute_h (int (m_distribute_hmode - 2), m_distribute_hpitch, m_distribute_hspace); + placer.distribute_h (href, vref, m_distribute_hpitch, m_distribute_hspace); } else if (m_vdistribute) { - placer.distribute_v (2 - int (m_distribute_vmode), m_distribute_vpitch, m_distribute_vspace); + placer.distribute_v (vref, href, m_distribute_vpitch, m_distribute_vspace); } transformations.resize (org_boxes.size ()); diff --git a/src/edt/unit_tests/edtDistributeTests.cc b/src/edt/unit_tests/edtDistributeTests.cc index aa5927a81..1a3288f61 100644 --- a/src/edt/unit_tests/edtDistributeTests.cc +++ b/src/edt/unit_tests/edtDistributeTests.cc @@ -53,27 +53,42 @@ TEST(1) edt::distributed_placer p; p = placer; - p.distribute_h (-1, 0, 100); + p.distribute_h (-1, 2, 0, 100); EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(200,0;300,200)[0],(400,100;450,250)[3],(550,-50;600,150)[4],(700,0;800,500)[1]"); p = placer; - p.distribute_h (-1, 100, 0); + p.distribute_h (-1, -1, 0, 100); + + EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(200,-100;300,100)[0],(400,-100;450,50)[3],(550,-100;600,100)[4],(700,-100;800,400)[1]"); + + p = placer; + p.distribute_h (-1, 0, 0, 100); + + EXPECT_EQ (plc2string (p), "(0,100;100,300)[2],(200,100;300,300)[0],(400,125;450,275)[3],(550,100;600,300)[4],(700,-50;800,450)[1]"); + + p = placer; + p.distribute_h (-1, 1, 0, 100); + + EXPECT_EQ (plc2string (p), "(0,300;100,500)[2],(200,300;300,500)[0],(400,350;450,500)[3],(550,300;600,500)[4],(700,0;800,500)[1]"); + + p = placer; + p.distribute_h (-1, 2, 100, 0); EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(100,0;200,200)[0],(200,100;250,250)[3],(300,-50;350,150)[4],(400,0;500,500)[1]"); p = placer; - p.distribute_h (-1, 0, 0); + p.distribute_h (-1, 2, 0, 0); EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(100,0;200,200)[0],(200,100;250,250)[3],(250,-50;300,150)[4],(300,0;400,500)[1]"); p = placer; - p.distribute_h (1, 0, 100); + p.distribute_h (1, 2, 0, 100); EXPECT_EQ (plc2string (p), "(1300,-100;1400,100)[2],(1500,100;1550,250)[3],(1650,-50;1700,150)[4],(1800,0;1900,200)[0],(2000,0;2100,500)[1]"); p = placer; - p.distribute_v (-1, 0, 100); + p.distribute_v (-1, 2, 0, 100); EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(1050,200;1100,400)[4],(1000,500;1100,700)[0],(2000,800;2100,1300)[1],(1000,1400;1050,1550)[3]"); } @@ -96,3 +111,49 @@ TEST(2) EXPECT_EQ (plc2string (p), "(-5,0;95,100)[0],(-5,100;95,200)[1],(95,100;195,200)[2],(95,0;195,100)[3]"); } +TEST(3) +{ + edt::distributed_placer placer; + + placer.insert (db::Box (0, 20, 1, 23), 0); + placer.insert (db::Box (3, 8, 8, 19), 1); + placer.insert (db::Box (6, 0, 12, 5), 2); + placer.insert (db::Box (13, 1, 19, 6), 3); + placer.insert (db::Box (10, 16, 11, 17), 4); + + edt::distributed_placer p; + + p = placer; + p.distribute_matrix (-1, 0, 0, -1, 0, 0); + + EXPECT_EQ (plc2string (p), "(0,17;1,20)[0],(1,5;6,16)[1],(6,0;12,5)[2],(13,0;19,5)[3],(12,16;13,17)[4]"); +} + +TEST(4) +{ + edt::distributed_placer placer; + + placer.insert (db::Box (0, 16, 1, 20), 0); + placer.insert (db::Box (0, 8, 5, 19), 1); + placer.insert (db::Box (0, 0, 12, 5), 2); + placer.insert (db::Box (12, 1, 19, 6), 3); + placer.insert (db::Box (0, 18, 1, 19), 4); + + edt::distributed_placer p; + + p = placer; + p.distribute_matrix (-1, 0, 0, 1, 0, 0); + + EXPECT_EQ (plc2string (p), "(6,9;7,13)[0],(1,9;6,20)[1],(0,4;12,9)[2],(12,4;19,9)[3],(0,9;1,10)[4]"); + + p = placer; + p.distribute_matrix (1, 10, 0, -1, 10, 0); + + EXPECT_EQ (plc2string (p), "(-38,30;-37,34)[0],(-18,10;-13,21)[1],(-8,0;4,5)[2],(12,0;19,5)[3],(-28,30;-27,31)[4]"); + + p = placer; + p.distribute_matrix (1, 0, 1, 1, 0, 1); + + EXPECT_EQ (plc2string (p), "(-9,16;-8,20)[0],(-7,9;-2,20)[1],(-1,3;11,8)[2],(12,3;19,8)[3],(-11,19;-10,20)[4]"); +} + From 1a191fbee9b2d88dfe33bcb7ba955cb619f6c34c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 8 Aug 2020 14:26:33 +0200 Subject: [PATCH 035/113] Small refactoring --- src/edt/edt/edtDistribute.h | 183 ++++++------------------------------ 1 file changed, 30 insertions(+), 153 deletions(-) diff --git a/src/edt/edt/edtDistribute.h b/src/edt/edt/edtDistribute.h index 8f40dd024..2a2746242 100644 --- a/src/edt/edt/edtDistribute.h +++ b/src/edt/edt/edtDistribute.h @@ -122,6 +122,21 @@ void do_bin (typename std::vector >::const_iterator b, ty } } +/** + * @brief Computes the effective box width (rounded to pitch, space added) + */ +template +inline typename Box::coord_type eff_dim (const Box &box, typename Box::coord_type pitch, typename Box::coord_type space) +{ + typedef typename Box::coord_type coord_type; + + coord_type d = (horizontal ? box.width () : box.height ()) + space; + if (pitch > 0) { + d = db::coord_traits::rounded (ceil (double (d) / double (pitch) - 1e-10) * double (pitch)); + } + return d; +} + template struct max_coord_join_op { @@ -131,128 +146,6 @@ struct max_coord_join_op } }; -/** - * @brief Computes the actual row/columns positions starting from 0 - * The positions are the "low" side positions of the boxes (ref = -1) - */ -template -void compute_positions (int ref, typename Box::coord_type pitch, typename Box::coord_type space, std::vector > &bins, Objects &objects) -{ - typedef typename Box::coord_type coord_type; - - tl::interval_map limits; - - coord_type min, max; - bool first = true; - for (typename Objects::const_iterator o = objects.begin (); o != objects.end (); ++o) { - coord_type b1 = box_position (o->first, -1); - coord_type b2 = box_position (o->first, 1); - if (first) { - min = b1; - max = b2; - first = false; - } else { - min = std::min (min, b1); - max = std::max (max, b1); - } - } - - max_coord_join_op join_op; - - limits.add (min, max, (coord_type) 0, join_op); - - // Determines the next column/row's position as the minimum position which is compatible with - // the space constraint. - - coord_type prev_bin_end = 0; - bool prev_bin_end_set = false; - - for (std::vector >::const_iterator b = bins.begin (); b != bins.end (); ++b) { - - coord_type min_pos = 1; - coord_type bin_end = prev_bin_end; - bool bin_end_set = prev_bin_end_set; - - coord_type max_limit = 0; - for (typename tl::interval_map::const_iterator j = limits.begin (); j != limits.end (); ++j) { - max_limit = std::max (max_limit, j->second); - } - - for (std::vector::const_iterator i = b->begin (); i != b->end (); ++i) { - - const Box &box = objects [*i].first; - - coord_type b1 = box_position (box, -1); - coord_type b2 = box_position (box, 1); - - coord_type start = box_position (box, -1); - coord_type ref_pos = box_position (box, ref); - coord_type end_pos = box_position (box, 1); - - bin_end = bin_end_set ? std::max (end_pos, bin_end) : end_pos; - bin_end_set = true; - - if (prev_bin_end_set && db::coord_traits::less (start, prev_bin_end)) { - - // for boxes overlapping into the previous bin try to shift them into the previous bin - for (typename tl::interval_map::const_iterator j = limits.find (b1); j != limits.end () && db::coord_traits::less (j->first.first, b2); ++j) { - if (db::coord_traits::less (b1, j->first.second)) { - min_pos = std::max (min_pos, j->second + space + (ref_pos - start)); - } - } - - } else { - - // otherwise separate the bins - min_pos = std::max (min_pos, max_limit + space + (ref_pos - start)); - - } - - } - - prev_bin_end = bin_end; - prev_bin_end_set = bin_end_set; - - if (pitch > 0) { - min_pos = db::coord_traits::rounded (ceil (double (min_pos) / double (pitch) - 1e-10) * double (pitch)); - } - - for (std::vector::const_iterator i = b->begin (); i != b->end (); ++i) { - - Box &box = objects [*i].first; - - coord_type b1 = box_position (box, -1); - coord_type b2 = box_position (box, 1); - - coord_type start = box_position (box, -1); - coord_type ref_pos = box_position (box, ref); - coord_type end_pos = box_position (box, 1); - - // because multiple objects may fall into one bin we need to look up again: - coord_type pos = min_pos; - for (typename tl::interval_map::const_iterator j = limits.find (b1); j != limits.end () && db::coord_traits::less (j->first.first, b2); ++j) { - if (db::coord_traits::less (b1, j->first.second)) { - pos = std::max (pos, j->second + space + (ref_pos - start)); - } - } - - if (pitch > 0 && ! db::coord_traits::equal (pos, min_pos)) { - pos = db::coord_traits::rounded (ceil (double (pos) / double (pitch) - 1e-10) * double (pitch)); - } - - if (horizontally) { - box.move (db::vector (pos - ref_pos, 0)); - } else { - box.move (db::vector (0, pos - ref_pos)); - } - - limits.add (b1, b2, pos + (end_pos - ref_pos), join_op); - - } - - } -} - /** * @brief Implements an algorithm for 2d-distributing rectangular objects */ @@ -374,6 +267,8 @@ public: std::sort (indexed_boxes.begin (), indexed_boxes.end (), box_compare (vref)); do_bin (indexed_boxes.begin (), indexed_boxes.end (), vref, vbins); + // rewrite the bins to cell occupation lists + std::vector > > cells; cells.resize (hbins.size ()); @@ -399,31 +294,23 @@ public: } + // initialize the cell widths + std::vector cell_widths, cell_heights; cell_widths.resize (hbins.size (), 0); cell_heights.resize (vbins.size (), 0); + // compute the cell widths as the maximum of the content + for (std::vector > >::const_iterator i = cells.begin (); i != cells.end (); ++i) { for (std::vector >::const_iterator j = i->begin (); j != i->end (); ++j) { coord_type wcell = 0, hcell = 0; - for (std::vector::const_iterator k = j->begin (); k != j->end (); ++k) { - - coord_type w = m_objects [*k].first.width () + hspace; - if (hpitch > 0) { - w = db::coord_traits::rounded (ceil (double (w) / double (hpitch) - 1e-10) * double (hpitch)); - } // NOTE: intra-cell objects are distributed horizontally - wcell += w; - - coord_type h = m_objects [*k].first.height () + vspace; - if (vpitch > 0) { - h = db::coord_traits::rounded (ceil (double (h) / double (vpitch) - 1e-10) * double (vpitch)); - } - hcell = std::max (hcell, h); - + wcell += eff_dim (m_objects [*k].first, hpitch, hspace); + hcell = std::max (hcell, eff_dim (m_objects [*k].first, vpitch, vspace)); } cell_widths [i - cells.begin ()] = std::max (cell_widths [i - cells.begin ()], wcell); @@ -433,6 +320,8 @@ public: } + // Compute the columns and row positions + std::vector cell_xpos, cell_ypos; cell_xpos.reserve (cell_widths.size ()); cell_ypos.reserve (cell_heights.size ()); @@ -447,21 +336,16 @@ public: y += *i; } + // Compute the actual coordinates of the objects inside the cells + for (std::vector > >::const_iterator i = cells.begin (); i != cells.end (); ++i) { for (std::vector >::const_iterator j = i->begin (); j != i->end (); ++j) { coord_type wcell = 0; - for (std::vector::const_iterator k = j->begin (); k != j->end (); ++k) { - - coord_type w = m_objects [*k].first.width () + hspace; - if (hpitch > 0) { - w = db::coord_traits::rounded (ceil (double (w) / double (hpitch) - 1e-10) * double (hpitch)); - } // NOTE: intra-cell objects are distributed horizontally - wcell += w; - + wcell += eff_dim (m_objects [*k].first, hpitch, hspace); } coord_type x = cell_xpos [i - cells.begin ()]; @@ -473,15 +357,8 @@ public: for (std::vector::const_iterator k = j->begin (); k != j->end (); ++k) { - coord_type w = m_objects [*k].first.width () + hspace; - if (hpitch > 0) { - w = db::coord_traits::rounded (ceil (double (w) / double (hpitch) - 1e-10) * double (hpitch)); - } - - coord_type h = m_objects [*k].first.height () + vspace; - if (vpitch > 0) { - h = db::coord_traits::rounded (ceil (double (h) / double (vpitch) - 1e-10) * double (vpitch)); - } + coord_type w = eff_dim (m_objects [*k].first, hpitch, hspace); + coord_type h = eff_dim (m_objects [*k].first, vpitch, vspace); coord_type y = cell_ypos [j - i->begin ()]; if (vref == 0) { From 2714b3410447a02e6353e1c9daabddd83313dbcf Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 8 Aug 2020 14:40:11 +0200 Subject: [PATCH 036/113] One bug fix for distribute feature --- src/edt/edt/edtDistribute.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/edt/edt/edtDistribute.h b/src/edt/edt/edtDistribute.h index 2a2746242..e7aad35eb 100644 --- a/src/edt/edt/edtDistribute.h +++ b/src/edt/edt/edtDistribute.h @@ -421,7 +421,7 @@ private: coord_type pnew = box_position (current, 1) + space; if (db::coord_traits::less (0, pitch)) { - pnew = coord_type (ceil (double (pnew + offset - p0) / double (pitch) - 1e-10)) * pitch - offset; + pnew = coord_type (ceil (double (pnew + offset - p0) / double (pitch) - 1e-10)) * pitch - offset + p0; } db::vector mv; From cdce2cb1510cbe0dbc474e302d3d48707aa54ce6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 8 Aug 2020 21:10:01 +0200 Subject: [PATCH 037/113] WIP: put the editor options into a separate dockable widget --- src/edt/edt/EditorOptionsDialog.ui | 139 ------------- src/edt/edt/EditorOptionsPath.ui | 188 ++++++++++-------- src/edt/edt/edt.pro | 1 - src/edt/edt/edtEditorOptionsPages.cc | 69 ++++--- src/edt/edt/edtEditorOptionsPages.h | 16 +- src/edt/edt/edtMainService.cc | 6 +- src/edt/edt/edtMainService.h | 10 +- src/edt/edt/edtPlugin.cc | 120 +++++------ src/edt/edt/edtPlugin.h | 4 +- src/edt/edt/edtService.cc | 8 +- src/edt/edt/edtServiceImpl.cc | 6 +- src/lay/lay/layMainWindow.cc | 56 ++++++ src/lay/lay/layMainWindow.h | 6 +- src/laybasic/laybasic/gsiDeclLayLayoutView.cc | 6 + src/laybasic/laybasic/layLayoutView.cc | 22 ++ src/laybasic/laybasic/layLayoutView.h | 27 ++- .../laybasic/layLayoutViewFunctions.cc | 2 +- 17 files changed, 318 insertions(+), 368 deletions(-) delete mode 100644 src/edt/edt/EditorOptionsDialog.ui diff --git a/src/edt/edt/EditorOptionsDialog.ui b/src/edt/edt/EditorOptionsDialog.ui deleted file mode 100644 index e34cea275..000000000 --- a/src/edt/edt/EditorOptionsDialog.ui +++ /dev/null @@ -1,139 +0,0 @@ - - EditorOptionsDialog - - - - 0 - 0 - 642 - 572 - - - - Object Editor Options - - - - 9 - - - 6 - - - - - 0 - - - - Tab 2 - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 8 - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 6 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Ok - - - - - - - Apply - - - - - - - Cancel - - - - - - - - - - - - cancel_pb - clicked() - EditorOptionsDialog - reject() - - - 506 - 388 - - - 276 - 205 - - - - - ok_pb - clicked() - EditorOptionsDialog - accept() - - - 344 - 388 - - - 276 - 205 - - - - - diff --git a/src/edt/edt/EditorOptionsPath.ui b/src/edt/edt/EditorOptionsPath.ui index 543354380..ac184b089 100644 --- a/src/edt/edt/EditorOptionsPath.ui +++ b/src/edt/edt/EditorOptionsPath.ui @@ -1,7 +1,8 @@ - + + EditorOptionsPath - - + + 0 0 @@ -9,44 +10,60 @@ 289 - + Form - - - 9 - - + + 6 + + 9 + + + 9 + + + 9 + + + 9 + - - + + QFrame::NoFrame - + QFrame::Raised - - - 0 - - + + 6 + + 0 + + + 0 + + + 0 + + + 0 + - - + + Width - - - - 0 - 0 + + + 0 0 @@ -54,18 +71,18 @@ - - - micron + + + µm - + Qt::Horizontal - + 21 20 @@ -77,31 +94,38 @@ - - - - 5 - 0 + + + 0 0 - + Extensions - - + + 9 - + + 9 + + + 9 + + + 9 + + 6 - + - + Qt::Horizontal - + 21 81 @@ -109,100 +133,94 @@ - - - - micron + + + + µm - - - - micron + + + + µm - - - - - 0 - 0 + + + + 1 0 - - - + + + end = - - - + + + start = - - - - - 0 - 0 + + + + 1 0 - - - + + + Variable - - - - - 5 - 0 + + + + 0 0 - + Flush - + Square - + Variable - + Round - - - + + + Type @@ -212,10 +230,10 @@ - + Qt::Vertical - + 271 63 diff --git a/src/edt/edt/edt.pro b/src/edt/edt/edt.pro index c9f440dca..9a1d11c75 100644 --- a/src/edt/edt/edt.pro +++ b/src/edt/edt/edt.pro @@ -30,7 +30,6 @@ FORMS = \ CopyModeDialog.ui \ ChangeLayerOptionsDialog.ui \ EditablePathPropertiesPage.ui \ - EditorOptionsDialog.ui \ EditorOptionsGeneric.ui \ EditorOptionsInst.ui \ EditorOptionsPath.ui \ diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index bd1940467..3362dd49c 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -25,10 +25,10 @@ #include "dbLibrary.h" #include "dbLibraryManager.h" #include "dbPCellHeader.h" -#include "edtEditorOptionsPages.h" #include "edtPCellParametersPage.h" #include "edtConfig.h" #include "edtService.h" +#include "edtEditorOptionsPages.h" #include "tlExceptions.h" #include "layPlugin.h" #include "layLayoutView.h" @@ -39,6 +39,11 @@ #include "ui_EditorOptionsText.h" #include "ui_EditorOptionsInst.h" +#include +#include +#include +#include + namespace edt { @@ -87,13 +92,27 @@ struct EOPCompareOp } }; -EditorOptionsPages::EditorOptionsPages (const std::vector &pages, lay::Dispatcher *root) - : mp_root (root) +EditorOptionsPages::EditorOptionsPages (QWidget *parent, const std::vector &pages, lay::Dispatcher *root) + : QFrame (parent), mp_root (root) { - mp_ui = new Ui::EditorOptionsDialog (); - mp_ui->setupUi (this); + QVBoxLayout *ly1 = new QVBoxLayout (this); + ly1->setMargin (0); - connect (mp_ui->apply_pb, SIGNAL (clicked ()), this, SLOT (apply ())); + mp_pages = new QTabWidget (this); + ly1->addWidget (mp_pages); + + QFrame *f = new QFrame (this); + ly1->addWidget (f); + + QToolButton *apply_pb = new QToolButton (f); + apply_pb->setText (tr ("Apply")); + + QHBoxLayout *ly2 = new QHBoxLayout (f); + ly2->setMargin (0); + ly2->addStretch (1); + ly2->addWidget (apply_pb); + + connect (apply_pb, SIGNAL (clicked ()), this, SLOT (apply ())); m_pages = pages; for (std::vector ::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) { @@ -107,11 +126,8 @@ EditorOptionsPages::EditorOptionsPages (const std::vector 0) { - delete m_pages [0]; + delete m_pages.front (); } - - delete mp_ui; - mp_ui = 0; } void @@ -131,16 +147,14 @@ void EditorOptionsPages::activate_page (edt::EditorOptionsPage *page) { try { - page->setup (mp_root); + if (page->active ()) { + page->setup (mp_root); + } } catch (...) { // catch any errors related to configuration file errors etc. } - update (page); - if (isVisible ()) { - activateWindow (); - raise (); - } + update (page); } void @@ -148,13 +162,13 @@ EditorOptionsPages::update (edt::EditorOptionsPage *page) { std::sort (m_pages.begin (), m_pages.end (), EOPCompareOp ()); - while (mp_ui->pages->count () > 0) { - mp_ui->pages->removeTab (0); + while (mp_pages->count () > 0) { + mp_pages->removeTab (0); } int index = -1; for (std::vector ::iterator p = m_pages.begin (); p != m_pages.end (); ++p) { if ((*p)->active ()) { - mp_ui->pages->addTab ((*p)->q_frame (), tl::to_qstring ((*p)->title ())); + mp_pages->addTab ((*p)->q_frame (), tl::to_qstring ((*p)->title ())); if ((*p) == page) { index = int (std::distance (m_pages.begin (), p)); } @@ -163,12 +177,12 @@ EditorOptionsPages::update (edt::EditorOptionsPage *page) } } if (index < 0) { - index = mp_ui->pages->currentIndex (); + index = mp_pages->currentIndex (); } - if (index >= int (mp_ui->pages->count ())) { - index = mp_ui->pages->count () - 1; + if (index >= int (mp_pages->count ())) { + index = mp_pages->count () - 1; } - mp_ui->pages->setCurrentIndex (index); + mp_pages->setCurrentIndex (index); } void @@ -209,15 +223,6 @@ BEGIN_PROTECTED END_PROTECTED_W (this) } -void -EditorOptionsPages::accept () -{ -BEGIN_PROTECTED - do_apply (); - QDialog::accept (); -END_PROTECTED_W (this) -} - // ------------------------------------------------------------------ // EditorOptionsGeneric implementation diff --git a/src/edt/edt/edtEditorOptionsPages.h b/src/edt/edt/edtEditorOptionsPages.h index 65145212f..68222ccc8 100644 --- a/src/edt/edt/edtEditorOptionsPages.h +++ b/src/edt/edt/edtEditorOptionsPages.h @@ -26,10 +26,12 @@ #include -#include +#include #include #include +class QTabWidget; + namespace Ui { class EditorOptionsDialog; @@ -87,26 +89,30 @@ private: * @brief The object properties dialog */ class EditorOptionsPages - : public QDialog + : public QFrame { Q_OBJECT public: - EditorOptionsPages (const std::vector &pages, lay::Dispatcher *root); + EditorOptionsPages (QWidget *parent, const std::vector &pages, lay::Dispatcher *root); ~EditorOptionsPages (); void unregister_page (edt::EditorOptionsPage *page); void activate_page (edt::EditorOptionsPage *page); + const std::vector &pages () const + { + return m_pages; + } + public slots: void apply (); void setup (); - void accept (); private: std::vector m_pages; - Ui::EditorOptionsDialog *mp_ui; lay::Dispatcher *mp_root; + QTabWidget *mp_pages; void update (edt::EditorOptionsPage *page); void do_apply (); diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index 71b210ac7..548b1b855 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -150,8 +150,6 @@ MainService::menu_activated (const std::string &symbol) cm_descend (); } else if (symbol == "edt::ascend") { cm_ascend (); - } else if (symbol == "edt::edit_options") { - cm_edit_options (); } else if (symbol == "edt::sel_align") { cm_align (); } else if (symbol == "edt::sel_distribute") { @@ -2272,9 +2270,9 @@ MainService::cm_change_layer () } void -MainService::cm_edit_options () +MainService::show_editor_options () { - show_editor_options_dialog (); + show_editor_options_page (view ()); } void diff --git a/src/edt/edt/edtMainService.h b/src/edt/edt/edtMainService.h index 76a733ee5..5313da97f 100644 --- a/src/edt/edt/edtMainService.h +++ b/src/edt/edt/edtMainService.h @@ -94,11 +94,6 @@ public: */ void cm_ascend (); - /** - * @brief Edit object options - */ - void cm_edit_options (); - /** * @brief Change the layer of the shapes in the selection */ @@ -194,6 +189,11 @@ public: */ void cm_tap (); + /** + * @brief Shows the editor options + */ + void show_editor_options (); + /** * @brief "paste" operation */ diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index e7bd816c3..d9f241d8f 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -31,6 +31,7 @@ #include "edtEditorOptionsPages.h" #include +#include namespace edt { @@ -189,7 +190,7 @@ class MainPluginDeclaration { public: MainPluginDeclaration (const std::string &title) - : mp_root (0), m_title (title), mp_obj_prop_dialog (0) + : mp_root (0), m_title (title) { // .. nothing yet .. } @@ -218,8 +219,6 @@ public: menu_entries.push_back (lay::menu_item ("edt::descend", "descend", "zoom_menu.end", tl::to_string (QObject::tr ("Descend")) + "(Ctrl+D)")); menu_entries.push_back (lay::menu_item ("edt::ascend", "ascend", "zoom_menu.end", tl::to_string (QObject::tr ("Ascend")) + "(Ctrl+A)")); - menu_entries.push_back (lay::separator ("edit_options_group:edit_mode", "edit_menu.end")); - menu_entries.push_back (lay::menu_item ("edt::edit_options", "edit_options:edit_mode", "edit_menu.end", tl::to_string (QObject::tr ("Editor Options")) + "(F3)")); menu_entries.push_back (lay::menu_item ("edt::sel_make_array", "make_array:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Make Array")))); menu_entries.push_back (lay::separator ("selection_group:edit_mode", "edit_menu.selection_menu.end")); menu_entries.push_back (lay::menu_item ("edt::sel_change_layer", "change_layer:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Change Layer")))); @@ -279,24 +278,6 @@ public: mp_root = root; - // create the editor options dialog - m_prop_dialog_pages.push_back (new edt::EditorOptionsGeneric ()); - - for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { - const PluginDeclarationBase *pd_base = dynamic_cast (&*cls); - if (pd_base) { - pd_base->get_editor_options_pages (m_prop_dialog_pages, root); - } - } - - mp_obj_prop_dialog = new edt::EditorOptionsPages (m_prop_dialog_pages, root); - - for (std::vector::const_iterator op = m_prop_dialog_pages.begin (); op != m_prop_dialog_pages.end (); ++op) { - if ((*op)->plugin_declaration () != 0) { - (*op)->activate (false); - } - } - // add entries to the combine mode dialog mp->menu ()->insert_item ("@toolbar.combine_mode.end", "combine_mode_add", new lay::ConfigureAction (tl::to_string (QObject::tr ("Add<:/cm_add.png>{Add shapes}")), cfg_edit_combine_mode, CMConverter ().to_string (CM_Add))); mp->menu ()->insert_item ("@toolbar.combine_mode.end", "combine_mode_merge", new lay::ConfigureAction (tl::to_string (QObject::tr ("Merge<:/cm_merge.png>{Merge shapes with background}")), cfg_edit_combine_mode, CMConverter ().to_string (CM_Merge))); @@ -334,42 +315,6 @@ public: } } - virtual void uninitialize (lay::Dispatcher *) - { - if (mp_obj_prop_dialog) { - delete mp_obj_prop_dialog; - mp_obj_prop_dialog = 0; - } - } - - virtual void config_finalize () - { - if (mp_obj_prop_dialog && mp_obj_prop_dialog->isVisible ()) { - mp_obj_prop_dialog->setup (); - } - } - - void show_dialog () const - { - if (mp_obj_prop_dialog) { - if (! mp_obj_prop_dialog->isVisible ()) { - mp_obj_prop_dialog->setup (); - mp_obj_prop_dialog->show (); - } - mp_obj_prop_dialog->activateWindow (); - mp_obj_prop_dialog->raise (); - } - } - - void activate (const lay::PluginDeclaration *pd, bool active) const - { - for (std::vector::const_iterator op = m_prop_dialog_pages.begin (); op != m_prop_dialog_pages.end (); ++op) { - if ((*op)->plugin_declaration () == pd) { - (*op)->activate (active); - } - } - } - void initialized (lay::Dispatcher *root) { lay::Dispatcher *mp = lay::Dispatcher::instance (); @@ -398,38 +343,63 @@ public: private: lay::Dispatcher *mp_root; std::string m_title; - edt::EditorOptionsPages *mp_obj_prop_dialog; - std::vector m_prop_dialog_pages; }; +static tl::RegisteredClass config_decl_main (new edt::MainPluginDeclaration (tl::to_string (QObject::tr ("Instances and shapes"))), 4000, "edt::MainService"); + void -show_editor_options_dialog () +show_editor_options_page (lay::LayoutView *view) { - // look for the plugin declaration and show the dialog + if (! view->editor_options_frame ()) { + return; + } + + std::vector prop_dialog_pages; + EditorOptionsGeneric *generic_opt = new EditorOptionsGeneric (); + generic_opt->setup (view->dispatcher ()); + prop_dialog_pages.push_back (generic_opt); + for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { - const MainPluginDeclaration *main_pd = dynamic_cast (&*cls); - if (main_pd) { - main_pd->show_dialog (); - break; + const PluginDeclarationBase *pd_base = dynamic_cast (&*cls); + if (pd_base) { + pd_base->get_editor_options_pages (prop_dialog_pages, view->dispatcher ()); } } + + for (std::vector::const_iterator op = prop_dialog_pages.begin (); op != prop_dialog_pages.end (); ++op) { + (*op)->activate (false); + } + + QObjectList children = view->editor_options_frame ()->children (); + for (QObjectList::iterator c = children.begin (); c != children.end (); ++c) { + if (dynamic_cast (*c)) { + delete *c; + } + } + + edt::EditorOptionsPages *pages = new edt::EditorOptionsPages (view->editor_options_frame (), prop_dialog_pages, view->dispatcher ()); + view->editor_options_frame ()->layout ()->addWidget (pages); } void -activate_service (const lay::PluginDeclaration *pd, bool active) +activate_service (lay::LayoutView *view, const lay::PluginDeclaration *pd, bool active) { - // look for the plugin declaration and show the dialog - for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { - const MainPluginDeclaration *main_pd = dynamic_cast (&*cls); - if (main_pd) { - main_pd->activate (pd, active); - break; - } + // TODO: is there a better way to find the editor options pages? + edt::EditorOptionsPages *eo_pages = 0; + QObjectList children = view->editor_options_frame ()->children (); + for (QObjectList::iterator c = children.begin (); c != children.end () && !eo_pages; ++c) { + eo_pages = dynamic_cast (*c); + } + + if (!eo_pages) { + return; + } + + for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { + (*op)->activate (((*op)->plugin_declaration () == pd || ! (*op)->plugin_declaration ()) && active); } } -static tl::RegisteredClass config_decl20 (new edt::MainPluginDeclaration (tl::to_string (QObject::tr ("Instances and shapes"))), 4000, "edt::MainService"); - class PartialPluginDeclaration : public lay::PluginDeclaration { diff --git a/src/edt/edt/edtPlugin.h b/src/edt/edt/edtPlugin.h index f75dec8dd..86898c714 100644 --- a/src/edt/edt/edtPlugin.h +++ b/src/edt/edt/edtPlugin.h @@ -52,14 +52,14 @@ namespace edt * * This dialog is a global resource which is managed by the main plugin declaration */ - void show_editor_options_dialog (); + void show_editor_options_page (lay::LayoutView *view); /** * @brief Activate or deactivate a certain service * * This will show or hide the editor properties pages for the respective service. */ - void activate_service (const lay::PluginDeclaration *pd, bool active); + void activate_service (lay::LayoutView *view, const lay::PluginDeclaration *pd, bool active); } #endif diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index f8dd7ee84..493387153 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -795,22 +795,22 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio void Service::activated () { - // make all editor option pages visible - activate_service (plugin_declaration (), true); - if (view ()->is_editable ()) { view ()->cancel (); // cancel any pending edit operations and clear the selection set_edit_marker (0); m_immediate = do_activated (); m_editing = false; } + + // make all editor option pages visible + activate_service (view (), plugin_declaration (), true); } void Service::deactivated () { // make all editor option pages visible - activate_service (plugin_declaration (), false); + activate_service (view (), plugin_declaration (), false); edit_cancel (); diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index 8e73c2775..e4a15b635 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -752,7 +752,7 @@ TextService::do_activated () // Show editor options dialog to allow entering of width std::vector edt_main_services = view ()->get_plugins (); if (edt_main_services.size () > 0) { - edt_main_services [0]->cm_edit_options (); + edt_main_services [0]->show_editor_options (); } return true; // start editing immediately @@ -918,7 +918,7 @@ PathService::do_activated () // Show editor options dialog to allow entering of width std::vector edt_main_services = view ()->get_plugins (); if (edt_main_services.size () > 0) { - edt_main_services [0]->cm_edit_options (); + edt_main_services [0]->show_editor_options (); } return false; // don't start editing immediately @@ -1123,7 +1123,7 @@ InstService::do_activated () // Show editor options dialog to allow entering of parameters std::vector edt_main_services = view ()->get_plugins (); if (edt_main_services.size () > 0) { - edt_main_services [0]->cm_edit_options (); + edt_main_services [0]->show_editor_options (); } m_cv_index = view ()->active_cellview_index (); diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 2e8f23f11..26eefba7a 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -500,6 +500,14 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha connect (mp_libs_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); m_libs_visible = true; + mp_eo_dock_widget = new QDockWidget (QObject::tr ("Editor Options"), this); + mp_eo_dock_widget->hide (); + mp_eo_dock_widget->setObjectName (QString::fromUtf8 ("eo_dock_widget")); + mp_eo_stack = new ControlWidgetStack (mp_eo_dock_widget, "eo_stack"); + mp_eo_dock_widget->setWidget (mp_eo_stack); + connect (mp_eo_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); + m_eo_visible = false; + mp_bm_dock_widget = new QDockWidget (QObject::tr ("Bookmarks"), this); mp_bm_dock_widget->setObjectName (QString::fromUtf8 ("bookmarks_dock_widget")); mp_bm_stack = new ControlWidgetStack (mp_bm_dock_widget, "bookmarks_stack"); @@ -539,6 +547,7 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha addDockWidget(Qt::LeftDockWidgetArea, mp_navigator_dock_widget); addDockWidget(Qt::LeftDockWidgetArea, mp_hp_dock_widget); addDockWidget(Qt::LeftDockWidgetArea, mp_libs_dock_widget); + addDockWidget(Qt::LeftDockWidgetArea, mp_eo_dock_widget); addDockWidget(Qt::RightDockWidgetArea, mp_bm_dock_widget); addDockWidget(Qt::RightDockWidgetArea, mp_lp_dock_widget); addDockWidget(Qt::RightDockWidgetArea, mp_layer_toolbox_dock_widget); @@ -925,6 +934,7 @@ MainWindow::close_all () mp_lp_stack->removeWidget (mp_views.size ()); mp_hp_stack->removeWidget (mp_views.size ()); mp_libs_stack->removeWidget (mp_views.size ()); + mp_eo_stack->removeWidget (mp_views.size ()); mp_bm_stack->removeWidget (mp_views.size ()); mp_view_stack->removeWidget (mp_views.size ()); @@ -1518,6 +1528,12 @@ MainWindow::update_dock_widget_state () mp_libs_dock_widget->hide (); } + if (m_eo_visible) { + mp_eo_dock_widget->show (); + } else { + mp_eo_dock_widget->hide (); + } + if (m_bm_visible) { mp_bm_dock_widget->show (); } else { @@ -1856,6 +1872,30 @@ MainWindow::select_mode (int m) } } + // if the current mode supports editing, show the editor options panel + + const lay::PluginDeclaration *pd_sel = 0; + for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { + const lay::PluginDeclaration *pd = cls.operator-> (); + if (pd->id () == m_mode) { + pd_sel = pd; + } + } + + bool editable = false; + if (pd_sel) { + editable = pd_sel->editable_enabled (); + } + + if (editable != m_eo_visible) { + m_eo_visible = editable; + if (m_eo_visible) { + mp_eo_dock_widget->show (); + } else { + mp_eo_dock_widget->hide (); + } + } + } } @@ -2526,6 +2566,7 @@ MainWindow::select_view (int index) mp_hp_stack->raiseWidget (index); mp_lp_stack->raiseWidget (index); mp_libs_stack->raiseWidget (index); + mp_eo_stack->raiseWidget (index); mp_bm_stack->raiseWidget (index); mp_setup_form->setup (); @@ -2717,6 +2758,7 @@ MainWindow::clone_current_view () mp_lp_stack->addWidget (view->layer_control_frame ()); mp_hp_stack->addWidget (view->hierarchy_control_frame ()); mp_libs_stack->addWidget (view->libraries_frame ()); + mp_eo_stack->addWidget (view->editor_options_frame ()); mp_bm_stack->addWidget (view->bookmarks_frame ()); bool f = m_disable_tab_selected; @@ -2965,6 +3007,7 @@ MainWindow::close_view (int index) mp_lp_stack->removeWidget (index); mp_hp_stack->removeWidget (index); mp_libs_stack->removeWidget (index); + mp_eo_stack->removeWidget (index); mp_bm_stack->removeWidget (index); view_closed_event (int (index)); @@ -3286,6 +3329,7 @@ MainWindow::create_view () mp_lp_stack->addWidget (mp_views.back ()->layer_control_frame ()); mp_hp_stack->addWidget (mp_views.back ()->hierarchy_control_frame ()); mp_libs_stack->addWidget (mp_views.back ()->libraries_frame ()); + mp_eo_stack->addWidget (mp_views.back ()->editor_options_frame ()); mp_bm_stack->addWidget (mp_views.back ()->bookmarks_frame ()); bool f = m_disable_tab_selected; @@ -3349,6 +3393,7 @@ MainWindow::create_or_load_layout (const std::string *filename, const db::LoadLa mp_lp_stack->addWidget (mp_views.back ()->layer_control_frame ()); mp_hp_stack->addWidget (mp_views.back ()->hierarchy_control_frame ()); mp_libs_stack->addWidget (mp_views.back ()->libraries_frame ()); + mp_eo_stack->addWidget (mp_views.back ()->editor_options_frame ()); mp_bm_stack->addWidget (mp_views.back ()->bookmarks_frame ()); bool f = m_disable_tab_selected; @@ -3824,6 +3869,13 @@ MainWindow::menu_activated (const std::string &symbol) cm_help_about (); } else if (symbol == "cm_help_about_qt") { cm_help_about_qt (); + } else if (symbol == "cm_edit_options") { + + if (!m_eo_visible) { + mp_eo_dock_widget->show (); + m_eo_visible = true; + } + } else { // Try the plugin declarations @@ -4189,6 +4241,10 @@ public: menu_entries.push_back (lay::submenu ("@secrets", at, tl::to_string (QObject::tr ("Secret Features")))); menu_entries.push_back (lay::submenu ("@toolbar", at, std::string ())); + at = "edit_menu.end"; + menu_entries.push_back (lay::separator ("edit_options_group:edit_mode", "edit_menu.end")); + menu_entries.push_back (lay::menu_item ("cm_edit_options", "edit_options:edit_mode", "edit_menu.end", tl::to_string (QObject::tr ("Editor Options")) + "(F3)")); + at = "file_menu.end"; menu_entries.push_back (lay::menu_item ("cm_new_layout", "new_layout:edit:edit_mode", at, tl::to_string (QObject::tr ("New Layout")))); menu_entries.push_back (lay::menu_item ("cm_new_panel", "new_panel:edit:edit_mode", at, tl::to_string (QObject::tr ("New Panel")))); diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index 6ffa374e2..eb24021d5 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -680,9 +680,9 @@ private: QToolBar *mp_tool_bar; QDockWidget *mp_navigator_dock_widget; lay::Navigator *mp_navigator; - QDockWidget *mp_hp_dock_widget, *mp_lp_dock_widget, *mp_libs_dock_widget, *mp_bm_dock_widget; - ControlWidgetStack *mp_hp_stack, *mp_lp_stack, *mp_libs_stack, *mp_bm_stack; - bool m_hp_visible, m_lp_visible, m_libs_visible, m_bm_visible, m_navigator_visible, m_layer_toolbox_visible; + QDockWidget *mp_hp_dock_widget, *mp_lp_dock_widget, *mp_libs_dock_widget, *mp_eo_dock_widget, *mp_bm_dock_widget; + ControlWidgetStack *mp_hp_stack, *mp_lp_stack, *mp_libs_stack, *mp_eo_stack, *mp_bm_stack; + bool m_hp_visible, m_lp_visible, m_libs_visible, m_eo_visible, m_bm_visible, m_navigator_visible, m_layer_toolbox_visible; QDockWidget *mp_layer_toolbox_dock_widget; lay::LayerToolbox *mp_layer_toolbox; ViewWidgetStack *mp_view_stack; diff --git a/src/laybasic/laybasic/gsiDeclLayLayoutView.cc b/src/laybasic/laybasic/gsiDeclLayLayoutView.cc index eae19ef72..a2f1d7cb7 100644 --- a/src/laybasic/laybasic/gsiDeclLayLayoutView.cc +++ b/src/laybasic/laybasic/gsiDeclLayLayoutView.cc @@ -467,6 +467,12 @@ Class decl_LayoutView (QT_EXTERNAL_BASE (QWidget) "lay", "Layou "\n" "This constant has been introduced in version 0.27.\n" ) + + gsi::constant ("LV_NoEditorOptionsPanel", (unsigned int) lay::LayoutView::LV_NoEditorOptionsPanel, + "@brief With this option, no editor options panel will be provided (see \\editor_options_frame)\n" + "Use this value with the constructor's 'options' argument.\n" + "\n" + "This constant has been introduced in version 0.27.\n" + ) + gsi::constant ("LV_NoBookmarksView", (unsigned int) lay::LayoutView::LV_NoBookmarksView, "@brief With this option, no bookmarks view will be provided (see \\bookmarks_frame)\n" "Use this value with the constructor's 'options' argument.\n" diff --git a/src/laybasic/laybasic/layLayoutView.cc b/src/laybasic/laybasic/layLayoutView.cc index 62a8566a3..9302623f3 100644 --- a/src/laybasic/laybasic/layLayoutView.cc +++ b/src/laybasic/laybasic/layLayoutView.cc @@ -390,6 +390,7 @@ LayoutView::init (db::Manager *mgr, QWidget * /*parent*/) mp_libraries_view = 0; mp_bookmarks_view = 0; mp_libraries_frame = 0; + mp_editor_options_frame = 0; mp_bookmarks_frame = 0; mp_min_hier_spbx = 0; mp_max_hier_spbx = 0; @@ -557,6 +558,20 @@ LayoutView::init (db::Manager *mgr, QWidget * /*parent*/) } + if (is_editable () && (m_options & LV_NoEditorOptionsPanel) == 0 && (m_options & LV_Naked) == 0) { + + QFrame *editor_options_frame = new QFrame (0); + editor_options_frame->setObjectName (QString::fromUtf8 ("editor_options_frame")); + mp_editor_options_frame = editor_options_frame; + + QVBoxLayout *left_frame_ly = new QVBoxLayout (editor_options_frame); + left_frame_ly->setMargin (0); + left_frame_ly->setSpacing (0); + + connect (mp_editor_options_frame, SIGNAL (destroyed ()), this, SLOT (side_panel_destroyed ())); + + } + // occupy services and editables: // these services get deleted by the canvas destructor automatically: if ((m_options & LV_NoTracker) == 0) { @@ -686,6 +701,11 @@ LayoutView::~LayoutView () mp_libraries_frame = 0; mp_libraries_view = 0; + if (mp_editor_options_frame) { + delete mp_editor_options_frame; + } + mp_editor_options_frame = 0; + if (mp_bookmarks_frame) { delete mp_bookmarks_frame; } @@ -709,6 +729,8 @@ void LayoutView::side_panel_destroyed () } else if (sender () == mp_libraries_frame) { mp_libraries_frame = 0; mp_libraries_view = 0; + } else if (sender () == mp_editor_options_frame) { + mp_editor_options_frame = 0; } else if (sender () == mp_bookmarks_frame) { mp_bookmarks_frame = 0; mp_bookmarks_view = 0; diff --git a/src/laybasic/laybasic/layLayoutView.h b/src/laybasic/laybasic/layLayoutView.h index 0081684b4..46938886a 100644 --- a/src/laybasic/laybasic/layLayoutView.h +++ b/src/laybasic/laybasic/layLayoutView.h @@ -175,14 +175,15 @@ public: LV_NoLayers = 1, LV_NoHierarchyPanel = 2, LV_NoLibrariesView = 4, - LV_NoBookmarksView = 8, - LV_Naked = 16, - LV_NoZoom = 32, - LV_NoGrid = 64, - LV_NoMove = 128, - LV_NoTracker = 256, - LV_NoSelection = 512, - LV_NoPlugins = 1024, + LV_NoEditorOptionsPanel = 8, + LV_NoBookmarksView = 16, + LV_Naked = 32, + LV_NoZoom = 64, + LV_NoGrid = 128, + LV_NoMove = 256, + LV_NoTracker = 512, + LV_NoSelection = 1024, + LV_NoPlugins = 2048, LV_NoServices = LV_NoMove + LV_NoTracker + LV_NoSelection + LV_NoPlugins }; @@ -268,6 +269,14 @@ public: return mp_libraries_frame; } + /** + * @brief Gets the container with the editor options + */ + QWidget *editor_options_frame () + { + return mp_editor_options_frame; + } + /** * @brief Gets the container with the bookmarks view */ @@ -2708,7 +2717,7 @@ private: lay::HierarchyControlPanel *mp_hierarchy_panel; lay::LibrariesView *mp_libraries_view; lay::BookmarksView *mp_bookmarks_view; - QWidget *mp_control_frame, *mp_hierarchy_frame, *mp_libraries_frame, *mp_bookmarks_frame; + QWidget *mp_control_frame, *mp_hierarchy_frame, *mp_libraries_frame, *mp_editor_options_frame, *mp_bookmarks_frame; QSpinBox *mp_min_hier_spbx; QSpinBox *mp_max_hier_spbx; std::list m_cellviews; diff --git a/src/laybasic/laybasic/layLayoutViewFunctions.cc b/src/laybasic/laybasic/layLayoutViewFunctions.cc index c7198ac11..89e963c06 100644 --- a/src/laybasic/laybasic/layLayoutViewFunctions.cc +++ b/src/laybasic/laybasic/layLayoutViewFunctions.cc @@ -2065,7 +2065,7 @@ public: menu_entries.push_back (lay::menu_item ("cm_select_next_item", "select_next_item:edit", at, tl::to_string (QObject::tr ("Select Next Item(Space)")))); menu_entries.push_back (lay::menu_item ("cm_select_next_item_add", "select_next_item_add:edit", at, tl::to_string (QObject::tr ("Select Next Item too(Shift+Space)")))); - at = "edit_menu.end"; + at = "edit_menu.edit_options_group"; menu_entries.push_back (lay::menu_item ("cm_undo", "undo:edit", at, tl::to_string (QObject::tr ("Undo(Ctrl+Z)")))); menu_entries.push_back (lay::menu_item ("cm_redo", "redo:edit", at, tl::to_string (QObject::tr ("Redo(Ctrl+Y)")))); From 4254221e6ec6b07cb258ffb22561a348fea68ddf Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 8 Aug 2020 21:32:21 +0200 Subject: [PATCH 038/113] WIP: some refinement --- src/edt/edt/edtEditorOptionsPages.cc | 11 ++++++----- src/edt/edt/edtEditorOptionsPages.h | 2 +- src/edt/edt/edtMainService.cc | 6 ++++++ src/edt/edt/edtMainService.h | 6 ++++++ src/edt/edt/edtPlugin.cc | 26 +++++++++++++++++++++++--- src/edt/edt/edtPlugin.h | 5 +++++ 6 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index 3362dd49c..4b9c0cf86 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -92,8 +92,8 @@ struct EOPCompareOp } }; -EditorOptionsPages::EditorOptionsPages (QWidget *parent, const std::vector &pages, lay::Dispatcher *root) - : QFrame (parent), mp_root (root) +EditorOptionsPages::EditorOptionsPages (QWidget *parent, const std::vector &pages, lay::Dispatcher *dispatcher) + : QFrame (parent), mp_dispatcher (dispatcher) { QVBoxLayout *ly1 = new QVBoxLayout (this); ly1->setMargin (0); @@ -148,7 +148,7 @@ EditorOptionsPages::activate_page (edt::EditorOptionsPage *page) { try { if (page->active ()) { - page->setup (mp_root); + page->setup (mp_dispatcher); } } catch (...) { // catch any errors related to configuration file errors etc. @@ -192,7 +192,7 @@ EditorOptionsPages::setup () for (std::vector ::iterator p = m_pages.begin (); p != m_pages.end (); ++p) { if ((*p)->active ()) { - (*p)->setup (mp_root); + (*p)->setup (mp_dispatcher); } } @@ -210,7 +210,8 @@ EditorOptionsPages::do_apply () { for (std::vector ::iterator p = m_pages.begin (); p != m_pages.end (); ++p) { if ((*p)->active ()) { - (*p)->apply (mp_root); + // NOTE: we apply to the root dispatcher, so other dispatchers (views) get informed too. + (*p)->apply (mp_dispatcher->dispatcher ()); } } } diff --git a/src/edt/edt/edtEditorOptionsPages.h b/src/edt/edt/edtEditorOptionsPages.h index 68222ccc8..8b7f34ebc 100644 --- a/src/edt/edt/edtEditorOptionsPages.h +++ b/src/edt/edt/edtEditorOptionsPages.h @@ -111,7 +111,7 @@ public slots: private: std::vector m_pages; - lay::Dispatcher *mp_root; + lay::Dispatcher *mp_dispatcher; QTabWidget *mp_pages; void update (edt::EditorOptionsPage *page); diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index 548b1b855..9e2673d21 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -2362,6 +2362,12 @@ private: db::cell_index_type m_topcell; }; +void +MainService::config_finalize () +{ + setup_pages (view ()); +} + void MainService::paste () { diff --git a/src/edt/edt/edtMainService.h b/src/edt/edt/edtMainService.h index 5313da97f..34bdf512d 100644 --- a/src/edt/edt/edtMainService.h +++ b/src/edt/edt/edtMainService.h @@ -199,6 +199,12 @@ public: */ virtual void paste (); + /** + * @brief Implementation of "config_finalize" + * This implementation will update the editor option page's parameters. + */ + virtual void config_finalize (); + private: // The layout view that this service is attached to lay::LayoutView *mp_view; diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index d9f241d8f..4e0ca160a 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -377,12 +377,12 @@ show_editor_options_page (lay::LayoutView *view) } } - edt::EditorOptionsPages *pages = new edt::EditorOptionsPages (view->editor_options_frame (), prop_dialog_pages, view->dispatcher ()); + edt::EditorOptionsPages *pages = new edt::EditorOptionsPages (view->editor_options_frame (), prop_dialog_pages, view); view->editor_options_frame ()->layout ()->addWidget (pages); } -void -activate_service (lay::LayoutView *view, const lay::PluginDeclaration *pd, bool active) +static +edt::EditorOptionsPages *get_pages_widget (lay::LayoutView *view) { // TODO: is there a better way to find the editor options pages? edt::EditorOptionsPages *eo_pages = 0; @@ -391,6 +391,13 @@ activate_service (lay::LayoutView *view, const lay::PluginDeclaration *pd, bool eo_pages = dynamic_cast (*c); } + return eo_pages; +} + +void +activate_service (lay::LayoutView *view, const lay::PluginDeclaration *pd, bool active) +{ + edt::EditorOptionsPages *eo_pages = get_pages_widget (view); if (!eo_pages) { return; } @@ -400,6 +407,19 @@ activate_service (lay::LayoutView *view, const lay::PluginDeclaration *pd, bool } } +void +setup_pages (lay::LayoutView *view) +{ + edt::EditorOptionsPages *eo_pages = get_pages_widget (view); + if (!eo_pages) { + return; + } + + for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { + (*op)->setup (view); + } +} + class PartialPluginDeclaration : public lay::PluginDeclaration { diff --git a/src/edt/edt/edtPlugin.h b/src/edt/edt/edtPlugin.h index 86898c714..fe0c8a2c0 100644 --- a/src/edt/edt/edtPlugin.h +++ b/src/edt/edt/edtPlugin.h @@ -60,6 +60,11 @@ namespace edt * This will show or hide the editor properties pages for the respective service. */ void activate_service (lay::LayoutView *view, const lay::PluginDeclaration *pd, bool active); + + /** + * @brief Setup the editor option pages for the given view + */ + void setup_pages (lay::LayoutView *view); } #endif From 452aafe8ae93e5b102a8540723bae6c86921660c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 8 Aug 2020 23:04:44 +0200 Subject: [PATCH 039/113] WIP: some refinement --- src/edt/edt/EditorOptionsGeneric.ui | 794 +++++++++++---------- src/edt/edt/EditorOptionsInst.ui | 444 +++++++----- src/edt/edt/EditorOptionsInstPCellParam.ui | 39 + src/edt/edt/edt.pro | 3 +- src/edt/edt/edtEditorOptionsPages.cc | 310 +++++--- src/edt/edt/edtEditorOptionsPages.h | 36 +- src/edt/edt/edtPlugin.cc | 3 + 7 files changed, 961 insertions(+), 668 deletions(-) create mode 100644 src/edt/edt/EditorOptionsInstPCellParam.ui diff --git a/src/edt/edt/EditorOptionsGeneric.ui b/src/edt/edt/EditorOptionsGeneric.ui index 36c1aadd0..66b4ca306 100644 --- a/src/edt/edt/EditorOptionsGeneric.ui +++ b/src/edt/edt/EditorOptionsGeneric.ui @@ -6,10 +6,16 @@ 0 0 - 500 - 417 + 400 + 446 + + + 0 + 0 + + Form @@ -18,390 +24,440 @@ 6 - 9 + 0 - 9 + 0 - 9 + 0 - 9 + 0 - - - Snapping + + + QFrame::NoFrame - - - 9 + + QFrame::Plain + + + true + + + + + 0 + 0 + 400 + 446 + - - 9 - - - 9 - - - 9 - - - 6 - - - - - Qt::Horizontal - - - - 148 - 16 - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 16 - 20 - - - - - - - - - 0 - 0 - - - - Enter the grid in micron. Can be anisotropic ("gx,gy") - - - - - - - Grid - - - - - - - Objects - - - - - - - - No grid + + + 2 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + Snapping - - - - Global grid + + + 4 + + + 4 + + + 4 + + + 4 + + + 6 + + + 2 + + + + + Grid + + + + + + + + 1 + 0 + + + + QComboBox::AdjustToContents + + + + No grid + + + + + Global grid + + + + + Other grid ... + + + + + + + + Snap to other objects + + + + + + + Objects + + + + + + + Qt::Horizontal + + + + 148 + 16 + + + + + + + + + 0 + 0 + + + + Enter the grid in micron. Can be anisotropic ("gx,gy") + + + + + + + + + + Angle Constraints - - - - Other grid ... + + + 4 + + + 4 + + + 4 + + + 4 + + + 6 + + + 2 + + + + + Movements + + + + + + + Connections + + + + + + + + 1 + 0 + + + + QComboBox::AdjustToContents + + + + Any Angle + + + + + Diagonal + + + + + Manhattan + + + + + + + + + 1 + 0 + + + + QComboBox::AdjustToContents + + + + Any Direction + + + + + Diagonal + + + + + Manhattan + + + + + + + + Qt::Horizontal + + + + 257 + 10 + + + + + + + + + + + Hierarchical Features - - - - - - - Snap to other objects - - - - + + + 4 + + + 4 + + + 4 + + + 4 + + + 6 + + + 2 + + + + + Copy mode + + + + + + + Shallow select + + + + + + + + 1 + 0 + + + + QComboBox::AdjustToContents + + + + Shallow mode (instance only) + + + + + Deep mode (instance and cell) + + + + + Ask + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Select top level objects only + + + + + + + + + + Instance Display + + + + 2 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + Show shapes while moving (max. + + + + + + + + 0 + 0 + + + + + + + + shapes) + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + + + + Qt::Vertical + + + + 121 + 70 + + + + + + - - - - Angle Constraints - - - - 9 - - - 9 - - - 9 - - - 9 - - - 6 - - - - - Movements - - - - - - - Connections - - - - - - - - Any Angle - - - - - Diagonal - - - - - Manhattan - - - - - - - - - Any Direction - - - - - Diagonal - - - - - Manhattan - - - - - - - - Qt::Horizontal - - - - 257 - 41 - - - - - - - - - - - Hierarchical Features - - - - 9 - - - 9 - - - 9 - - - 9 - - - 6 - - - - - Copy mode - - - - - - - Shallow select - - - - - - - - 0 - 0 - - - - QComboBox::AdjustToContentsOnFirstShow - - - - Shallow mode (instance only) - - - - - Deep mode (instance and cell) - - - - - Ask - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - Select top level objects only - - - - - - - - - - Instance Display - - - - 6 - - - 9 - - - 9 - - - 9 - - - 9 - - - - - Show shapes when moving (max. - - - - - - - - 0 - 0 - - - - - - - - shapes) - - - - - - - Qt::Horizontal - - - - 20 - 20 - - - - - - - - - - - Qt::Vertical - - - - 121 - 51 - - - - - - grid_cb - edit_grid_le - snap_objects_cbx - conn_angle_cb - move_angle_cb - hier_sel_cbx - show_shapes_cbx - max_shapes_le - diff --git a/src/edt/edt/EditorOptionsInst.ui b/src/edt/edt/EditorOptionsInst.ui index 3cd38120a..d7529a797 100644 --- a/src/edt/edt/EditorOptionsInst.ui +++ b/src/edt/edt/EditorOptionsInst.ui @@ -6,10 +6,16 @@ 0 0 - 680 - 574 + 358 + 496 + + + 0 + 0 + + Form @@ -18,163 +24,215 @@ 6 - 9 + 0 - 9 + 0 - 9 + 0 - 9 + 0 - + QFrame::NoFrame - QFrame::Raised + QFrame::Plain - - - 6 + + true + + + + + 0 + 0 + 358 + 496 + - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Cell - - - - - - - - 0 - 0 - - - - - - - - ... - - - - - - - Library - - - - - - - - 0 - 0 - - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 522 - 8 - - - - - - - - - 0 - 1 - - - - 0 - - - - Geometry - - - - 6 - + - 9 + 4 - 9 + 4 - 9 + 4 - 9 + 4 - + + 2 + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 6 + + + 2 + + + + + Library + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Cell + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + ... + + + + :/find.png:/find.png + + + true + + + + + + + + 0 + 0 + + + + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 4 + + + + + + + + Place origin of cell + + + + Rotation / Scaling - + - 9 + 4 - 9 + 4 - 9 + 4 - 9 + 4 - + 6 + + 2 + - + 0 0 @@ -194,14 +252,14 @@ - Scaling factor (magnification) + Scaling factor - + 0 0 @@ -229,10 +287,17 @@ + + + + (magnification) + + + - + @@ -246,33 +311,36 @@ true - + - 9 + 4 - 9 + 4 - 9 + 4 - 9 + 4 - + 6 + + 2 + - Column vector (x,y) + Column step - + 1 0 @@ -292,7 +360,7 @@ - + 1 0 @@ -312,7 +380,7 @@ - + 1 0 @@ -332,7 +400,7 @@ - + 1 0 @@ -352,14 +420,14 @@ - Row vector (x,y) + Row step - Rows/Columns + Dimension @@ -368,12 +436,15 @@ columns = + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + - + 1 0 @@ -385,74 +456,89 @@ rows = + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + - + 1 0 + + + 0 + 0 + + - + - Warning: although row and column vectors can be arbitrary combination, -some design systems only accept orthogonal (rectangular) arrays. + Warning: although row and column vectors can be arbitrary combination, some design systems only accept orthogonal (rectangular) arrays. true + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 4 + + + + - - + + Qt::Vertical 20 - 40 + 120 + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 4 - - - PCell - - - - - - Place origin of cell - - - - - - - Qt::Vertical - - - - 50 - 8 - - - - @@ -462,22 +548,8 @@ some design systems only accept orthogonal (rectangular) arrays.
layWidgets.h
- - cell_le - browse_pb - lib_cbx - param_tab_widget - scale_le - angle_le - mirror_cbx - rows_le - columns_le - row_x_le - row_y_le - column_x_le - column_y_le - place_origin_cb - - + + + diff --git a/src/edt/edt/EditorOptionsInstPCellParam.ui b/src/edt/edt/EditorOptionsInstPCellParam.ui new file mode 100644 index 000000000..2e1e0178e --- /dev/null +++ b/src/edt/edt/EditorOptionsInstPCellParam.ui @@ -0,0 +1,39 @@ + + + EditorOptionsInstPCellParam + + + + 0 + 0 + 358 + 481 + + + + + 0 + 0 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + diff --git a/src/edt/edt/edt.pro b/src/edt/edt/edt.pro index 9a1d11c75..d78982deb 100644 --- a/src/edt/edt/edt.pro +++ b/src/edt/edt/edt.pro @@ -43,7 +43,8 @@ FORMS = \ RoundCornerOptionsDialog.ui \ TextPropertiesPage.ui \ PCellParametersDialog.ui \ - DistributeOptionsDialog.ui + DistributeOptionsDialog.ui \ + EditorOptionsInstPCellParam.ui SOURCES = \ edtConfig.cc \ diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index 4b9c0cf86..f050b9ca4 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -38,6 +38,7 @@ #include "ui_EditorOptionsPath.h" #include "ui_EditorOptionsText.h" #include "ui_EditorOptionsInst.h" +#include "ui_EditorOptionsInstPCellParam.h" #include #include @@ -160,18 +161,23 @@ EditorOptionsPages::activate_page (edt::EditorOptionsPage *page) void EditorOptionsPages::update (edt::EditorOptionsPage *page) { - std::sort (m_pages.begin (), m_pages.end (), EOPCompareOp ()); + std::vector sorted_pages = m_pages; + std::sort (sorted_pages.begin (), sorted_pages.end (), EOPCompareOp ()); + + if (! page && m_pages.size () > 0) { + page = m_pages.back (); + } while (mp_pages->count () > 0) { mp_pages->removeTab (0); } int index = -1; - for (std::vector ::iterator p = m_pages.begin (); p != m_pages.end (); ++p) { + for (std::vector ::iterator p = sorted_pages.begin (); p != sorted_pages.end (); ++p) { if ((*p)->active ()) { - mp_pages->addTab ((*p)->q_frame (), tl::to_qstring ((*p)->title ())); if ((*p) == page) { - index = int (std::distance (m_pages.begin (), p)); + index = mp_pages->count (); } + mp_pages->addTab ((*p)->q_frame (), tl::to_qstring ((*p)->title ())); } else { (*p)->q_frame ()->setParent (0); } @@ -522,7 +528,7 @@ EditorOptionsPath::setup (lay::Plugin *root) // EditorOptionsInst implementation EditorOptionsInst::EditorOptionsInst (lay::Dispatcher *root) - : QWidget (), EditorOptionsPage (), mp_root (root), mp_pcell_parameters (0) + : QWidget (), EditorOptionsPage (), mp_root (root) { mp_ui = new Ui::EditorOptionsInst (); mp_ui->setupUi (this); @@ -532,10 +538,6 @@ EditorOptionsInst::EditorOptionsInst (lay::Dispatcher *root) connect (mp_ui->lib_cbx, SIGNAL (currentIndexChanged (int)), this, SLOT (library_changed (int))); connect (mp_ui->cell_le, SIGNAL (textChanged (const QString &)), this, SLOT (cell_name_changed (const QString &))); - QHBoxLayout *layout = new QHBoxLayout (mp_ui->pcell_tab); - layout->setMargin (0); - mp_ui->pcell_tab->setLayout (layout); - m_cv_index = -1; } @@ -554,17 +556,51 @@ EditorOptionsInst::title () const void EditorOptionsInst::library_changed (int) { + update_cell_edits (); +/* @@@ BEGIN_PROTECTED update_pcell_parameters (); END_PROTECTED +@@@*/ } void EditorOptionsInst::cell_name_changed (const QString &) { + update_cell_edits (); +/* @@@ BEGIN_PROTECTED update_pcell_parameters (); END_PROTECTED +@@@*/ +} + +void +EditorOptionsInst::update_cell_edits () +{ + db::Layout *layout = 0; + + // find the layout the cell has to be looked up: that is either the layout of the current instance or + // the library selected + if (mp_ui->lib_cbx->current_library ()) { + layout = &mp_ui->lib_cbx->current_library ()->layout (); + } else { + layout = &lay::LayoutView::current ()->cellview (m_cv_index)->layout (); + } + + std::pair pc = layout->pcell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ()); + std::pair cc = layout->cell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ()); + + // by the way, update the foreground color of the cell edit box as well (red, if not valid) + QPalette pl = mp_ui->cell_le->palette (); + if (! pc.first && ! cc.first) { + pl.setColor (QPalette::Text, Qt::red); + pl.setColor (QPalette::Base, QColor (Qt::red).lighter (180)); + } else { + pl.setColor (QPalette::Text, palette ().color (QPalette::Text)); + pl.setColor (QPalette::Base, palette ().color (QPalette::Base)); + } + mp_ui->cell_le->setPalette (pl); } void @@ -608,7 +644,7 @@ BEGIN_PROTECTED } else if (layout->is_valid_cell_index (form.selected_cell_index ())) { mp_ui->cell_le->setText (tl::to_qstring (layout->cell_name (form.selected_cell_index ()))); } - update_pcell_parameters (); + // @@@@update_pcell_parameters (); } } @@ -641,28 +677,6 @@ EditorOptionsInst::apply (lay::Plugin *root) root->config_set (cfg_edit_inst_lib_name, std::string ()); } - // pcell parameters - std::string param; - db::Layout *layout = 0; - - if (mp_ui->lib_cbx->current_library ()) { - layout = &mp_ui->lib_cbx->current_library ()->layout (); - } else if (m_cv_index >= 0 && lay::LayoutView::current () && lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) { - layout = &lay::LayoutView::current ()->cellview (m_cv_index)->layout (); - } - - if (layout && mp_pcell_parameters) { - std::pair pc = layout->pcell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ()); - if (pc.first) { - const db::PCellDeclaration *pc_decl = layout->pcell_declaration (pc.second); - if (pc_decl) { - param = pcell_parameters_to_string (pc_decl->named_parameters (mp_pcell_parameters->get_parameters ())); - } - } - } - - root->config_set (cfg_edit_inst_pcell_parameters, param); - // rotation, scaling double angle = 0.0; tl::from_string (tl::to_string (mp_ui->angle_le->text ()), angle); @@ -724,61 +738,6 @@ EditorOptionsInst::setup (lay::Plugin *root) root->config_get (cfg_edit_inst_lib_name, l); mp_ui->lib_cbx->set_current_library (db::LibraryManager::instance ().lib_ptr_by_name (l)); - // pcell parameters - std::string param; - root->config_get (cfg_edit_inst_pcell_parameters, param); - - db::Layout *layout = 0; - if (mp_ui->lib_cbx->current_library ()) { - layout = &mp_ui->lib_cbx->current_library ()->layout (); - } else if (m_cv_index >= 0 && lay::LayoutView::current () && lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) { - layout = &lay::LayoutView::current ()->cellview (m_cv_index)->layout (); - } - - std::vector pv; - - if (layout && mp_pcell_parameters) { - - std::pair pc = layout->pcell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ()); - - if (pc.first) { - - const db::PCellDeclaration *pc_decl = layout->pcell_declaration (pc.second); - if (pc_decl) { - - std::map parameters; - try { - tl::Extractor ex (param.c_str ()); - ex.test ("!"); // used to flag PCells - while (! ex.at_end ()) { - std::string n; - ex.read_word_or_quoted (n); - ex.test (":"); - ex.read (parameters.insert (std::make_pair (n, tl::Variant ())).first->second); - ex.test (";"); - } - } catch (...) { } - - const std::vector &pcp = pc_decl->parameter_declarations (); - for (std::vector::const_iterator pd = pcp.begin (); pd != pcp.end (); ++pd) { - std::map::const_iterator p = parameters.find (pd->get_name ()); - if (p != parameters.end ()) { - pv.push_back (p->second); - } else { - pv.push_back (pd->get_default ()); - } - } - - } - - } - - } - - try { - update_pcell_parameters (pv); - } catch (...) { } - // rotation, scaling double angle = 0.0; root->config_get (cfg_edit_inst_angle, angle); @@ -819,43 +778,172 @@ EditorOptionsInst::setup (lay::Plugin *root) mp_ui->place_origin_cb->setChecked (place_origin); } -void -EditorOptionsInst::update_pcell_parameters () +// ------------------------------------------------------------------ +// EditorOptionsInstPCellParam implementation + +EditorOptionsInstPCellParam::EditorOptionsInstPCellParam (lay::Dispatcher *root) + : QWidget (), EditorOptionsPage (), mp_root (root), mp_pcell_parameters (0) +{ + mp_ui = new Ui::EditorOptionsInstPCellParam (); + mp_ui->setupUi (this); +} + +EditorOptionsInstPCellParam::~EditorOptionsInstPCellParam () +{ + delete mp_ui; + mp_ui = 0; +} + +std::string +EditorOptionsInstPCellParam::title () const +{ + return tl::to_string (QObject::tr ("PCell")); +} + +/* @@@ +void +EditorOptionsInstPCellParam::library_changed (int) +{ +BEGIN_PROTECTED + update_pcell_parameters (); +END_PROTECTED +} + +void +EditorOptionsInstPCellParam::cell_name_changed (const QString &) +{ +BEGIN_PROTECTED + update_pcell_parameters (); +END_PROTECTED +} +*/ + +void +EditorOptionsInstPCellParam::apply (lay::Plugin *root) +{ + // pcell parameters + std::string param; + db::Layout *layout = 0; + + db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name); + if (lib) { + layout = &lib->layout (); + } else if (m_cv_index >= 0 && lay::LayoutView::current () && lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) { + layout = &lay::LayoutView::current ()->cellview (m_cv_index)->layout (); + } + + if (layout && mp_pcell_parameters) { + std::pair pc = layout->pcell_by_name (tl::to_string (m_cell_name).c_str ()); + if (pc.first) { + const db::PCellDeclaration *pc_decl = layout->pcell_declaration (pc.second); + if (pc_decl) { + param = pcell_parameters_to_string (pc_decl->named_parameters (mp_pcell_parameters->get_parameters ())); + } + } + } + + root->config_set (cfg_edit_inst_pcell_parameters, param); +} + +void +EditorOptionsInstPCellParam::setup (lay::Plugin *root) +{ + m_cv_index = -1; + if (lay::LayoutView::current ()) { + m_cv_index = lay::LayoutView::current ()->active_cellview_index (); + } + + // cell name + root->config_get (cfg_edit_inst_cell_name, m_cell_name); + + // library + root->config_get (cfg_edit_inst_lib_name, m_lib_name); + db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name); + + // pcell parameters + std::string param; + root->config_get (cfg_edit_inst_pcell_parameters, param); + + db::Layout *layout = 0; + if (lib) { + layout = &lib->layout (); + } else if (m_cv_index >= 0 && lay::LayoutView::current () && lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) { + layout = &lay::LayoutView::current ()->cellview (m_cv_index)->layout (); + } + + std::vector pv; + + if (layout && mp_pcell_parameters) { + + std::pair pc = layout->pcell_by_name (tl::to_string (m_cell_name).c_str ()); + + if (pc.first) { + + const db::PCellDeclaration *pc_decl = layout->pcell_declaration (pc.second); + if (pc_decl) { + + std::map parameters; + try { + tl::Extractor ex (param.c_str ()); + ex.test ("!"); // used to flag PCells + while (! ex.at_end ()) { + std::string n; + ex.read_word_or_quoted (n); + ex.test (":"); + ex.read (parameters.insert (std::make_pair (n, tl::Variant ())).first->second); + ex.test (";"); + } + } catch (...) { } + + const std::vector &pcp = pc_decl->parameter_declarations (); + for (std::vector::const_iterator pd = pcp.begin (); pd != pcp.end (); ++pd) { + std::map::const_iterator p = parameters.find (pd->get_name ()); + if (p != parameters.end ()) { + pv.push_back (p->second); + } else { + pv.push_back (pd->get_default ()); + } + } + + } + + } + + } + + try { + update_pcell_parameters (pv); + } catch (...) { } +} + +void +EditorOptionsInstPCellParam::update_pcell_parameters () { update_pcell_parameters (std::vector ()); } -void -EditorOptionsInst::update_pcell_parameters (const std::vector ¶meters) +void +EditorOptionsInstPCellParam::update_pcell_parameters (const std::vector ¶meters) { db::Layout *layout = 0; +/* @@@ if (m_cv_index < 0 || !lay::LayoutView::current () || !lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) { mp_ui->param_tab_widget->setTabEnabled (1, false); return; } +@@@*/ - // find the layout the cell has to be looked up: that is either the layout of the current instance or + // find the layout the cell has to be looked up: that is either the layout of the current instance or // the library selected - if (mp_ui->lib_cbx->current_library ()) { - layout = &mp_ui->lib_cbx->current_library ()->layout (); + db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name); + if (lib) { + layout = &lib->layout (); } else { layout = &lay::LayoutView::current ()->cellview (m_cv_index)->layout (); } - std::pair pc = layout->pcell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ()); - std::pair cc = layout->cell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ()); - - // by the way, update the foreground color of the cell edit box as well (red, if not valid) - QPalette pl = mp_ui->cell_le->palette (); - if (! pc.first && ! cc.first) { - pl.setColor (QPalette::Text, Qt::red); - pl.setColor (QPalette::Base, QColor (Qt::red).lighter (180)); - } else { - pl.setColor (QPalette::Text, palette ().color (QPalette::Text)); - pl.setColor (QPalette::Base, palette ().color (QPalette::Base)); - } - mp_ui->cell_le->setPalette (pl); + std::pair pc = layout->pcell_by_name (tl::to_string (m_cell_name).c_str ()); PCellParametersPage::State pcp_state; @@ -870,15 +958,15 @@ EditorOptionsInst::update_pcell_parameters (const std::vector &par if (pc.first && layout->pcell_declaration (pc.second)) { - mp_ui->param_tab_widget->setTabEnabled (1, true); + // @@@mp_ui->param_tab_widget->setTabEnabled (1, true); lay::LayoutView *view = lay::LayoutView::current (); - mp_pcell_parameters = new PCellParametersPage (mp_ui->pcell_tab, &view->cellview (m_cv_index)->layout (), view, m_cv_index, layout->pcell_declaration (pc.second), parameters); - mp_ui->pcell_tab->layout ()->addWidget (mp_pcell_parameters); + mp_pcell_parameters = new PCellParametersPage (this, &view->cellview (m_cv_index)->layout (), view, m_cv_index, layout->pcell_declaration (pc.second), parameters); + this->layout ()->addWidget (mp_pcell_parameters); mp_pcell_parameters->set_state (pcp_state); } else { - mp_ui->param_tab_widget->setTabEnabled (1, false); + // @@@mp_ui->param_tab_widget->setTabEnabled (1, false); } } diff --git a/src/edt/edt/edtEditorOptionsPages.h b/src/edt/edt/edtEditorOptionsPages.h index 8b7f34ebc..723a42603 100644 --- a/src/edt/edt/edtEditorOptionsPages.h +++ b/src/edt/edt/edtEditorOptionsPages.h @@ -41,6 +41,7 @@ namespace Ui class EditorOptionsPath; class EditorOptionsText; class EditorOptionsInst; + class EditorOptionsInstPCellParam; } namespace lay @@ -181,7 +182,7 @@ public: virtual QWidget *q_frame () { return this; } virtual std::string title () const; - virtual int order () const { return 20; } + virtual int order () const { return 30; } void apply (lay::Plugin *root); void setup (lay::Plugin *root); @@ -217,6 +218,7 @@ public slots: void update_pcell_parameters (); void library_changed (int index); void cell_name_changed (const QString &s); + void update_cell_edits (); private: Ui::EditorOptionsInst *mp_ui; @@ -227,6 +229,38 @@ private: void update_pcell_parameters (const std::vector ¶meters); }; +/** + * @brief The instance properties page (PCell parameters) + */ +class EditorOptionsInstPCellParam + : public QWidget, public EditorOptionsPage +{ +Q_OBJECT + +public: + EditorOptionsInstPCellParam (lay::Dispatcher *root); + ~EditorOptionsInstPCellParam (); + + virtual QWidget *q_frame () { return this; } + + virtual std::string title () const; + virtual int order () const { return 21; } + void apply (lay::Plugin *root); + void setup (lay::Plugin *root); + +public slots: + void update_pcell_parameters (); + +private: + Ui::EditorOptionsInstPCellParam *mp_ui; + lay::Dispatcher *mp_root; + edt::PCellParametersPage *mp_pcell_parameters; + int m_cv_index; + std::string m_lib_name, m_cell_name; + + void update_pcell_parameters (const std::vector ¶meters); +}; + } #endif diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 4e0ca160a..8e579a759 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -88,6 +88,7 @@ void get_inst_options (std::vector < std::pair > &opti static void get_inst_editor_options_pages (std::vector &ret, lay::Dispatcher *root) { + ret.push_back (new EditorOptionsInstPCellParam (root)); ret.push_back (new EditorOptionsInst (root)); } @@ -402,9 +403,11 @@ activate_service (lay::LayoutView *view, const lay::PluginDeclaration *pd, bool return; } + // @@@ TODO: this is very inefficient as each "activate" will regenerate the tabs for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { (*op)->activate (((*op)->plugin_declaration () == pd || ! (*op)->plugin_declaration ()) && active); } + } void From a1443a801849417bd8a272d0009b79af89a81f6c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 10 Aug 2020 10:00:29 +0200 Subject: [PATCH 040/113] WIP: 'editor options' now works again, brings dock widget to front if in tab. --- src/lay/lay/layMainWindow.cc | 50 ++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 26eefba7a..6ff3d246c 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -414,7 +414,43 @@ MainWindow::instance () return mw_instance; } -// ----------------------------------- +// ------------------------------------------------------------- + +static void +show_dock_widget (QDockWidget *dock_widget, bool visible) +{ + if (visible) { + + dock_widget->show (); + + // NOTE: this is a clumsy way to make sure the dock widget is made the current tab if it's in a tabbed dock + // TODO: is there a better way to do this? + QMainWindow *main_window = dynamic_cast (dock_widget->parent ()); + if (! main_window) { + return; + } + + // Look up all children of the main window and find the QTabBars. These are the dock tabs (we don't create others). + // Inside these, look up the right tab by checking the titles. + QList mw_children = main_window->children (); + for (QList::const_iterator i = mw_children.begin (); i != mw_children.end (); ++i) { + QTabBar *tab_bar = dynamic_cast (*i); + if (tab_bar) { + for (int j = 0; j < tab_bar->count (); ++j) { + if (tab_bar->tabText (j) == dock_widget->windowTitle ()) { + tab_bar->setCurrentIndex (j); + return; + } + } + } + } + + } else { + dock_widget->hide (); + } +} + +// ------------------------------------------------------------- MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const char *name, bool undo_enabled) : QMainWindow (0), @@ -759,7 +795,7 @@ MainWindow::init_menu () } void -MainWindow::dock_widget_visibility_changed (bool /*visible*/) +MainWindow::dock_widget_visibility_changed (bool visible) { if (sender () == mp_lp_dock_widget) { dispatcher ()->config_set (cfg_show_layer_panel, tl::to_string (!mp_lp_dock_widget->isHidden ())); @@ -773,6 +809,8 @@ MainWindow::dock_widget_visibility_changed (bool /*visible*/) dispatcher ()->config_set (cfg_show_navigator, tl::to_string (!mp_navigator_dock_widget->isHidden ())); } else if (sender () == mp_layer_toolbox_dock_widget) { dispatcher ()->config_set (cfg_show_layer_toolbox, tl::to_string (!mp_layer_toolbox_dock_widget->isHidden ())); + } else if (sender () == mp_eo_dock_widget) { + m_eo_visible = visible; } } @@ -1889,11 +1927,7 @@ MainWindow::select_mode (int m) if (editable != m_eo_visible) { m_eo_visible = editable; - if (m_eo_visible) { - mp_eo_dock_widget->show (); - } else { - mp_eo_dock_widget->hide (); - } + show_dock_widget (mp_eo_dock_widget, m_eo_visible); } } @@ -3872,8 +3906,8 @@ MainWindow::menu_activated (const std::string &symbol) } else if (symbol == "cm_edit_options") { if (!m_eo_visible) { - mp_eo_dock_widget->show (); m_eo_visible = true; + show_dock_widget (mp_eo_dock_widget, m_eo_visible); } } else { From 701f69005317ad3b1d333b1c42480e6d47e5a5d5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 10 Aug 2020 10:32:01 +0200 Subject: [PATCH 041/113] WIP: bugfix - PCell parameter page wasn't updated initially --- src/edt/edt/EditorOptionsPath.ui | 2 +- src/edt/edt/edtEditorOptionsPages.cc | 2 +- src/edt/edt/edtMainService.cc | 6 ------ src/edt/edt/edtMainService.h | 5 ----- src/edt/edt/edtService.cc | 6 ++++++ src/edt/edt/edtServiceImpl.cc | 18 ------------------ 6 files changed, 8 insertions(+), 31 deletions(-) diff --git a/src/edt/edt/EditorOptionsPath.ui b/src/edt/edt/EditorOptionsPath.ui index ac184b089..cf8a30249 100644 --- a/src/edt/edt/EditorOptionsPath.ui +++ b/src/edt/edt/EditorOptionsPath.ui @@ -56,7 +56,7 @@ - Width + Width diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index f050b9ca4..230b95a28 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -873,7 +873,7 @@ EditorOptionsInstPCellParam::setup (lay::Plugin *root) std::vector pv; - if (layout && mp_pcell_parameters) { + if (layout) { std::pair pc = layout->pcell_by_name (tl::to_string (m_cell_name).c_str ()); diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index 9e2673d21..304bcb738 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -2269,12 +2269,6 @@ MainService::cm_change_layer () } -void -MainService::show_editor_options () -{ - show_editor_options_page (view ()); -} - void MainService::check_no_guiding_shapes () { diff --git a/src/edt/edt/edtMainService.h b/src/edt/edt/edtMainService.h index 34bdf512d..1ae28fc79 100644 --- a/src/edt/edt/edtMainService.h +++ b/src/edt/edt/edtMainService.h @@ -189,11 +189,6 @@ public: */ void cm_tap (); - /** - * @brief Shows the editor options - */ - void show_editor_options (); - /** * @brief "paste" operation */ diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index 493387153..c244213ad 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -796,10 +796,16 @@ void Service::activated () { if (view ()->is_editable ()) { + view ()->cancel (); // cancel any pending edit operations and clear the selection set_edit_marker (0); + m_immediate = do_activated (); m_editing = false; + + // Show editor options panel + show_editor_options_page (view ()); + } // make all editor option pages visible diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index e4a15b635..7a7ff40bf 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -749,12 +749,6 @@ TextService::do_activated () { m_rot = 0; - // Show editor options dialog to allow entering of width - std::vector edt_main_services = view ()->get_plugins (); - if (edt_main_services.size () > 0) { - edt_main_services [0]->show_editor_options (); - } - return true; // start editing immediately } @@ -915,12 +909,6 @@ PathService::do_begin_edit (const db::DPoint &p) bool PathService::do_activated () { - // Show editor options dialog to allow entering of width - std::vector edt_main_services = view ()->get_plugins (); - if (edt_main_services.size () > 0) { - edt_main_services [0]->show_editor_options (); - } - return false; // don't start editing immediately } @@ -1120,12 +1108,6 @@ InstService::properties_page (db::Manager *manager, QWidget *parent) bool InstService::do_activated () { - // Show editor options dialog to allow entering of parameters - std::vector edt_main_services = view ()->get_plugins (); - if (edt_main_services.size () > 0) { - edt_main_services [0]->show_editor_options (); - } - m_cv_index = view ()->active_cellview_index (); m_has_valid_cell = false; From 4ffae9668e34e42b85699e56949f9bb63b311634 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 10 Aug 2020 19:51:36 +0200 Subject: [PATCH 042/113] WIP: reworked drag&drop from library view into canvas - now will switch to instance mode and take parameters from the instance options rather than popping up a PCell dialog and using mag 1, no mirror, no rotation etc. --- src/edt/edt/edt.pro | 2 - src/edt/edt/edtPCellParametersDialog.cc | 65 ------------ src/edt/edt/edtPCellParametersDialog.h | 86 --------------- src/edt/edt/edtServiceImpl.cc | 133 +++++++----------------- src/edt/edt/edtServiceImpl.h | 4 - 5 files changed, 39 insertions(+), 251 deletions(-) delete mode 100644 src/edt/edt/edtPCellParametersDialog.cc delete mode 100644 src/edt/edt/edtPCellParametersDialog.h diff --git a/src/edt/edt/edt.pro b/src/edt/edt/edt.pro index d78982deb..613c92dc3 100644 --- a/src/edt/edt/edt.pro +++ b/src/edt/edt/edt.pro @@ -21,7 +21,6 @@ HEADERS = \ edtServiceImpl.h \ edtUtils.h \ edtCommon.h \ - edtPCellParametersDialog.h \ edtDistribute.h FORMS = \ @@ -61,7 +60,6 @@ SOURCES = \ edtServiceImpl.cc \ edtUtils.cc \ gsiDeclEdt.cc \ - edtPCellParametersDialog.cc \ edtDistribute.cc INCLUDEPATH += $$TL_INC $$GSI_INC $$LAYBASIC_INC $$DB_INC diff --git a/src/edt/edt/edtPCellParametersDialog.cc b/src/edt/edt/edtPCellParametersDialog.cc deleted file mode 100644 index bdb33b47f..000000000 --- a/src/edt/edt/edtPCellParametersDialog.cc +++ /dev/null @@ -1,65 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2020 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 "edtPCellParametersDialog.h" - -#include - -namespace edt -{ - -PCellParametersDialog::PCellParametersDialog (QWidget *parent) - : QDialog (parent) -{ - setupUi (this); - - connect (buttons->button (QDialogButtonBox::Apply), SIGNAL (clicked ()), this, SLOT (apply_pressed ())); -} - -void -PCellParametersDialog::apply_pressed () -{ - emit parameters_changed (); - parameters_changed_event (); -} - -std::vector -PCellParametersDialog::get_parameters () -{ - return parameters->get_parameters (); -} - -void -PCellParametersDialog::set_parameters (const std::vector &p) -{ - parameters->set_parameters (p); -} - -int -PCellParametersDialog::exec (const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type &p) -{ - parameters->setup (layout, view, cv_index, pcell_decl, p); - return QDialog::exec (); -} - -} diff --git a/src/edt/edt/edtPCellParametersDialog.h b/src/edt/edt/edtPCellParametersDialog.h deleted file mode 100644 index 2a977a4cd..000000000 --- a/src/edt/edt/edtPCellParametersDialog.h +++ /dev/null @@ -1,86 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2020 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_edtPCellParametersDialog -#define HDR_edtPCellParametersDialog - -#include "dbPCellDeclaration.h" -#include "ui_PCellParametersDialog.h" - -#include - -namespace lay -{ - class LayoutView; -} - -namespace edt -{ - -/** - * @brief A QScrollArea that displays and allows editing PCell parameters - */ -class PCellParametersDialog - : public QDialog, private Ui::PCellParametersDialog -{ -Q_OBJECT - -public: - /** - * @brief Constructor: create a dialog showing the given parameters - * @param parent The parent widget - */ - PCellParametersDialog (QWidget *parent); - - /** - * @brief Executes the parameter dialog - * @param layout The layout in which the PCell instance resides - * @param view The layout view from which to take layers for example - * @param cv_index The index of the cellview in "view" - * @param pcell_decl The PCell declaration - * @param parameters The parameter values to show (if empty, the default values are used) - */ - int exec (const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type &p); - - /** - * @brief Get the current parameters - */ - std::vector get_parameters (); - - /** - * @brief Sets the given parameters as values - */ - void set_parameters (const std::vector &values); - - tl::Event parameters_changed_event; - -signals: - void parameters_changed (); - -private slots: - void apply_pressed (); -}; - -} - -#endif diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index 7a7ff40bf..c3779c76e 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -25,7 +25,6 @@ #include "edtServiceImpl.h" #include "edtPropertiesPages.h" #include "edtInstPropertiesPage.h" -#include "edtPCellParametersDialog.h" #include "edtService.h" #include "dbEdge.h" #include "dbLibrary.h" @@ -1130,17 +1129,14 @@ InstService::get_default_layer_for_pcell () bool InstService::drag_enter_event (const db::DPoint &p, const lay::DragDropDataBase *data) -{ +{ const lay::CellDragDropData *cd = dynamic_cast (data); if (view ()->is_editable () && cd && (cd->layout () == & view ()->active_cellview ()->layout () || cd->library ())) { view ()->cancel (); - set_edit_marker (0); - m_cv_index = view ()->active_cellview_index (); - m_in_drag_drop = true; - + // configure from the drag/drop data if (cd->library ()) { if (m_lib_name != cd->library ()->get_name ()) { m_lib_name = cd->library ()->get_name (); @@ -1155,38 +1151,45 @@ InstService::drag_enter_event (const db::DPoint &p, const lay::DragDropDataBase if (cd->is_pcell ()) { const db::PCellDeclaration *pcell_decl = cd->layout ()->pcell_declaration (cd->cell_index ()); - if (pcell_decl) { + if (! pcell_decl) { + return false; + } - if (m_cell_or_pcell_name != pcell_decl->name ()) { - m_cell_or_pcell_name = pcell_decl->name (); - m_pcell_parameters.clear (); + if (m_cell_or_pcell_name != pcell_decl->name ()) { + m_cell_or_pcell_name = pcell_decl->name (); + m_pcell_parameters.clear (); + } + + m_is_pcell = true; + + // NOTE: we reuse previous parameters for convenience unless PCell or library has changed + const std::vector &pd = pcell_decl->parameter_declarations(); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + if (i->get_type () == db::PCellParameterDeclaration::t_layer && !i->is_hidden () && !i->is_readonly () && i->get_default ().is_nil ()) { + m_pcell_parameters.insert (std::make_pair (i->get_name (), get_default_layer_for_pcell ())); + } else { + m_pcell_parameters.insert (std::make_pair (i->get_name (), i->get_default ())); } - - m_is_pcell = true; - - // NOTE: we reuse previous parameters for convenience unless PCell or library has changed - const std::vector &pd = pcell_decl->parameter_declarations(); - for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { - if (i->get_type () == db::PCellParameterDeclaration::t_layer && !i->is_hidden () && !i->is_readonly () && i->get_default ().is_nil ()) { - m_pcell_parameters.insert (std::make_pair (i->get_name (), get_default_layer_for_pcell ())); - } else { - m_pcell_parameters.insert (std::make_pair (i->get_name (), i->get_default ())); - } - } - - do_begin_edit (p); - return true; - } } else if (cd->layout ()->is_valid_cell_index (cd->cell_index ())) { m_cell_or_pcell_name = cd->layout ()->cell_name (cd->cell_index ()); - do_begin_edit (p); - return true; + } else { + return false; } + sync_to_config (); + m_in_drag_drop = true; + + view ()->switch_mode (plugin_declaration ()->id ()); + + do_begin_edit (p); + + // action taken. + return true; + } return false; @@ -1203,7 +1206,7 @@ InstService::drag_move_event (const db::DPoint &p, const lay::DragDropDataBase * } } -void +void InstService::drag_leave_event () { if (m_in_drag_drop) { @@ -1212,7 +1215,7 @@ InstService::drag_leave_event () } } -bool +bool InstService::selection_applies (const lay::ObjectInstPath &sel) const { return sel.is_cell_inst (); @@ -1221,53 +1224,8 @@ InstService::selection_applies (const lay::ObjectInstPath &sel) const bool InstService::drop_event (const db::DPoint & /*p*/, const lay::DragDropDataBase * /*data*/) { - if (m_in_drag_drop) { - - const lay::CellView &cv = view ()->cellview (m_cv_index); - if (! cv.is_valid ()) { - return false; - } - - make_cell (cv); - - bool accepted = true; - - if (m_has_valid_cell && mp_pcell_decl) { - - std::vector pv = mp_pcell_decl->map_parameters (m_pcell_parameters); - - // Turn off the drag cursor for the modal dialog - QApplication::restoreOverrideCursor (); - - // for PCells dragged show the parameter dialog for a chance to edit the initial parameters - if (! mp_pcell_parameters_dialog.get ()) { - mp_pcell_parameters_dialog.reset (new edt::PCellParametersDialog (view ())); - mp_pcell_parameters_dialog->parameters_changed_event.add (this, &InstService::apply_edits); - } - - if (! mp_pcell_parameters_dialog->exec (mp_current_layout, view (), m_cv_index, mp_pcell_decl, pv)) { - accepted = false; - } else { - m_has_valid_cell = false; - m_pcell_parameters = mp_pcell_decl->named_parameters (mp_pcell_parameters_dialog->get_parameters ()); - } - - } - - set_edit_marker (0); - - if (accepted) { - do_finish_edit (); - } else { - do_cancel_edit (); - } - - sync_to_config (); - return true; - - } else { - return false; - } + m_in_drag_drop = false; + return false; } void @@ -1538,6 +1496,8 @@ InstService::do_cancel_edit () m_has_valid_cell = false; m_in_drag_drop = false; + set_edit_marker (0); + // clean up any proxy cells created so far const lay::CellView &cv = view ()->cellview (m_cv_index); if (cv.is_valid ()) { @@ -1651,16 +1611,6 @@ InstService::config_finalize () edt::Service::config_finalize (); } -void -InstService::apply_edits() -{ - if (mp_pcell_decl && mp_pcell_parameters_dialog.get ()) { - m_pcell_parameters = mp_pcell_decl->named_parameters (mp_pcell_parameters_dialog->get_parameters ()); - } - - sync_to_config (); -} - void InstService::update_marker () { @@ -1685,14 +1635,9 @@ InstService::get_inst (db::CellInstArray &inst) // compute the instance's transformation db::VCplxTrans pt = (db::CplxTrans (cv->layout ().dbu ()) * m_trans).inverted (); - db::ICplxTrans trans; - if (m_in_drag_drop) { - trans = db::ICplxTrans (1.0, 0.0, false, pt * m_disp - db::Point ()); - } else { - trans = db::ICplxTrans (m_scale, m_angle, m_mirror, pt * m_disp - db::Point ()); - } + db::ICplxTrans trans = db::ICplxTrans (m_scale, m_angle, m_mirror, pt * m_disp - db::Point ()); - if (! m_in_drag_drop && m_array && m_rows > 0 && m_columns > 0) { + if (m_array && m_rows > 0 && m_columns > 0) { db::Vector row = db::Vector (pt * db::DVector (m_row_x, m_row_y)); db::Vector column = db::Vector (pt * db::DVector (m_column_x, m_column_y)); inst = db::CellInstArray (db::CellInst (ci.second), trans, row, column, m_rows, m_columns); diff --git a/src/edt/edt/edtServiceImpl.h b/src/edt/edt/edtServiceImpl.h index 3fdad1ef2..048b117a1 100644 --- a/src/edt/edt/edtServiceImpl.h +++ b/src/edt/edt/edtServiceImpl.h @@ -38,8 +38,6 @@ namespace lay namespace edt { -class PCellParametersDialog; - /** * @brief Implementation of the edt::Service for generic shape editing */ @@ -245,10 +243,8 @@ private: const db::PCellDeclaration *mp_pcell_decl; int m_cv_index; db::ICplxTrans m_trans; - std::auto_ptr mp_pcell_parameters_dialog; void update_marker (); - void apply_edits (); bool get_inst (db::CellInstArray &inst); std::pair make_cell (const lay::CellView &cv); tl::Variant get_default_layer_for_pcell (); From e8668ed092715d08f4489f9afe86f00fb01bfea5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 11 Aug 2020 08:27:06 +0200 Subject: [PATCH 043/113] WIP: PCell parameters are now remembered for each PCell name/library --- src/edt/edt/PCellParametersDialog.ui | 73 --------------------- src/edt/edt/edt.pro | 1 - src/edt/edt/edtMainService.cc | 9 +++ src/edt/edt/edtMainService.h | 4 ++ src/edt/edt/edtPCellParametersPage.cc | 10 +++ src/edt/edt/edtService.cc | 37 +++++++++++ src/edt/edt/edtService.h | 5 ++ src/edt/edt/edtServiceImpl.cc | 93 ++++++++++++++++++++++----- src/edt/edt/edtServiceImpl.h | 4 ++ 9 files changed, 147 insertions(+), 89 deletions(-) delete mode 100644 src/edt/edt/PCellParametersDialog.ui diff --git a/src/edt/edt/PCellParametersDialog.ui b/src/edt/edt/PCellParametersDialog.ui deleted file mode 100644 index be3b83cde..000000000 --- a/src/edt/edt/PCellParametersDialog.ui +++ /dev/null @@ -1,73 +0,0 @@ - - - PCellParametersDialog - - - - 0 - 0 - 469 - 429 - - - - PCell Parameters - - - - - - - - - QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - edt::PCellParametersPage - QWidget -
edtPCellParametersPage.h
- 1 -
-
- - - - buttons - rejected() - PCellParametersDialog - reject() - - - 321 - 405 - - - 337 - 423 - - - - - buttons - accepted() - PCellParametersDialog - accept() - - - 427 - 405 - - - 443 - 425 - - - - -
diff --git a/src/edt/edt/edt.pro b/src/edt/edt/edt.pro index 613c92dc3..a10d63c9e 100644 --- a/src/edt/edt/edt.pro +++ b/src/edt/edt/edt.pro @@ -41,7 +41,6 @@ FORMS = \ PolygonPropertiesPage.ui \ RoundCornerOptionsDialog.ui \ TextPropertiesPage.ui \ - PCellParametersDialog.ui \ DistributeOptionsDialog.ui \ EditorOptionsInstPCellParam.ui diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index 304bcb738..b2b522623 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -59,6 +59,7 @@ MainService::MainService (db::Manager *manager, lay::LayoutView *view, lay::Disp : lay::Plugin (view), lay::Editable (view), db::Object (manager), + dm_setup_pages (this, &MainService::do_setup_pages), mp_view (view), mp_root (root), m_needs_update (false), @@ -2358,6 +2359,14 @@ private: void MainService::config_finalize () +{ + // It's important that the editor option pages are updated last - because the + // configuration change may trigger other configuration changes + dm_setup_pages (); +} + +void +MainService::do_setup_pages () { setup_pages (view ()); } diff --git a/src/edt/edt/edtMainService.h b/src/edt/edt/edtMainService.h index 1ae28fc79..2b22cefee 100644 --- a/src/edt/edt/edtMainService.h +++ b/src/edt/edt/edtMainService.h @@ -201,6 +201,9 @@ public: virtual void config_finalize (); private: + // The deferred execution handler for the config_finalize event + tl::DeferredMethod dm_setup_pages; + // The layout view that this service is attached to lay::LayoutView *mp_view; lay::Dispatcher *mp_root; @@ -242,6 +245,7 @@ private: lay::FlattenInstOptionsDialog *flatten_inst_options_dialog (); edt::MakeCellOptionsDialog *make_cell_options_dialog (); edt::MakeArrayOptionsDialog *make_array_options_dialog (); + void do_setup_pages (); }; } diff --git a/src/edt/edt/edtPCellParametersPage.cc b/src/edt/edt/edtPCellParametersPage.cc index 87ab6d3e4..08ad31c71 100644 --- a/src/edt/edt/edtPCellParametersPage.cc +++ b/src/edt/edt/edtPCellParametersPage.cc @@ -165,6 +165,8 @@ PCellParametersPage::init () mp_parameters_area = 0; QGridLayout *frame_layout = new QGridLayout (this); + // spacing and margin for tool windows + frame_layout->setMargin (0); setLayout (frame_layout); mp_error_icon = new QLabel (this); @@ -200,6 +202,7 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int m_widgets.clear (); mp_parameters_area = new QScrollArea (this); + mp_parameters_area->setFrameShape (QFrame::NoFrame); QGridLayout *frame_layout = dynamic_cast (QFrame::layout ()); frame_layout->addWidget (mp_parameters_area, 0, 0, 1, 2); frame_layout->setRowStretch (0, 1); @@ -211,6 +214,9 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int QGridLayout *inner_grid = new QGridLayout (inner_frame); inner_frame->setLayout (inner_grid); + inner_grid->setMargin (4); + inner_grid->setHorizontalSpacing (6); + inner_grid->setVerticalSpacing (2); QWidget *main_frame = inner_frame; QGridLayout *main_grid = inner_grid; @@ -247,6 +253,9 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int main_grid->addWidget (gb, main_row, 0, 1, 2); inner_grid = new QGridLayout (gb); + inner_grid->setMargin (4); + inner_grid->setHorizontalSpacing (6); + inner_grid->setVerticalSpacing (2); gb->setLayout (inner_grid); inner_frame = gb; @@ -286,6 +295,7 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int QHBoxLayout *hb = new QHBoxLayout (f); hb->setMargin (0); f->setLayout (hb); + f->setFrameShape (QFrame::NoFrame); QLineEdit *le = new QLineEdit (f); le->setEnabled (! p->is_readonly ()); diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index c244213ad..f479b1c56 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -231,6 +231,12 @@ Service::snap2 (const db::DPoint &p, const db::DPoint &plast, bool connect) cons return lay::obj_snap (m_snap_to_objects ? view () : 0, plast, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, connect ? connect_ac () : move_ac (), snap_range).second; } +void +Service::service_configuration_changed () +{ + // The base class implementation does nothing +} + bool Service::configure (const std::string &name, const std::string &value) { @@ -238,27 +244,58 @@ Service::configure (const std::string &name, const std::string &value) edt::ACConverter acc; if (name == cfg_edit_global_grid) { + egc.from_string (value, m_global_grid); + service_configuration_changed (); + } else if (name == cfg_edit_show_shapes_of_instances) { + tl::from_string (value, m_show_shapes_of_instances); + service_configuration_changed (); + } else if (name == cfg_edit_max_shapes_of_instances) { + tl::from_string (value, m_max_shapes_of_instances); + service_configuration_changed (); + } else if (name == cfg_edit_grid) { + egc.from_string (value, m_edit_grid); + service_configuration_changed (); + return true; // taken + } else if (name == cfg_edit_snap_to_objects) { + tl::from_string (value, m_snap_to_objects); + service_configuration_changed (); + return true; // taken + } else if (name == cfg_edit_move_angle_mode) { + acc.from_string (value, m_move_ac); + service_configuration_changed (); + return true; // taken + } else if (name == cfg_edit_connect_angle_mode) { + acc.from_string (value, m_connect_ac); + service_configuration_changed (); + return true; // taken + } else if (name == cfg_edit_top_level_selection) { + tl::from_string (value, m_top_level_sel); + service_configuration_changed (); + } else if (name == cfg_edit_hier_copy_mode) { + tl::from_string (value, m_hier_copy_mode); + service_configuration_changed (); + } return false; // not taken diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h index 6e93219cb..997d7e102 100644 --- a/src/edt/edt/edtService.h +++ b/src/edt/edt/edtService.h @@ -455,6 +455,11 @@ protected: */ virtual void do_cancel_edit () { } + /** + * @brief Called when a configuration parameter provided by the service base class has changed + */ + virtual void service_configuration_changed (); + /** * @brief Install a marker for representing the edited object * diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index c3779c76e..63e5235f6 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -1288,10 +1288,6 @@ InstService::do_begin_edit (const db::DPoint &p) m_trans = db::VCplxTrans (1.0 / cv->layout ().dbu ()) * tv [0] * db::CplxTrans (cv->layout ().dbu ()) * cv.context_trans (); } - lay::Marker *marker = new lay::Marker (view (), m_cv_index, ! show_shapes_of_instances (), show_shapes_of_instances () ? max_shapes_of_instances () : 0); - marker->set_vertex_shape (lay::ViewOp::Cross); - marker->set_vertex_size (9 /*cross vertex size*/); - set_edit_marker (marker); update_marker (); } @@ -1505,18 +1501,33 @@ InstService::do_cancel_edit () } } +void +InstService::service_configuration_changed () +{ + m_needs_update = true; +} + bool InstService::configure (const std::string &name, const std::string &value) { if (name == cfg_edit_inst_cell_name) { - m_cell_or_pcell_name = value; - m_needs_update = true; + + if (value != m_cell_or_pcell_name) { + m_cell_or_pcell_name = value; + m_needs_update = true; + } + return true; // taken } if (name == cfg_edit_inst_lib_name) { - m_lib_name = value; - m_needs_update = true; + + if (value != m_lib_name) { + m_lib_name_previous = m_lib_name; + m_lib_name = value; + m_needs_update = true; + } + return true; // taken } @@ -1603,9 +1614,58 @@ void InstService::config_finalize () { if (m_needs_update) { + + // if the library or cell name has changed, store the current pcell parameters and try to reuse + // an existing parameter set + if (! m_cell_or_pcell_name_previous.empty () && (m_cell_or_pcell_name_previous != m_cell_or_pcell_name || m_lib_name_previous != m_lib_name)) { + + m_stored_pcell_parameters[std::make_pair (m_cell_or_pcell_name_previous, m_lib_name_previous)] = m_pcell_parameters; + + std::map, std::map >::const_iterator p = m_stored_pcell_parameters.find (std::make_pair (m_cell_or_pcell_name, m_lib_name)); + if (p != m_stored_pcell_parameters.end ()) { + m_pcell_parameters = p->second; + } else { + m_pcell_parameters.clear (); + } + + m_is_pcell = false; + + const lay::CellView &cv = view ()->cellview (m_cv_index); + if (cv.is_valid ()) { + + db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name); + + // find the layout the cell has to be looked up: that is either the layout of the current instance or + // the library selected + const db::Layout *layout; + if (lib) { + layout = &lib->layout (); + } else { + layout = &cv->layout (); + } + + m_is_pcell = layout->pcell_by_name (m_cell_or_pcell_name.c_str ()).first; + + } + + } + m_has_valid_cell = false; - update_marker (); + update_marker (); // NOTE: sets m_is_pcell m_needs_update = false; + + // remember the current cell or library name + m_cell_or_pcell_name_previous = m_cell_or_pcell_name; + m_lib_name_previous = m_lib_name; + + // Reflects any changes in PCell parameters induced by reuse of make_cell (called from update_marker) + // in the configuration + if (m_is_pcell) { + dispatcher ()->config_set (cfg_edit_inst_pcell_parameters, pcell_parameters_to_string (m_pcell_parameters)); + } else { + dispatcher ()->config_set (cfg_edit_inst_pcell_parameters, std::string ()); + } + } edt::Service::config_finalize (); @@ -1614,13 +1674,16 @@ InstService::config_finalize () void InstService::update_marker () { - lay::Marker *marker = dynamic_cast (edit_marker ()); - if (marker) { + lay::Marker *marker = new lay::Marker (view (), m_cv_index, ! show_shapes_of_instances (), show_shapes_of_instances () ? max_shapes_of_instances () : 0); + marker->set_vertex_shape (lay::ViewOp::Cross); + marker->set_vertex_size (9 /*cross vertex size*/); + set_edit_marker (marker); + + db::CellInstArray inst; + if (get_inst (inst)) { + marker->set (inst, m_trans); + } else { marker->set (); - db::CellInstArray inst; - if (get_inst (inst)) { - marker->set (inst, m_trans); - } } } diff --git a/src/edt/edt/edtServiceImpl.h b/src/edt/edt/edtServiceImpl.h index 048b117a1..3720a9f52 100644 --- a/src/edt/edt/edtServiceImpl.h +++ b/src/edt/edt/edtServiceImpl.h @@ -220,6 +220,8 @@ public: protected: bool configure (const std::string &name, const std::string &value); + void service_configuration_changed (); + void config_finalize (); private: @@ -228,7 +230,9 @@ private: bool m_mirror; db::DPoint m_disp; std::string m_cell_or_pcell_name, m_lib_name; + std::string m_cell_or_pcell_name_previous, m_lib_name_previous; std::map m_pcell_parameters; + std::map, std::map > m_stored_pcell_parameters; bool m_is_pcell; bool m_array; unsigned int m_rows, m_columns; From cf3e79b8f8beb44c7e0ed7c6964d16c0c85036f7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 11 Aug 2020 08:52:23 +0200 Subject: [PATCH 044/113] WIP: PCell parameters are now remembered also for drag&drop --- src/edt/edt/edtServiceImpl.cc | 100 +++++++++++++++------------------- src/edt/edt/edtServiceImpl.h | 1 + 2 files changed, 46 insertions(+), 55 deletions(-) diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index 63e5235f6..471c9de8e 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -1140,14 +1140,11 @@ InstService::drag_enter_event (const db::DPoint &p, const lay::DragDropDataBase if (cd->library ()) { if (m_lib_name != cd->library ()->get_name ()) { m_lib_name = cd->library ()->get_name (); - m_pcell_parameters.clear (); } } else { m_lib_name.clear (); } - m_is_pcell = false; - if (cd->is_pcell ()) { const db::PCellDeclaration *pcell_decl = cd->layout ()->pcell_declaration (cd->cell_index ()); @@ -1157,19 +1154,6 @@ InstService::drag_enter_event (const db::DPoint &p, const lay::DragDropDataBase if (m_cell_or_pcell_name != pcell_decl->name ()) { m_cell_or_pcell_name = pcell_decl->name (); - m_pcell_parameters.clear (); - } - - m_is_pcell = true; - - // NOTE: we reuse previous parameters for convenience unless PCell or library has changed - const std::vector &pd = pcell_decl->parameter_declarations(); - for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { - if (i->get_type () == db::PCellParameterDeclaration::t_layer && !i->is_hidden () && !i->is_readonly () && i->get_default ().is_nil ()) { - m_pcell_parameters.insert (std::make_pair (i->get_name (), get_default_layer_for_pcell ())); - } else { - m_pcell_parameters.insert (std::make_pair (i->get_name (), i->get_default ())); - } } } else if (cd->layout ()->is_valid_cell_index (cd->cell_index ())) { @@ -1180,6 +1164,8 @@ InstService::drag_enter_event (const db::DPoint &p, const lay::DragDropDataBase return false; } + switch_cell_or_pcell (); + sync_to_config (); m_in_drag_drop = true; @@ -1610,54 +1596,58 @@ InstService::configure (const std::string &name, const std::string &value) return edt::Service::configure (name, value); } +void +InstService::switch_cell_or_pcell () +{ + // if the library or cell name has changed, store the current pcell parameters and try to reuse + // an existing parameter set + if (! m_cell_or_pcell_name_previous.empty () && (m_cell_or_pcell_name_previous != m_cell_or_pcell_name || m_lib_name_previous != m_lib_name)) { + + m_stored_pcell_parameters[std::make_pair (m_cell_or_pcell_name_previous, m_lib_name_previous)] = m_pcell_parameters; + + std::map, std::map >::const_iterator p = m_stored_pcell_parameters.find (std::make_pair (m_cell_or_pcell_name, m_lib_name)); + if (p != m_stored_pcell_parameters.end ()) { + m_pcell_parameters = p->second; + } else { + m_pcell_parameters.clear (); + } + + } + + db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name); + const lay::CellView &cv = view ()->cellview (m_cv_index); + + // find the layout the cell has to be looked up: that is either the layout of the current instance or + // the library selected + const db::Layout *layout = 0; + if (lib) { + layout = &lib->layout (); + } else if (cv.is_valid ()) { + layout = &cv->layout (); + } + + if (layout) { + m_is_pcell = layout->pcell_by_name (m_cell_or_pcell_name.c_str ()).first; + } else { + m_is_pcell = false; + } + + // remember the current cell or library name + m_cell_or_pcell_name_previous = m_cell_or_pcell_name; + m_lib_name_previous = m_lib_name; +} + void InstService::config_finalize () { if (m_needs_update) { - // if the library or cell name has changed, store the current pcell parameters and try to reuse - // an existing parameter set - if (! m_cell_or_pcell_name_previous.empty () && (m_cell_or_pcell_name_previous != m_cell_or_pcell_name || m_lib_name_previous != m_lib_name)) { - - m_stored_pcell_parameters[std::make_pair (m_cell_or_pcell_name_previous, m_lib_name_previous)] = m_pcell_parameters; - - std::map, std::map >::const_iterator p = m_stored_pcell_parameters.find (std::make_pair (m_cell_or_pcell_name, m_lib_name)); - if (p != m_stored_pcell_parameters.end ()) { - m_pcell_parameters = p->second; - } else { - m_pcell_parameters.clear (); - } - - m_is_pcell = false; - - const lay::CellView &cv = view ()->cellview (m_cv_index); - if (cv.is_valid ()) { - - db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name); - - // find the layout the cell has to be looked up: that is either the layout of the current instance or - // the library selected - const db::Layout *layout; - if (lib) { - layout = &lib->layout (); - } else { - layout = &cv->layout (); - } - - m_is_pcell = layout->pcell_by_name (m_cell_or_pcell_name.c_str ()).first; - - } - - } + switch_cell_or_pcell (); m_has_valid_cell = false; - update_marker (); // NOTE: sets m_is_pcell + update_marker (); m_needs_update = false; - // remember the current cell or library name - m_cell_or_pcell_name_previous = m_cell_or_pcell_name; - m_lib_name_previous = m_lib_name; - // Reflects any changes in PCell parameters induced by reuse of make_cell (called from update_marker) // in the configuration if (m_is_pcell) { diff --git a/src/edt/edt/edtServiceImpl.h b/src/edt/edt/edtServiceImpl.h index 3720a9f52..f9545694a 100644 --- a/src/edt/edt/edtServiceImpl.h +++ b/src/edt/edt/edtServiceImpl.h @@ -253,6 +253,7 @@ private: std::pair make_cell (const lay::CellView &cv); tl::Variant get_default_layer_for_pcell (); void sync_to_config (); + void switch_cell_or_pcell (); }; } From 649d6da1b84867d76c6d13ac51200964869b5b66 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 11 Aug 2020 09:17:46 +0200 Subject: [PATCH 045/113] WIP: PCell page indicates now if not a PCell. --- src/edt/edt/edtEditorOptionsPages.cc | 57 ++++++++++++---------------- src/edt/edt/edtEditorOptionsPages.h | 2 + 2 files changed, 26 insertions(+), 33 deletions(-) diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index 230b95a28..ede46467e 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -782,7 +782,7 @@ EditorOptionsInst::setup (lay::Plugin *root) // EditorOptionsInstPCellParam implementation EditorOptionsInstPCellParam::EditorOptionsInstPCellParam (lay::Dispatcher *root) - : QWidget (), EditorOptionsPage (), mp_root (root), mp_pcell_parameters (0) + : QWidget (), EditorOptionsPage (), mp_root (root), mp_pcell_parameters (0), mp_placeholder_label (0) { mp_ui = new Ui::EditorOptionsInstPCellParam (); mp_ui->setupUi (this); @@ -800,24 +800,6 @@ EditorOptionsInstPCellParam::title () const return tl::to_string (QObject::tr ("PCell")); } -/* @@@ -void -EditorOptionsInstPCellParam::library_changed (int) -{ -BEGIN_PROTECTED - update_pcell_parameters (); -END_PROTECTED -} - -void -EditorOptionsInstPCellParam::cell_name_changed (const QString &) -{ -BEGIN_PROTECTED - update_pcell_parameters (); -END_PROTECTED -} -*/ - void EditorOptionsInstPCellParam::apply (lay::Plugin *root) { @@ -926,24 +908,24 @@ void EditorOptionsInstPCellParam::update_pcell_parameters (const std::vector ¶meters) { db::Layout *layout = 0; - -/* @@@ - if (m_cv_index < 0 || !lay::LayoutView::current () || !lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) { - mp_ui->param_tab_widget->setTabEnabled (1, false); - return; - } -@@@*/ + lay::LayoutView *view = lay::LayoutView::current (); // find the layout the cell has to be looked up: that is either the layout of the current instance or // the library selected db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name); if (lib) { layout = &lib->layout (); - } else { - layout = &lay::LayoutView::current ()->cellview (m_cv_index)->layout (); + } else if (view) { + const lay::CellView &cv = view->cellview (m_cv_index); + if (cv.is_valid ()) { + layout = &cv->layout (); + } } - std::pair pc = layout->pcell_by_name (tl::to_string (m_cell_name).c_str ()); + std::pair pc (false, 0); + if (layout) { + pc = layout->pcell_by_name (tl::to_string (m_cell_name).c_str ()); + } PCellParametersPage::State pcp_state; @@ -954,19 +936,28 @@ EditorOptionsInstPCellParam::update_pcell_parameters (const std::vector deleteLater (); } + if (mp_placeholder_label) { + mp_placeholder_label->hide (); + mp_placeholder_label->deleteLater (); + } + mp_pcell_parameters = 0; + mp_placeholder_label = 0; - if (pc.first && layout->pcell_declaration (pc.second)) { + if (pc.first && layout->pcell_declaration (pc.second) && view && view->cellview (m_cv_index).is_valid ()) { - // @@@mp_ui->param_tab_widget->setTabEnabled (1, true); - lay::LayoutView *view = lay::LayoutView::current (); mp_pcell_parameters = new PCellParametersPage (this, &view->cellview (m_cv_index)->layout (), view, m_cv_index, layout->pcell_declaration (pc.second), parameters); this->layout ()->addWidget (mp_pcell_parameters); mp_pcell_parameters->set_state (pcp_state); } else { - // @@@mp_ui->param_tab_widget->setTabEnabled (1, false); + + mp_placeholder_label = new QLabel (this); + mp_placeholder_label->setText (tr ("Not a PCell")); + mp_placeholder_label->setAlignment (Qt::AlignHCenter | Qt::AlignVCenter); + this->layout ()->addWidget (mp_placeholder_label); + } } diff --git a/src/edt/edt/edtEditorOptionsPages.h b/src/edt/edt/edtEditorOptionsPages.h index 723a42603..afa18e11d 100644 --- a/src/edt/edt/edtEditorOptionsPages.h +++ b/src/edt/edt/edtEditorOptionsPages.h @@ -31,6 +31,7 @@ #include class QTabWidget; +class QLabel; namespace Ui { @@ -255,6 +256,7 @@ private: Ui::EditorOptionsInstPCellParam *mp_ui; lay::Dispatcher *mp_root; edt::PCellParametersPage *mp_pcell_parameters; + QLabel *mp_placeholder_label; int m_cv_index; std::string m_lib_name, m_cell_name; From 1ea29dd9f3c9c66e4523ed97a09d0b2d525a789d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 11 Aug 2020 09:44:59 +0200 Subject: [PATCH 046/113] WIP: avoid a segfault when closing tab while dragging an instance. --- src/edt/edt/edtEditorOptionsPages.cc | 9 +++++++-- src/edt/edt/edtPlugin.cc | 3 +-- src/lay/lay/layMainWindow.cc | 15 +++++++++++++-- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index ede46467e..c9397fe59 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -579,13 +579,18 @@ void EditorOptionsInst::update_cell_edits () { db::Layout *layout = 0; + lay::LayoutView *view = lay::LayoutView::current (); // find the layout the cell has to be looked up: that is either the layout of the current instance or // the library selected if (mp_ui->lib_cbx->current_library ()) { layout = &mp_ui->lib_cbx->current_library ()->layout (); - } else { - layout = &lay::LayoutView::current ()->cellview (m_cv_index)->layout (); + } else if (view && view->cellview (m_cv_index).is_valid ()) { + layout = &view->cellview (m_cv_index)->layout (); + } + + if (! layout) { + return; } std::pair pc = layout->pcell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ()); diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 8e579a759..d1e1c970f 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -403,11 +403,10 @@ activate_service (lay::LayoutView *view, const lay::PluginDeclaration *pd, bool return; } - // @@@ TODO: this is very inefficient as each "activate" will regenerate the tabs + // TODO: this is very inefficient as each "activate" will regenerate the tabs for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { (*op)->activate (((*op)->plugin_declaration () == pd || ! (*op)->plugin_declaration ()) && active); } - } void diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 6ff3d246c..2e5b07755 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -537,12 +537,11 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha m_libs_visible = true; mp_eo_dock_widget = new QDockWidget (QObject::tr ("Editor Options"), this); - mp_eo_dock_widget->hide (); mp_eo_dock_widget->setObjectName (QString::fromUtf8 ("eo_dock_widget")); mp_eo_stack = new ControlWidgetStack (mp_eo_dock_widget, "eo_stack"); mp_eo_dock_widget->setWidget (mp_eo_stack); connect (mp_eo_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); - m_eo_visible = false; + m_eo_visible = true; mp_bm_dock_widget = new QDockWidget (QObject::tr ("Bookmarks"), this); mp_bm_dock_widget->setObjectName (QString::fromUtf8 ("bookmarks_dock_widget")); @@ -939,6 +938,7 @@ MainWindow::show () void MainWindow::close_all () { + cancel (); mp_layer_toolbox->set_view (0); // try a smooth shutdown of the current view @@ -1151,9 +1151,18 @@ MainWindow::config_finalize () // Not set the window state: this ensures we have handled cfg_window_geometry // before we restore the state if (! m_config_window_state.empty ()) { + QByteArray state = QByteArray::fromBase64 (m_config_window_state.c_str ()); m_config_window_state.clear (); + + bool eo_visible = m_eo_visible; + restoreState (state); + + // Keep the editor options visibility state + m_eo_visible = eo_visible; + show_dock_widget (mp_eo_dock_widget, m_eo_visible); + } // Update the default grids menu if necessary @@ -3025,6 +3034,8 @@ MainWindow::close_view (int index) { if (view (index)) { + cancel (); + // this suppresses view_selected events that would otherwise be created bool f = m_disable_tab_selected; m_disable_tab_selected = true; From 913a266cd74f8a01d5a1add90e78933fdb0888b3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 11 Aug 2020 17:34:52 +0200 Subject: [PATCH 047/113] WIP: styling. --- src/edt/edt/EditorOptionsPath.ui | 23 ++-- src/edt/edt/EditorOptionsText.ui | 185 ++++++++++++++++--------------- 2 files changed, 111 insertions(+), 97 deletions(-) diff --git a/src/edt/edt/EditorOptionsPath.ui b/src/edt/edt/EditorOptionsPath.ui index cf8a30249..0b2769df6 100644 --- a/src/edt/edt/EditorOptionsPath.ui +++ b/src/edt/edt/EditorOptionsPath.ui @@ -18,16 +18,16 @@ 6 - 9 + 4 - 9 + 4 - 9 + 4 - 9 + 4 @@ -106,20 +106,23 @@ - 9 + 4 - 9 + 4 - 9 + 4 - 9 + 4 - + 6 + + 2 + @@ -128,7 +131,7 @@ 21 - 81 + 10 diff --git a/src/edt/edt/EditorOptionsText.ui b/src/edt/edt/EditorOptionsText.ui index 3008d9de8..bd19c5e98 100644 --- a/src/edt/edt/EditorOptionsText.ui +++ b/src/edt/edt/EditorOptionsText.ui @@ -1,7 +1,8 @@ - + + EditorOptionsText - - + + 0 0 @@ -9,194 +10,204 @@ 243 - + Form - - - 9 + + + 2 - - 6 + + 4 + + + 4 + + + 4 + + + 4 - - + + QFrame::NoFrame - + QFrame::Raised - - - 9 + + + 4 - + + 4 + + + 4 + + + 4 + + 6 - - - + + 2 + + + + Alignment - - - - - 0 - 5 + + + + 0 0 - + h = - - - + + + Text size - - - - - 7 - 0 + + + + 0 0 - - - - - 0 - 5 + + + + 0 0 - + Text - - - - Hint: orientation, alignments and size cannot be saved to OASIS files -Enable a vector font and text scaling in the setup dialog -to show text objects scaled and rotated + + + + Hint: orientation, alignments and size cannot be saved to OASIS files. Enable a vector font and text scaling in the setup dialog to show text objects scaled and rotated. + + + true - - + + - - - - (Leave empty for default) + + + + (empty for default) - - - - - 5 - 0 + + + + 1 0 - + (Default) - + Top - + Center - + Bottom - - - - - 0 - 5 + + + + 0 0 - + v = - - - - - 5 - 0 + + + + 1 0 - + (Default) - + Left - + Center - + Right - + - + Qt::Vertical - + QSizePolicy::Fixed - + 20 5 @@ -209,10 +220,10 @@ to show text objects scaled and rotated
- + Qt::Vertical - + 20 40 From 0ff1a472f5a60475a0af7ddb4f4b5caefed8a787 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 11 Aug 2020 17:47:33 +0200 Subject: [PATCH 048/113] WIP: don't accept cell drops from wrong technology --- src/edt/edt/edtServiceImpl.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index 471c9de8e..5f854d0e4 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -1138,9 +1138,18 @@ InstService::drag_enter_event (const db::DPoint &p, const lay::DragDropDataBase // configure from the drag/drop data if (cd->library ()) { + + // Reject drag & drop if the target technology does not match + if (cd->library ()->for_technologies () && view ()->cellview (view ()->active_cellview_index ()).is_valid ()) { + if (! cd->library ()->is_for_technology (view ()->cellview (view ()->active_cellview_index ())->tech_name ())) { + return false; + } + } + if (m_lib_name != cd->library ()->get_name ()) { m_lib_name = cd->library ()->get_name (); } + } else { m_lib_name.clear (); } From b8c33f1b59500151fd4acfa52ca680a3b2ef6c2e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 11 Aug 2020 18:44:19 +0200 Subject: [PATCH 049/113] WIP: no-apply for instance properties --- src/edt/edt/EditorOptionsInst.ui | 3 + src/edt/edt/edtEditorOptionsPages.cc | 200 ++++++++++++++++----------- src/edt/edt/edtEditorOptionsPages.h | 46 +++--- src/edt/edt/edtPlugin.cc | 17 ++- 4 files changed, 154 insertions(+), 112 deletions(-) diff --git a/src/edt/edt/EditorOptionsInst.ui b/src/edt/edt/EditorOptionsInst.ui index d7529a797..53002f9de 100644 --- a/src/edt/edt/EditorOptionsInst.ui +++ b/src/edt/edt/EditorOptionsInst.ui @@ -174,6 +174,9 @@ 0 + + false +
diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index c9397fe59..784f36c99 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -51,8 +51,8 @@ namespace edt // ------------------------------------------------------------------ // EditorOptionsPage implementation -EditorOptionsPage::EditorOptionsPage () - : mp_owner (0), m_active (true), mp_plugin_declaration (0) +EditorOptionsPage::EditorOptionsPage (lay::Dispatcher *dispatcher) + : mp_owner (0), m_active (true), mp_plugin_declaration (0), mp_dispatcher (dispatcher) { // nothing yet .. } @@ -230,11 +230,43 @@ BEGIN_PROTECTED END_PROTECTED_W (this) } +// ------------------------------------------------------------------ +// Indicates an error on a line edit + +static void indicate_error (QLineEdit *le, bool error) +{ + // by the way, update the foreground color of the cell edit box as well (red, if not valid) + QPalette pl = le->palette (); + if (error) { + pl.setColor (QPalette::Active, QPalette::Text, Qt::red); + pl.setColor (QPalette::Active, QPalette::Base, QColor (Qt::red).lighter (180)); + } else { + QWidget *pw = dynamic_cast (le->parent ()); + tl_assert (pw != 0); + pl.setColor (QPalette::Active, QPalette::Text, pw->palette ().color (QPalette::Text)); + pl.setColor (QPalette::Active, QPalette::Base, pw->palette ().color (QPalette::Base)); + } + le->setPalette (pl); +} + +template +static void configure_from_line_edit (lay::Dispatcher *dispatcher, QLineEdit *le, const std::string &cfg_name) +{ + try { + Value value = Value (0); + tl::from_string (tl::to_string (le->text ()), value); + dispatcher->config_set (cfg_name, tl::to_string (value)); + indicate_error (le, false); + } catch (...) { + indicate_error (le, true); + } +} + // ------------------------------------------------------------------ // EditorOptionsGeneric implementation -EditorOptionsGeneric::EditorOptionsGeneric () - : QWidget (), EditorOptionsPage () +EditorOptionsGeneric::EditorOptionsGeneric (lay::Dispatcher *dispatcher) + : QWidget (), EditorOptionsPage (dispatcher) { mp_ui = new Ui::EditorOptionsGeneric (); mp_ui->setupUi (this); @@ -255,7 +287,7 @@ EditorOptionsGeneric::title () const } void -EditorOptionsGeneric::apply (lay::Plugin *root) +EditorOptionsGeneric::apply (lay::Dispatcher *root) { // Edit grid @@ -300,7 +332,7 @@ EditorOptionsGeneric::show_shapes_changed () } void -EditorOptionsGeneric::setup (lay::Plugin *root) +EditorOptionsGeneric::setup (lay::Dispatcher *root) { // Edit grid @@ -355,8 +387,8 @@ EditorOptionsGeneric::setup (lay::Plugin *root) // ------------------------------------------------------------------ // EditorOptionsText implementation -EditorOptionsText::EditorOptionsText () - : QWidget (), EditorOptionsPage () +EditorOptionsText::EditorOptionsText (lay::Dispatcher *dispatcher) + : QWidget (), EditorOptionsPage (dispatcher) { mp_ui = new Ui::EditorOptionsText (); mp_ui->setupUi (this); @@ -375,7 +407,7 @@ EditorOptionsText::title () const } void -EditorOptionsText::apply (lay::Plugin *root) +EditorOptionsText::apply (lay::Dispatcher *root) { // Text string root->config_set (cfg_edit_text_string, tl::unescape_string (tl::to_string (mp_ui->text_le->text ()))); @@ -399,7 +431,7 @@ EditorOptionsText::apply (lay::Plugin *root) } void -EditorOptionsText::setup (lay::Plugin *root) +EditorOptionsText::setup (lay::Dispatcher *root) { // Text string std::string s; @@ -428,8 +460,8 @@ EditorOptionsText::setup (lay::Plugin *root) // ------------------------------------------------------------------ // EditorOptionsPath implementation -EditorOptionsPath::EditorOptionsPath () - : QWidget (), EditorOptionsPage () +EditorOptionsPath::EditorOptionsPath (lay::Dispatcher *dispatcher) + : QWidget (), EditorOptionsPage (dispatcher) { mp_ui = new Ui::EditorOptionsPath (); mp_ui->setupUi (this); @@ -457,7 +489,7 @@ EditorOptionsPath::type_changed (int type) } void -EditorOptionsPath::apply (lay::Plugin *root) +EditorOptionsPath::apply (lay::Dispatcher *root) { // width @@ -494,7 +526,7 @@ EditorOptionsPath::apply (lay::Plugin *root) } void -EditorOptionsPath::setup (lay::Plugin *root) +EditorOptionsPath::setup (lay::Dispatcher *root) { // width @@ -527,16 +559,27 @@ EditorOptionsPath::setup (lay::Plugin *root) // ------------------------------------------------------------------ // EditorOptionsInst implementation -EditorOptionsInst::EditorOptionsInst (lay::Dispatcher *root) - : QWidget (), EditorOptionsPage (), mp_root (root) +EditorOptionsInst::EditorOptionsInst (lay::Dispatcher *dispatcher) + : QWidget (), EditorOptionsPage (dispatcher) { mp_ui = new Ui::EditorOptionsInst (); mp_ui->setupUi (this); connect (mp_ui->array_grp, SIGNAL (clicked ()), this, SLOT (array_changed ())); connect (mp_ui->browse_pb, SIGNAL (clicked ()), this, SLOT (browse_cell ())); - connect (mp_ui->lib_cbx, SIGNAL (currentIndexChanged (int)), this, SLOT (library_changed (int))); - connect (mp_ui->cell_le, SIGNAL (textChanged (const QString &)), this, SLOT (cell_name_changed (const QString &))); + connect (mp_ui->lib_cbx, SIGNAL (currentIndexChanged (int)), this, SLOT (library_changed ())); + connect (mp_ui->cell_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->angle_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->scale_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->rows_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->row_x_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->row_y_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->columns_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->column_x_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->column_y_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->mirror_cbx, SIGNAL (clicked ()), this, SLOT (edited ())); + connect (mp_ui->array_grp, SIGNAL (clicked ()), this, SLOT (edited ())); + connect (mp_ui->place_origin_cb, SIGNAL (clicked ()), this, SLOT (edited ())); m_cv_index = -1; } @@ -554,25 +597,17 @@ EditorOptionsInst::title () const } void -EditorOptionsInst::library_changed (int) +EditorOptionsInst::library_changed () { update_cell_edits (); -/* @@@ -BEGIN_PROTECTED - update_pcell_parameters (); -END_PROTECTED -@@@*/ + edited (); } void -EditorOptionsInst::cell_name_changed (const QString &) +EditorOptionsInst::cell_name_changed () { update_cell_edits (); -/* @@@ -BEGIN_PROTECTED - update_pcell_parameters (); -END_PROTECTED -@@@*/ + edited (); } void @@ -597,15 +632,7 @@ EditorOptionsInst::update_cell_edits () std::pair cc = layout->cell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ()); // by the way, update the foreground color of the cell edit box as well (red, if not valid) - QPalette pl = mp_ui->cell_le->palette (); - if (! pc.first && ! cc.first) { - pl.setColor (QPalette::Text, Qt::red); - pl.setColor (QPalette::Base, QColor (Qt::red).lighter (180)); - } else { - pl.setColor (QPalette::Text, palette ().color (QPalette::Text)); - pl.setColor (QPalette::Base, palette ().color (QPalette::Base)); - } - mp_ui->cell_le->setPalette (pl); + indicate_error (mp_ui->cell_le, ! pc.first && ! cc.first); } void @@ -649,7 +676,7 @@ BEGIN_PROTECTED } else if (layout->is_valid_cell_index (form.selected_cell_index ())) { mp_ui->cell_le->setText (tl::to_qstring (layout->cell_name (form.selected_cell_index ()))); } - // @@@@update_pcell_parameters (); + edited (); } } @@ -657,6 +684,12 @@ BEGIN_PROTECTED END_PROTECTED } +void +EditorOptionsInst::edited () +{ + apply (dispatcher ()); +} + void EditorOptionsInst::array_changed () { @@ -667,15 +700,16 @@ EditorOptionsInst::array_changed () mp_ui->columns_le->setEnabled (array); mp_ui->column_x_le->setEnabled (array); mp_ui->column_y_le->setEnabled (array); + edited (); } void -EditorOptionsInst::apply (lay::Plugin *root) +EditorOptionsInst::apply (lay::Dispatcher *root) { // cell name root->config_set (cfg_edit_inst_cell_name, tl::to_string (mp_ui->cell_le->text ())); - // cell name + // lib name if (mp_ui->lib_cbx->current_library ()) { root->config_set (cfg_edit_inst_lib_name, mp_ui->lib_cbx->current_library ()->get_name ()); } else { @@ -683,36 +717,23 @@ EditorOptionsInst::apply (lay::Plugin *root) } // rotation, scaling - double angle = 0.0; - tl::from_string (tl::to_string (mp_ui->angle_le->text ()), angle); - root->config_set (cfg_edit_inst_angle, tl::to_string (angle)); + configure_from_line_edit (root, mp_ui->angle_le, cfg_edit_inst_angle); bool mirror = mp_ui->mirror_cbx->isChecked (); root->config_set (cfg_edit_inst_mirror, tl::to_string (mirror)); - double scale = 1.0; - tl::from_string (tl::to_string (mp_ui->scale_le->text ()), scale); - root->config_set (cfg_edit_inst_scale, tl::to_string (scale)); + configure_from_line_edit (root, mp_ui->scale_le, cfg_edit_inst_scale); // array bool array = mp_ui->array_grp->isChecked (); root->config_set (cfg_edit_inst_array, tl::to_string (array)); - int rows = 1, columns = 1; - double row_x = 0.0, row_y = 0.0, column_x = 0.0, column_y = 0.0; - tl::from_string (tl::to_string (mp_ui->rows_le->text ()), rows); - tl::from_string (tl::to_string (mp_ui->row_x_le->text ()), row_x); - tl::from_string (tl::to_string (mp_ui->row_y_le->text ()), row_y); - tl::from_string (tl::to_string (mp_ui->columns_le->text ()), columns); - tl::from_string (tl::to_string (mp_ui->column_x_le->text ()), column_x); - tl::from_string (tl::to_string (mp_ui->column_y_le->text ()), column_y); - - root->config_set (cfg_edit_inst_rows, tl::to_string (rows)); - root->config_set (cfg_edit_inst_row_x, tl::to_string (row_x)); - root->config_set (cfg_edit_inst_row_y, tl::to_string (row_y)); - root->config_set (cfg_edit_inst_columns, tl::to_string (columns)); - root->config_set (cfg_edit_inst_column_x, tl::to_string (column_x)); - root->config_set (cfg_edit_inst_column_y, tl::to_string (column_y)); + configure_from_line_edit (root, mp_ui->rows_le, cfg_edit_inst_rows); + configure_from_line_edit (root, mp_ui->row_x_le, cfg_edit_inst_row_x); + configure_from_line_edit (root, mp_ui->row_y_le, cfg_edit_inst_row_y); + configure_from_line_edit (root, mp_ui->columns_le, cfg_edit_inst_columns); + configure_from_line_edit (root, mp_ui->column_x_le, cfg_edit_inst_column_x); + configure_from_line_edit (root, mp_ui->column_y_le, cfg_edit_inst_column_y); // place origin of cell flag bool place_origin = mp_ui->place_origin_cb->isChecked (); @@ -720,29 +741,42 @@ EditorOptionsInst::apply (lay::Plugin *root) } void -EditorOptionsInst::setup (lay::Plugin *root) +EditorOptionsInst::setup (lay::Dispatcher *root) { m_cv_index = -1; if (lay::LayoutView::current ()) { m_cv_index = lay::LayoutView::current ()->active_cellview_index (); } - mp_ui->lib_cbx->update_list (); - if (m_cv_index >= 0 && lay::LayoutView::current () && lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) { - mp_ui->lib_cbx->set_technology_filter (lay::LayoutView::current ()->cellview (m_cv_index)->tech_name (), true); - } else { - mp_ui->lib_cbx->set_technology_filter (std::string (), false); + + try { + + mp_ui->lib_cbx->blockSignals (true); + + mp_ui->lib_cbx->update_list (); + if (m_cv_index >= 0 && lay::LayoutView::current () && lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) { + mp_ui->lib_cbx->set_technology_filter (lay::LayoutView::current ()->cellview (m_cv_index)->tech_name (), true); + } else { + mp_ui->lib_cbx->set_technology_filter (std::string (), false); + } + + // cell name + std::string s; + root->config_get (cfg_edit_inst_cell_name, s); + mp_ui->cell_le->setText (tl::to_qstring (s)); + + // library + std::string l; + root->config_get (cfg_edit_inst_lib_name, l); + mp_ui->lib_cbx->set_current_library (db::LibraryManager::instance ().lib_ptr_by_name (l)); + + mp_ui->lib_cbx->blockSignals (false); + update_cell_edits (); + + } catch (...) { + mp_ui->lib_cbx->blockSignals (false); + throw; } - // cell name - std::string s; - root->config_get (cfg_edit_inst_cell_name, s); - mp_ui->cell_le->setText (tl::to_qstring (s)); - - // library - std::string l; - root->config_get (cfg_edit_inst_lib_name, l); - mp_ui->lib_cbx->set_current_library (db::LibraryManager::instance ().lib_ptr_by_name (l)); - // rotation, scaling double angle = 0.0; root->config_get (cfg_edit_inst_angle, angle); @@ -786,8 +820,8 @@ EditorOptionsInst::setup (lay::Plugin *root) // ------------------------------------------------------------------ // EditorOptionsInstPCellParam implementation -EditorOptionsInstPCellParam::EditorOptionsInstPCellParam (lay::Dispatcher *root) - : QWidget (), EditorOptionsPage (), mp_root (root), mp_pcell_parameters (0), mp_placeholder_label (0) +EditorOptionsInstPCellParam::EditorOptionsInstPCellParam (lay::Dispatcher *dispatcher) + : QWidget (), EditorOptionsPage (dispatcher), mp_pcell_parameters (0), mp_placeholder_label (0) { mp_ui = new Ui::EditorOptionsInstPCellParam (); mp_ui->setupUi (this); @@ -806,7 +840,7 @@ EditorOptionsInstPCellParam::title () const } void -EditorOptionsInstPCellParam::apply (lay::Plugin *root) +EditorOptionsInstPCellParam::apply (lay::Dispatcher *root) { // pcell parameters std::string param; @@ -833,7 +867,7 @@ EditorOptionsInstPCellParam::apply (lay::Plugin *root) } void -EditorOptionsInstPCellParam::setup (lay::Plugin *root) +EditorOptionsInstPCellParam::setup (lay::Dispatcher *root) { m_cv_index = -1; if (lay::LayoutView::current ()) { diff --git a/src/edt/edt/edtEditorOptionsPages.h b/src/edt/edt/edtEditorOptionsPages.h index afa18e11d..c2eb527a8 100644 --- a/src/edt/edt/edtEditorOptionsPages.h +++ b/src/edt/edt/edtEditorOptionsPages.h @@ -65,14 +65,14 @@ class EditorOptionsPages; class EditorOptionsPage { public: - EditorOptionsPage (); + EditorOptionsPage (lay::Dispatcher *dispatcher); virtual ~EditorOptionsPage (); virtual QWidget *q_frame () = 0; virtual std::string title () const = 0; virtual int order () const = 0; - virtual void apply (lay::Plugin *root) = 0; - virtual void setup (lay::Plugin *root) = 0; + virtual void apply (lay::Dispatcher *root) = 0; + virtual void setup (lay::Dispatcher *root) = 0; bool active () const { return m_active; } void activate (bool active); @@ -81,10 +81,17 @@ public: const lay::PluginDeclaration *plugin_declaration () const { return mp_plugin_declaration; } void set_plugin_declaration (const lay::PluginDeclaration *pd) { mp_plugin_declaration = pd; } +protected: + lay::Dispatcher *dispatcher () const + { + return mp_dispatcher; + } + private: EditorOptionsPages *mp_owner; bool m_active; const lay::PluginDeclaration *mp_plugin_declaration; + lay::Dispatcher *mp_dispatcher; }; /** @@ -129,15 +136,15 @@ class EditorOptionsGeneric Q_OBJECT public: - EditorOptionsGeneric (); + EditorOptionsGeneric (lay::Dispatcher *dispatcher); ~EditorOptionsGeneric (); virtual QWidget *q_frame () { return this; } virtual std::string title () const; virtual int order () const { return 0; } - void apply (lay::Plugin *root); - void setup (lay::Plugin *root); + void apply (lay::Dispatcher *root); + void setup (lay::Dispatcher *root); public slots: void grid_changed (int); @@ -154,15 +161,15 @@ class EditorOptionsText : public QWidget, public EditorOptionsPage { public: - EditorOptionsText (); + EditorOptionsText (lay::Dispatcher *dispatcher); ~EditorOptionsText (); virtual QWidget *q_frame () { return this; } virtual std::string title () const; virtual int order () const { return 10; } - void apply (lay::Plugin *root); - void setup (lay::Plugin *root); + void apply (lay::Dispatcher *root); + void setup (lay::Dispatcher *root); private: Ui::EditorOptionsText *mp_ui; @@ -177,15 +184,15 @@ class EditorOptionsPath Q_OBJECT public: - EditorOptionsPath (); + EditorOptionsPath (lay::Dispatcher *dispatcher); ~EditorOptionsPath (); virtual QWidget *q_frame () { return this; } virtual std::string title () const; virtual int order () const { return 30; } - void apply (lay::Plugin *root); - void setup (lay::Plugin *root); + void apply (lay::Dispatcher *root); + void setup (lay::Dispatcher *root); public slots: void type_changed (int); @@ -210,20 +217,20 @@ public: virtual std::string title () const; virtual int order () const { return 20; } - void apply (lay::Plugin *root); - void setup (lay::Plugin *root); + void apply (lay::Dispatcher *root); + void setup (lay::Dispatcher *root); public slots: void array_changed (); void browse_cell (); void update_pcell_parameters (); - void library_changed (int index); - void cell_name_changed (const QString &s); + void library_changed (); + void cell_name_changed (); void update_cell_edits (); + void edited (); private: Ui::EditorOptionsInst *mp_ui; - lay::Dispatcher *mp_root; edt::PCellParametersPage *mp_pcell_parameters; int m_cv_index; @@ -246,15 +253,14 @@ public: virtual std::string title () const; virtual int order () const { return 21; } - void apply (lay::Plugin *root); - void setup (lay::Plugin *root); + void apply (lay::Dispatcher *root); + void setup (lay::Dispatcher *root); public slots: void update_pcell_parameters (); private: Ui::EditorOptionsInstPCellParam *mp_ui; - lay::Dispatcher *mp_root; edt::PCellParametersPage *mp_pcell_parameters; QLabel *mp_placeholder_label; int m_cv_index; diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index d1e1c970f..a190a8272 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -46,9 +46,9 @@ void get_text_options (std::vector < std::pair > &opti } static -void get_text_editor_options_pages (std::vector &ret, lay::Dispatcher *) +void get_text_editor_options_pages (std::vector &ret, lay::Dispatcher *dispatcher) { - ret.push_back (new edt::EditorOptionsText ()); + ret.push_back (new edt::EditorOptionsText (dispatcher)); } static @@ -61,9 +61,9 @@ void get_path_options (std::vector < std::pair > &opti } static -void get_path_editor_options_pages (std::vector &ret, lay::Dispatcher *) +void get_path_editor_options_pages (std::vector &ret, lay::Dispatcher *dispatcher) { - ret.push_back (new EditorOptionsPath ()); + ret.push_back (new EditorOptionsPath (dispatcher)); } static @@ -86,10 +86,10 @@ void get_inst_options (std::vector < std::pair > &opti } static -void get_inst_editor_options_pages (std::vector &ret, lay::Dispatcher *root) +void get_inst_editor_options_pages (std::vector &ret, lay::Dispatcher *dispatcher) { - ret.push_back (new EditorOptionsInstPCellParam (root)); - ret.push_back (new EditorOptionsInst (root)); + ret.push_back (new EditorOptionsInstPCellParam (dispatcher)); + ret.push_back (new EditorOptionsInst (dispatcher)); } template @@ -356,8 +356,7 @@ show_editor_options_page (lay::LayoutView *view) } std::vector prop_dialog_pages; - EditorOptionsGeneric *generic_opt = new EditorOptionsGeneric (); - generic_opt->setup (view->dispatcher ()); + EditorOptionsGeneric *generic_opt = new EditorOptionsGeneric (view->dispatcher ()); prop_dialog_pages.push_back (generic_opt); for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { From e12746526fc14ede13fbf4781f446eea11d9c07c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 13 Aug 2020 01:20:14 +0200 Subject: [PATCH 050/113] Interactive manipulation of PCell parameters on editoptions page. --- src/edt/edt/edtEditorOptionsPages.cc | 67 +++++-- src/edt/edt/edtEditorOptionsPages.h | 25 ++- src/edt/edt/edtInstPropertiesPage.cc | 3 +- src/edt/edt/edtPCellParametersPage.cc | 177 +++++++++++++----- src/edt/edt/edtPCellParametersPage.h | 47 +++-- src/edt/edt/edtPlugin.cc | 1 + src/lay/lay/layMainWindow.cc | 28 ++- .../laybasic/layLayoutViewFunctions.cc | 4 +- src/laybasic/laybasic/layWidgets.cc | 4 +- 9 files changed, 257 insertions(+), 99 deletions(-) diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index 784f36c99..ad2339f57 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -52,7 +52,7 @@ namespace edt // EditorOptionsPage implementation EditorOptionsPage::EditorOptionsPage (lay::Dispatcher *dispatcher) - : mp_owner (0), m_active (true), mp_plugin_declaration (0), mp_dispatcher (dispatcher) + : QWidget (0), mp_owner (0), m_active (true), mp_plugin_declaration (0), mp_dispatcher (dispatcher) { // nothing yet .. } @@ -131,6 +131,15 @@ EditorOptionsPages::~EditorOptionsPages () } } +void +EditorOptionsPages::focusInEvent (QFocusEvent * /*event*/) +{ + // Sends the focus to the current page's last focus owner + if (mp_pages->currentWidget () && mp_pages->currentWidget ()->focusWidget ()) { + mp_pages->currentWidget ()->focusWidget ()->setFocus (); + } +} + void EditorOptionsPages::unregister_page (edt::EditorOptionsPage *page) { @@ -266,7 +275,7 @@ static void configure_from_line_edit (lay::Dispatcher *dispatcher, QLineEdit *le // EditorOptionsGeneric implementation EditorOptionsGeneric::EditorOptionsGeneric (lay::Dispatcher *dispatcher) - : QWidget (), EditorOptionsPage (dispatcher) + : EditorOptionsPage (dispatcher) { mp_ui = new Ui::EditorOptionsGeneric (); mp_ui->setupUi (this); @@ -388,7 +397,7 @@ EditorOptionsGeneric::setup (lay::Dispatcher *root) // EditorOptionsText implementation EditorOptionsText::EditorOptionsText (lay::Dispatcher *dispatcher) - : QWidget (), EditorOptionsPage (dispatcher) + : EditorOptionsPage (dispatcher) { mp_ui = new Ui::EditorOptionsText (); mp_ui->setupUi (this); @@ -461,7 +470,7 @@ EditorOptionsText::setup (lay::Dispatcher *root) // EditorOptionsPath implementation EditorOptionsPath::EditorOptionsPath (lay::Dispatcher *dispatcher) - : QWidget (), EditorOptionsPage (dispatcher) + : EditorOptionsPage (dispatcher) { mp_ui = new Ui::EditorOptionsPath (); mp_ui->setupUi (this); @@ -560,7 +569,7 @@ EditorOptionsPath::setup (lay::Dispatcher *root) // EditorOptionsInst implementation EditorOptionsInst::EditorOptionsInst (lay::Dispatcher *dispatcher) - : QWidget (), EditorOptionsPage (dispatcher) + : EditorOptionsPage (dispatcher) { mp_ui = new Ui::EditorOptionsInst (); mp_ui->setupUi (this); @@ -684,12 +693,6 @@ BEGIN_PROTECTED END_PROTECTED } -void -EditorOptionsInst::edited () -{ - apply (dispatcher ()); -} - void EditorOptionsInst::array_changed () { @@ -821,7 +824,7 @@ EditorOptionsInst::setup (lay::Dispatcher *root) // EditorOptionsInstPCellParam implementation EditorOptionsInstPCellParam::EditorOptionsInstPCellParam (lay::Dispatcher *dispatcher) - : QWidget (), EditorOptionsPage (dispatcher), mp_pcell_parameters (0), mp_placeholder_label (0) + : EditorOptionsPage (dispatcher), mp_pcell_parameters (0), mp_placeholder_label (0) { mp_ui = new Ui::EditorOptionsInstPCellParam (); mp_ui->setupUi (this); @@ -853,17 +856,21 @@ EditorOptionsInstPCellParam::apply (lay::Dispatcher *root) layout = &lay::LayoutView::current ()->cellview (m_cv_index)->layout (); } + bool ok = true; + if (layout && mp_pcell_parameters) { std::pair pc = layout->pcell_by_name (tl::to_string (m_cell_name).c_str ()); if (pc.first) { const db::PCellDeclaration *pc_decl = layout->pcell_declaration (pc.second); if (pc_decl) { - param = pcell_parameters_to_string (pc_decl->named_parameters (mp_pcell_parameters->get_parameters ())); + param = pcell_parameters_to_string (pc_decl->named_parameters (mp_pcell_parameters->get_parameters (&ok))); } } } - root->config_set (cfg_edit_inst_pcell_parameters, param); + if (ok) { + root->config_set (cfg_edit_inst_pcell_parameters, param); + } } void @@ -874,11 +881,24 @@ EditorOptionsInstPCellParam::setup (lay::Dispatcher *root) m_cv_index = lay::LayoutView::current ()->active_cellview_index (); } + bool needs_update = (mp_pcell_parameters == 0); + // cell name - root->config_get (cfg_edit_inst_cell_name, m_cell_name); + std::string cn; + root->config_get (cfg_edit_inst_cell_name, cn); + if (cn != m_cell_name) { + m_cell_name = cn; + needs_update = true; + } // library - root->config_get (cfg_edit_inst_lib_name, m_lib_name); + std::string ln; + root->config_get (cfg_edit_inst_lib_name, ln); + if (ln != m_lib_name) { + m_lib_name = ln; + needs_update = true; + } + db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name); // pcell parameters @@ -932,8 +952,17 @@ EditorOptionsInstPCellParam::setup (lay::Dispatcher *root) } + if (! needs_update) { + bool ok = false; + if (mp_pcell_parameters->get_parameters (&ok) != pv || ! ok) { + needs_update = true; + } + } + try { - update_pcell_parameters (pv); + if (needs_update) { + update_pcell_parameters (pv); + } } catch (...) { } } @@ -985,10 +1014,12 @@ EditorOptionsInstPCellParam::update_pcell_parameters (const std::vector pcell_declaration (pc.second) && view && view->cellview (m_cv_index).is_valid ()) { - mp_pcell_parameters = new PCellParametersPage (this, &view->cellview (m_cv_index)->layout (), view, m_cv_index, layout->pcell_declaration (pc.second), parameters); + mp_pcell_parameters = new PCellParametersPage (this, true /*dense*/); + mp_pcell_parameters->setup (&view->cellview (m_cv_index)->layout (), view, m_cv_index, layout->pcell_declaration (pc.second), parameters); this->layout ()->addWidget (mp_pcell_parameters); mp_pcell_parameters->set_state (pcp_state); + connect (mp_pcell_parameters, SIGNAL (edited ()), this, SLOT (edited ())); } else { diff --git a/src/edt/edt/edtEditorOptionsPages.h b/src/edt/edt/edtEditorOptionsPages.h index c2eb527a8..017299841 100644 --- a/src/edt/edt/edtEditorOptionsPages.h +++ b/src/edt/edt/edtEditorOptionsPages.h @@ -63,7 +63,10 @@ class EditorOptionsPages; * @brief The base class for a object properties page */ class EditorOptionsPage + : public QWidget { +Q_OBJECT + public: EditorOptionsPage (lay::Dispatcher *dispatcher); virtual ~EditorOptionsPage (); @@ -81,6 +84,12 @@ public: const lay::PluginDeclaration *plugin_declaration () const { return mp_plugin_declaration; } void set_plugin_declaration (const lay::PluginDeclaration *pd) { mp_plugin_declaration = pd; } +protected slots: + void edited () + { + apply (dispatcher ()); + } + protected: lay::Dispatcher *dispatcher () const { @@ -108,6 +117,7 @@ public: void unregister_page (edt::EditorOptionsPage *page); void activate_page (edt::EditorOptionsPage *page); + void focusInEvent (QFocusEvent *event); const std::vector &pages () const { @@ -131,7 +141,7 @@ private: * @brief The generic properties page */ class EditorOptionsGeneric - : public QWidget, public EditorOptionsPage + : public EditorOptionsPage { Q_OBJECT @@ -158,7 +168,7 @@ private: * @brief The text properties page */ class EditorOptionsText - : public QWidget, public EditorOptionsPage + : public EditorOptionsPage { public: EditorOptionsText (lay::Dispatcher *dispatcher); @@ -179,7 +189,7 @@ private: * @brief The path properties page */ class EditorOptionsPath - : public QWidget, public EditorOptionsPage + : public EditorOptionsPage { Q_OBJECT @@ -205,7 +215,7 @@ private: * @brief The instance properties page */ class EditorOptionsInst - : public QWidget, public EditorOptionsPage + : public EditorOptionsPage { Q_OBJECT @@ -220,14 +230,13 @@ public: void apply (lay::Dispatcher *root); void setup (lay::Dispatcher *root); -public slots: +private slots: void array_changed (); void browse_cell (); void update_pcell_parameters (); void library_changed (); void cell_name_changed (); void update_cell_edits (); - void edited (); private: Ui::EditorOptionsInst *mp_ui; @@ -241,7 +250,7 @@ private: * @brief The instance properties page (PCell parameters) */ class EditorOptionsInstPCellParam - : public QWidget, public EditorOptionsPage + : public EditorOptionsPage { Q_OBJECT @@ -256,7 +265,7 @@ public: void apply (lay::Dispatcher *root); void setup (lay::Dispatcher *root); -public slots: +private slots: void update_pcell_parameters (); private: diff --git a/src/edt/edt/edtInstPropertiesPage.cc b/src/edt/edt/edtInstPropertiesPage.cc index 6f62c5654..fa791a381 100644 --- a/src/edt/edt/edtInstPropertiesPage.cc +++ b/src/edt/edt/edtInstPropertiesPage.cc @@ -686,7 +686,8 @@ InstPropertiesPage::update_pcell_parameters () mp_pcell_parameters->deleteLater (); } - mp_pcell_parameters = new PCellParametersPage (pcell_tab, &cv->layout (), mp_service->view (), pos->cv_index (), layout->pcell_declaration (pc.second), parameters); + mp_pcell_parameters = new PCellParametersPage (pcell_tab); + mp_pcell_parameters->setup (&cv->layout (), mp_service->view (), pos->cv_index (), layout->pcell_declaration (pc.second), parameters); pcell_tab->layout ()->addWidget (mp_pcell_parameters); } diff --git a/src/edt/edt/edtPCellParametersPage.cc b/src/edt/edt/edtPCellParametersPage.cc index 08ad31c71..670610982 100644 --- a/src/edt/edt/edtPCellParametersPage.cc +++ b/src/edt/edt/edtPCellParametersPage.cc @@ -38,6 +38,24 @@ namespace edt { +static void indicate_error (QLineEdit *le, const tl::Exception *ex) +{ + // by the way, update the foreground color of the cell edit box as well (red, if not valid) + QPalette pl = le->palette (); + if (ex) { + pl.setColor (QPalette::Active, QPalette::Text, Qt::red); + pl.setColor (QPalette::Active, QPalette::Base, QColor (Qt::red).lighter (180)); + le->setToolTip (tl::to_qstring (ex->msg ())); + } else { + QWidget *pw = dynamic_cast (le->parent ()); + tl_assert (pw != 0); + pl.setColor (QPalette::Active, QPalette::Text, pw->palette ().color (QPalette::Text)); + pl.setColor (QPalette::Active, QPalette::Base, pw->palette ().color (QPalette::Base)); + le->setToolTip (QString ()); + } + le->setPalette (pl); +} + static void set_value (const db::PCellParameterDeclaration &p, const db::Layout * /*layout*/, QWidget *widget, const tl::Variant &value) { if (p.get_choices ().empty ()) { @@ -142,15 +160,8 @@ static void set_value (const db::PCellParameterDeclaration &p, const db::Layout } } -PCellParametersPage::PCellParametersPage (QWidget *parent, const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type ¶meters) - : QFrame (parent) -{ - init (); - setup (layout, view, cv_index, pcell_decl, parameters); -} - -PCellParametersPage::PCellParametersPage (QWidget *parent) - : QFrame (parent) +PCellParametersPage::PCellParametersPage (QWidget *parent, bool dense) + : QFrame (parent), m_dense (dense), dm_parameter_changed (this, &PCellParametersPage::do_parameter_changed) { init (); } @@ -175,6 +186,7 @@ PCellParametersPage::init () frame_layout->addWidget (mp_error_icon, 1, 0, 1, 1); mp_error_label = new QLabel (this); + mp_error_label->setWordWrap (true); QPalette palette = mp_error_label->palette (); palette.setColor (QPalette::Foreground, Qt::red); mp_error_label->setPalette (palette); @@ -214,9 +226,11 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int QGridLayout *inner_grid = new QGridLayout (inner_frame); inner_frame->setLayout (inner_grid); - inner_grid->setMargin (4); - inner_grid->setHorizontalSpacing (6); - inner_grid->setVerticalSpacing (2); + if (m_dense) { + inner_grid->setMargin (4); + inner_grid->setHorizontalSpacing (6); + inner_grid->setVerticalSpacing (2); + } QWidget *main_frame = inner_frame; QGridLayout *main_grid = inner_grid; @@ -253,9 +267,11 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int main_grid->addWidget (gb, main_row, 0, 1, 2); inner_grid = new QGridLayout (gb); - inner_grid->setMargin (4); - inner_grid->setHorizontalSpacing (6); - inner_grid->setVerticalSpacing (2); + if (m_dense) { + inner_grid->setMargin (4); + inner_grid->setHorizontalSpacing (6); + inner_grid->setVerticalSpacing (2); + } gb->setLayout (inner_grid); inner_frame = gb; @@ -301,6 +317,7 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int le->setEnabled (! p->is_readonly ()); hb->addWidget (le); le->setMaximumWidth (150); + le->setObjectName (tl::to_qstring (p->get_name ())); m_widgets.push_back (le); QLabel *ul = new QLabel (f); @@ -309,7 +326,7 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int inner_grid->addWidget (f, row, 1); - connect (le, SIGNAL (editingFinished ()), this, SLOT (activated ())); + connect (le, SIGNAL (editingFinished ()), this, SLOT (parameter_changed ())); } break; @@ -319,10 +336,11 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int { QLineEdit *le = new QLineEdit (inner_frame); le->setEnabled (! p->is_readonly ()); + le->setObjectName (tl::to_qstring (p->get_name ())); m_widgets.push_back (le); inner_grid->addWidget (le, row, 1); - connect (le, SIGNAL (editingFinished ()), this, SLOT (activated ())); + connect (le, SIGNAL (editingFinished ()), this, SLOT (parameter_changed ())); } break; @@ -332,19 +350,25 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int ly->setEnabled (! p->is_readonly ()); ly->set_no_layer_available (true); ly->set_view (mp_view, m_cv_index, true /*all layers*/); + ly->setObjectName (tl::to_qstring (p->get_name ())); m_widgets.push_back (ly); inner_grid->addWidget (ly, row, 1); + + connect (ly, SIGNAL (activated (int)), this, SLOT (parameter_changed ())); } break; case db::PCellParameterDeclaration::t_boolean: { QCheckBox *cbx = new QCheckBox (inner_frame); + // this makes the checkbox not stretch over the full width - better when navigating with tab + cbx->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Preferred)); cbx->setEnabled (! p->is_readonly ()); + cbx->setObjectName (tl::to_qstring (p->get_name ())); m_widgets.push_back (cbx); inner_grid->addWidget (cbx, row, 1); - connect (cbx, SIGNAL (stateChanged (int)), this, SLOT (activated ())); + connect (cbx, SIGNAL (stateChanged (int)), this, SLOT (parameter_changed ())); } break; @@ -356,6 +380,7 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int } else { QComboBox *cb = new QComboBox (inner_frame); + cb->setObjectName (tl::to_qstring (p->get_name ())); int i = 0; for (std::vector::const_iterator c = p->get_choices ().begin (); c != p->get_choices ().end (); ++c, ++i) { @@ -366,7 +391,8 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int } } - connect (cb, SIGNAL (activated (int)), this, SLOT (activated ())); + connect (cb, SIGNAL (activated (int)), this, SLOT (parameter_changed ())); + cb->setEnabled (! p->is_readonly ()); cb->setMinimumContentsLength (30); cb->setSizeAdjustPolicy (QComboBox::AdjustToMinimumContentsLengthWithIcon); @@ -387,17 +413,24 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int mp_parameters_area->setWidget (main_frame); main_frame->show (); - // does a first coerce and update - get_parameters (); + // does a first coerce and update. Ignore errors for now. + bool ok = false; + get_parameters (&ok); } PCellParametersPage::State PCellParametersPage::get_state () { State s; + s.valid = true; s.vScrollPosition = mp_parameters_area->verticalScrollBar ()->value (); s.hScrollPosition = mp_parameters_area->horizontalScrollBar ()->value (); + + if (focusWidget ()) { + s.focusWidget = focusWidget ()->objectName (); + } + return s; } @@ -405,29 +438,42 @@ void PCellParametersPage::set_state (const State &s) { if (s.valid) { + mp_parameters_area->verticalScrollBar ()->setValue (s.vScrollPosition); mp_parameters_area->horizontalScrollBar ()->setValue (s.hScrollPosition); + + if (! s.focusWidget.isEmpty ()) { + QWidget *c = findChild (s.focusWidget); + if (c) { + c->setFocus (); + } + } + } } void -PCellParametersPage::activated () +PCellParametersPage::parameter_changed () { - // does a coerce and update - get_parameters (); + dm_parameter_changed (); } -void -PCellParametersPage::clicked () +void +PCellParametersPage::do_parameter_changed () { // does a coerce and update - get_parameters (); + bool ok = false; + get_parameters (&ok); + if (ok) { + emit edited (); + } } std::vector -PCellParametersPage::get_parameters () +PCellParametersPage::get_parameters (bool *ok) { std::vector parameters; + bool edit_error = true; int r = 0; const std::vector &pcp = mp_pcell_decl->parameter_declarations (); @@ -453,9 +499,22 @@ PCellParametersPage::get_parameters () { QLineEdit *le = dynamic_cast (m_widgets [r]); if (le) { - int v = 0; - tl::from_string (tl::to_string (le->text ()), v); - parameters.back () = tl::Variant (v); + + try { + + int v = 0; + tl::from_string (tl::to_string (le->text ()), v); + + parameters.back () = tl::Variant (v); + indicate_error (le, 0); + + } catch (tl::Exception &ex) { + + indicate_error (le, &ex); + edit_error = false; + + } + } } break; @@ -464,9 +523,22 @@ PCellParametersPage::get_parameters () { QLineEdit *le = dynamic_cast (m_widgets [r]); if (le) { - double v = 0; - tl::from_string (tl::to_string (le->text ()), v); - parameters.back () = tl::Variant (v); + + try { + + double v = 0; + tl::from_string (tl::to_string (le->text ()), v); + + parameters.back () = tl::Variant (v); + indicate_error (le, 0); + + } catch (tl::Exception &ex) { + + indicate_error (le, &ex); + edit_error = false; + + } + } } break; @@ -526,22 +598,43 @@ PCellParametersPage::get_parameters () try { + if (! edit_error) { + throw tl::Exception (tl::to_string (tr ("There are errors. See the highlighted edit fields for details."))); + } + // coerce the parameters mp_pcell_decl->coerce_parameters (*mp_layout, parameters); set_parameters (parameters); + mp_error_label->hide (); + mp_error_icon->hide (); + + if (ok) { + *ok = true; + } + } catch (tl::ScriptError &ex) { - mp_error_label->setText (tl::to_qstring (ex.basic_msg ())); - mp_error_label->setToolTip (tl::to_qstring (ex.msg ())); - mp_error_icon->show (); - mp_error_label->show (); + if (ok) { + mp_error_label->setText (tl::to_qstring (ex.basic_msg ())); + mp_error_label->setToolTip (tl::to_qstring (ex.msg ())); + mp_error_icon->show (); + mp_error_label->show (); + *ok = false; + } else { + throw; + } } catch (tl::Exception &ex) { - mp_error_label->setText (tl::to_qstring (ex.msg ())); - mp_error_icon->show (); - mp_error_label->show (); + if (ok) { + mp_error_label->setText (tl::to_qstring (ex.msg ())); + mp_error_icon->show (); + mp_error_label->show (); + *ok = false; + } else { + throw; + } } @@ -549,7 +642,7 @@ PCellParametersPage::get_parameters () } void -PCellParametersPage::set_parameters (const std::vector ¶meters) +PCellParametersPage::set_parameters (const std::vector ¶meters) { // write the changed value back size_t r = 0; diff --git a/src/edt/edt/edtPCellParametersPage.h b/src/edt/edt/edtPCellParametersPage.h index a41a7c233..9786a2c8b 100644 --- a/src/edt/edt/edtPCellParametersPage.h +++ b/src/edt/edt/edtPCellParametersPage.h @@ -25,6 +25,7 @@ #define HDR_edtPCellParametersPage #include "dbPCellDeclaration.h" +#include "tlDeferredExecution.h" #include #include @@ -42,7 +43,7 @@ namespace edt * @brief A QScrollArea that displays and allows editing PCell parameters */ class PCellParametersPage - : public QFrame + : public QFrame, public tl::Object { Q_OBJECT @@ -54,31 +55,28 @@ public: bool valid; int hScrollPosition; int vScrollPosition; + QString focusWidget; }; - /** - * @brief Constructor: creates a page showing the given parameters - * - * @param parent The parent widget - * @param layout The layout in which the PCell instance resides - * @param view The layout view from which to take layers for example - * @param cv_index The index of the cellview in "view" - * @param pcell_decl The PCell declaration - * @param parameters The parameter values to show (if empty, the default values are used) - */ - PCellParametersPage (QWidget *parent, const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type ¶meters); - /** * @brief Default constructor * * Use "setup" to configure the page. + * + * @param dense Use a dense layout if true */ - PCellParametersPage (QWidget *parent); + PCellParametersPage (QWidget *parent, bool dense = false); /** - * @brief Delayed initialization + * @brief initialization * * Use this method to setup when the arguments are not available in the constructor + * + * @param layout The layout in which the PCell instance resides + * @param view The layout view from which to take layers for example + * @param cv_index The index of the cellview in "view" + * @param pcell_decl The PCell declaration + * @param parameters The parameter values to show (if empty, the default values are used) */ void setup (const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type ¶meters); @@ -94,8 +92,12 @@ public: /** * @brief Get the current parameters + * + * *ok is set to true, if there is no error. In case of an error it's set to false. + * The error is indicated in the error label in the editor page. + * If ok is null, an exception is thrown. */ - std::vector get_parameters (); + std::vector get_parameters (bool *ok = 0); /** * @brief Get the PCell declaration pointer @@ -110,10 +112,12 @@ public: */ void set_parameters (const std::vector &values); -public slots: - void activated (); - void clicked (); - +signals: + void edited (); + +private slots: + void parameter_changed (); + private: QScrollArea *mp_parameters_area; QLabel *mp_error_label; @@ -124,8 +128,11 @@ private: lay::LayoutView *mp_view; int m_cv_index; db::pcell_parameters_type m_parameters; + bool m_dense; + tl::DeferredMethod dm_parameter_changed; void init (); + void do_parameter_changed (); }; } diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index a190a8272..89e1e48f6 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -379,6 +379,7 @@ show_editor_options_page (lay::LayoutView *view) edt::EditorOptionsPages *pages = new edt::EditorOptionsPages (view->editor_options_frame (), prop_dialog_pages, view); view->editor_options_frame ()->layout ()->addWidget (pages); + view->editor_options_frame ()->setFocusProxy (pages); } static diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 2e5b07755..0a7bacd70 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -176,6 +176,16 @@ public: mp_bglabel->show (); } + void focusInEvent (QFocusEvent *) + { + for (size_t i = 0; i < m_widgets.size (); ++i) { + if (m_widgets [i]->isVisible ()) { + m_widgets [i]->setFocus (); + break; + } + } + } + void addWidget (QWidget *w) { m_widgets.push_back (w); @@ -422,6 +432,7 @@ show_dock_widget (QDockWidget *dock_widget, bool visible) if (visible) { dock_widget->show (); + dock_widget->setFocus (); // NOTE: this is a clumsy way to make sure the dock widget is made the current tab if it's in a tabbed dock // TODO: is there a better way to do this? @@ -526,6 +537,7 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha mp_hp_dock_widget->setObjectName (QString::fromUtf8 ("hp_dock_widget")); mp_hp_stack = new ControlWidgetStack (mp_hp_dock_widget, "hp_stack"); mp_hp_dock_widget->setWidget (mp_hp_stack); + mp_hp_dock_widget->setFocusProxy (mp_hp_stack); connect (mp_hp_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); m_hp_visible = true; @@ -533,6 +545,7 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha mp_libs_dock_widget->setObjectName (QString::fromUtf8 ("libs_dock_widget")); mp_libs_stack = new ControlWidgetStack (mp_libs_dock_widget, "libs_stack"); mp_libs_dock_widget->setWidget (mp_libs_stack); + mp_libs_dock_widget->setFocusProxy (mp_libs_stack); connect (mp_libs_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); m_libs_visible = true; @@ -540,6 +553,7 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha mp_eo_dock_widget->setObjectName (QString::fromUtf8 ("eo_dock_widget")); mp_eo_stack = new ControlWidgetStack (mp_eo_dock_widget, "eo_stack"); mp_eo_dock_widget->setWidget (mp_eo_stack); + mp_eo_dock_widget->setFocusProxy (mp_eo_stack); connect (mp_eo_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); m_eo_visible = true; @@ -547,6 +561,7 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha mp_bm_dock_widget->setObjectName (QString::fromUtf8 ("bookmarks_dock_widget")); mp_bm_stack = new ControlWidgetStack (mp_bm_dock_widget, "bookmarks_stack"); mp_bm_dock_widget->setWidget (mp_bm_stack); + mp_bm_dock_widget->setFocusProxy (mp_bm_stack); connect (mp_bm_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); m_bm_visible = true; @@ -558,6 +573,7 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha mp_layer_toolbox_dock_widget->setObjectName (QString::fromUtf8 ("lt_dock_widget")); mp_layer_toolbox = new LayerToolbox (mp_layer_toolbox_dock_widget, "layer_toolbox"); mp_layer_toolbox_dock_widget->setWidget (mp_layer_toolbox); + mp_layer_toolbox_dock_widget->setFocusProxy (mp_layer_toolbox); connect (mp_layer_toolbox_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); m_layer_toolbox_visible = true; @@ -565,6 +581,7 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha mp_lp_dock_widget->setObjectName (QString::fromUtf8 ("lp_dock_widget")); mp_lp_stack = new ControlWidgetStack (mp_lp_dock_widget, "lp_stack"); mp_lp_dock_widget->setWidget (mp_lp_stack); + mp_lp_dock_widget->setFocusProxy (mp_lp_stack); connect (mp_lp_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); m_lp_visible = true; @@ -572,6 +589,7 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha mp_navigator_dock_widget->setObjectName (QString::fromUtf8 ("navigator_dock_widget")); mp_navigator = new Navigator (this); mp_navigator_dock_widget->setWidget (mp_navigator); + mp_navigator_dock_widget->setFocusProxy (mp_navigator); connect (mp_navigator_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); m_navigator_visible = true; @@ -3916,10 +3934,8 @@ MainWindow::menu_activated (const std::string &symbol) cm_help_about_qt (); } else if (symbol == "cm_edit_options") { - if (!m_eo_visible) { - m_eo_visible = true; - show_dock_widget (mp_eo_dock_widget, m_eo_visible); - } + m_eo_visible = true; + show_dock_widget (mp_eo_dock_widget, m_eo_visible); } else { @@ -4371,8 +4387,8 @@ public: menu_entries.push_back (lay::separator ("macros_group", at)); at = "@toolbar.end"; - menu_entries.push_back (lay::menu_item ("cm_prev_display_state", "prev_display_state", at, tl::to_string (QObject::tr ("Back(Shift+Tab)<:/back.png>")))); - menu_entries.push_back (lay::menu_item ("cm_next_display_state", "next_display_state", at, tl::to_string (QObject::tr ("Forward(Tab)<:/forward.png>")))); + menu_entries.push_back (lay::menu_item ("cm_prev_display_state", "prev_display_state", at, tl::to_string (QObject::tr ("Back<:/back.png>")))); + menu_entries.push_back (lay::menu_item ("cm_next_display_state", "next_display_state", at, tl::to_string (QObject::tr ("Forward<:/forward.png>")))); menu_entries.push_back (lay::separator ("toolbar_post_navigation_group", at)); } }; diff --git a/src/laybasic/laybasic/layLayoutViewFunctions.cc b/src/laybasic/laybasic/layLayoutViewFunctions.cc index 89e963c06..2f63cbd34 100644 --- a/src/laybasic/laybasic/layLayoutViewFunctions.cc +++ b/src/laybasic/laybasic/layLayoutViewFunctions.cc @@ -2185,8 +2185,8 @@ public: menu_entries.push_back (lay::separator ("zoom_group", at)); menu_entries.push_back (lay::menu_item ("cm_zoom_fit", "zoom_fit", at, tl::to_string (QObject::tr ("Zoom Fit(F2)")))); menu_entries.push_back (lay::menu_item ("cm_zoom_fit_sel", "zoom_fit_sel", at, tl::to_string (QObject::tr ("Zoom Fit Selection(Shift+F2)")))); - menu_entries.push_back (lay::menu_item ("cm_zoom_in", "zoom_in", at, tl::to_string (QObject::tr ("Zoom In(Return)")))); - menu_entries.push_back (lay::menu_item ("cm_zoom_out", "zoom_out", at, tl::to_string (QObject::tr ("Zoom Out(Shift+Return)")))); + menu_entries.push_back (lay::menu_item ("cm_zoom_in", "zoom_in", at, tl::to_string (QObject::tr ("Zoom In(Ctrl++)")))); + menu_entries.push_back (lay::menu_item ("cm_zoom_out", "zoom_out", at, tl::to_string (QObject::tr ("Zoom Out(Ctrl+-)")))); /* disabled because that interferes with the use of the arrow keys for moving the selection MenuLayoutEntry::separator ("pan_group"); menu_entries.push_back (lay::menu_item ("cm_pan_up", "pan_up", at, tl::to_string (QObject::tr ("Pan Up(Up)")))); diff --git a/src/laybasic/laybasic/layWidgets.cc b/src/laybasic/laybasic/layWidgets.cc index aee5a598f..6ea947b02 100644 --- a/src/laybasic/laybasic/layWidgets.cc +++ b/src/laybasic/laybasic/layWidgets.cc @@ -590,7 +590,7 @@ LibrarySelectionComboBox::set_technology_filter (const std::string &tech, bool e void LibrarySelectionComboBox::update_list () { - blockSignals (true); + bool wasBlocked = blockSignals (true); db::Library *lib = current_library (); @@ -620,7 +620,7 @@ LibrarySelectionComboBox::update_list () set_current_library (lib); - blockSignals (false); + blockSignals (wasBlocked); } LibrarySelectionComboBox::~LibrarySelectionComboBox () From 85e68940f2f546f0f937381d8df09ca5a7989cf2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 14 Aug 2020 01:24:46 +0200 Subject: [PATCH 051/113] WIP: interactive controls for generic edit options, bug fix for instance marker - appeared also when editing generic editor options for polygon, box ... --- src/edt/edt/edtEditorOptionsPages.cc | 40 ++++++++++++++++++++-------- src/edt/edt/edtServiceImpl.cc | 22 +++++++++------ 2 files changed, 43 insertions(+), 19 deletions(-) diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index ad2339f57..07cf8f8fd 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -242,18 +242,20 @@ END_PROTECTED_W (this) // ------------------------------------------------------------------ // Indicates an error on a line edit -static void indicate_error (QLineEdit *le, bool error) +static void indicate_error (QLineEdit *le, const tl::Exception *ex) { // by the way, update the foreground color of the cell edit box as well (red, if not valid) QPalette pl = le->palette (); - if (error) { + if (ex) { pl.setColor (QPalette::Active, QPalette::Text, Qt::red); pl.setColor (QPalette::Active, QPalette::Base, QColor (Qt::red).lighter (180)); + le->setToolTip (tl::to_qstring (ex->msg ())); } else { QWidget *pw = dynamic_cast (le->parent ()); tl_assert (pw != 0); pl.setColor (QPalette::Active, QPalette::Text, pw->palette ().color (QPalette::Text)); pl.setColor (QPalette::Active, QPalette::Base, pw->palette ().color (QPalette::Base)); + le->setToolTip (QString ()); } le->setPalette (pl); } @@ -265,9 +267,9 @@ static void configure_from_line_edit (lay::Dispatcher *dispatcher, QLineEdit *le Value value = Value (0); tl::from_string (tl::to_string (le->text ()), value); dispatcher->config_set (cfg_name, tl::to_string (value)); - indicate_error (le, false); - } catch (...) { - indicate_error (le, true); + indicate_error (le, 0); + } catch (tl::Exception &ex) { + indicate_error (le, &ex); } } @@ -281,6 +283,16 @@ EditorOptionsGeneric::EditorOptionsGeneric (lay::Dispatcher *dispatcher) mp_ui->setupUi (this); connect (mp_ui->grid_cb, SIGNAL (activated (int)), this, SLOT (grid_changed (int))); + + connect (mp_ui->edit_grid_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->grid_cb, SIGNAL (activated (int)), this, SLOT (edited ())); + connect (mp_ui->move_angle_cb, SIGNAL (activated (int)), this, SLOT (edited ())); + connect (mp_ui->conn_angle_cb, SIGNAL (activated (int)), this, SLOT (edited ())); + connect (mp_ui->hier_sel_cbx, SIGNAL (clicked ()), this, SLOT (edited ())); + connect (mp_ui->hier_copy_mode_cbx, SIGNAL (activated (int)), this, SLOT (edited ())); + connect (mp_ui->snap_objects_cbx, SIGNAL (clicked ()), this, SLOT (edited ())); + connect (mp_ui->max_shapes_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->show_shapes_cbx, SIGNAL (clicked ()), this, SLOT (edited ())); } EditorOptionsGeneric::~EditorOptionsGeneric () @@ -300,16 +312,21 @@ EditorOptionsGeneric::apply (lay::Dispatcher *root) { // Edit grid - db::DVector eg; EditGridConverter egc; if (mp_ui->grid_cb->currentIndex () == 0) { - eg = db::DVector (-1.0, -1.0); + root->config_set (cfg_edit_grid, egc.to_string (db::DVector (-1.0, -1.0))); } else if (mp_ui->grid_cb->currentIndex () == 1) { - eg = db::DVector (); + root->config_set (cfg_edit_grid, egc.to_string (db::DVector ())); } else { - egc.from_string_picky (tl::to_string (mp_ui->edit_grid_le->text ()), eg); + try { + db::DVector eg; + egc.from_string_picky (tl::to_string (mp_ui->edit_grid_le->text ()), eg); + indicate_error (mp_ui->edit_grid_le, 0); + root->config_set (cfg_edit_grid, egc.to_string (eg)); + } catch (tl::Exception &ex) { + indicate_error (mp_ui->edit_grid_le, &ex); + } } - root->config_set (cfg_edit_grid, egc.to_string (eg)); // Edit & move angle @@ -641,7 +658,8 @@ EditorOptionsInst::update_cell_edits () std::pair cc = layout->cell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ()); // by the way, update the foreground color of the cell edit box as well (red, if not valid) - indicate_error (mp_ui->cell_le, ! pc.first && ! cc.first); + tl::Exception ex ("No cell or PCell with this name"); + indicate_error (mp_ui->cell_le, (! pc.first && ! cc.first) ? &ex : 0); } void diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index 5f854d0e4..fbf9096f1 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -1673,16 +1673,22 @@ InstService::config_finalize () void InstService::update_marker () { - lay::Marker *marker = new lay::Marker (view (), m_cv_index, ! show_shapes_of_instances (), show_shapes_of_instances () ? max_shapes_of_instances () : 0); - marker->set_vertex_shape (lay::ViewOp::Cross); - marker->set_vertex_size (9 /*cross vertex size*/); - set_edit_marker (marker); + if (editing ()) { + + lay::Marker *marker = new lay::Marker (view (), m_cv_index, ! show_shapes_of_instances (), show_shapes_of_instances () ? max_shapes_of_instances () : 0); + marker->set_vertex_shape (lay::ViewOp::Cross); + marker->set_vertex_size (9 /*cross vertex size*/); + set_edit_marker (marker); + + db::CellInstArray inst; + if (get_inst (inst)) { + marker->set (inst, m_trans); + } else { + marker->set (); + } - db::CellInstArray inst; - if (get_inst (inst)) { - marker->set (inst, m_trans); } else { - marker->set (); + set_edit_marker (0); } } From 04f2d3a8c07773ac21ed266e7a46d1f4524b5e9d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 14 Aug 2020 02:14:47 +0200 Subject: [PATCH 052/113] WIP: removed 'apply' button on editor options as this is no longer required :) --- src/edt/edt/EditorOptionsGeneric.ui | 12 +++++++ src/edt/edt/edtEditorOptionsPages.cc | 52 +++++++++++++++------------- 2 files changed, 39 insertions(+), 25 deletions(-) diff --git a/src/edt/edt/EditorOptionsGeneric.ui b/src/edt/edt/EditorOptionsGeneric.ui index 66b4ca306..4ba0b27e1 100644 --- a/src/edt/edt/EditorOptionsGeneric.ui +++ b/src/edt/edt/EditorOptionsGeneric.ui @@ -458,6 +458,18 @@ + + scrollArea + grid_cb + edit_grid_le + snap_objects_cbx + conn_angle_cb + move_angle_cb + hier_sel_cbx + hier_copy_mode_cbx + show_shapes_cbx + max_shapes_le + diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index 07cf8f8fd..f4859dbc2 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -102,19 +102,6 @@ EditorOptionsPages::EditorOptionsPages (QWidget *parent, const std::vectoraddWidget (mp_pages); - QFrame *f = new QFrame (this); - ly1->addWidget (f); - - QToolButton *apply_pb = new QToolButton (f); - apply_pb->setText (tr ("Apply")); - - QHBoxLayout *ly2 = new QHBoxLayout (f); - ly2->setMargin (0); - ly2->addStretch (1); - ly2->addWidget (apply_pb); - - connect (apply_pb, SIGNAL (clicked ()), this, SLOT (apply ())); - m_pages = pages; for (std::vector ::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) { (*p)->set_owner (this); @@ -339,9 +326,7 @@ EditorOptionsGeneric::apply (lay::Dispatcher *root) root->config_set (cfg_edit_hier_copy_mode, tl::to_string ((cpm < 0 || cpm > 1) ? -1 : cpm)); root->config_set (cfg_edit_snap_to_objects, tl::to_string (mp_ui->snap_objects_cbx->isChecked ())); - unsigned int max_shapes = 1000; - tl::from_string (tl::to_string (mp_ui->max_shapes_le->text ()), max_shapes); - root->config_set (cfg_edit_max_shapes_of_instances, tl::to_string (max_shapes)); + configure_from_line_edit (root, mp_ui->max_shapes_le, cfg_edit_max_shapes_of_instances); root->config_set (cfg_edit_show_shapes_of_instances, tl::to_string (mp_ui->show_shapes_cbx->isChecked ())); } @@ -375,6 +360,7 @@ EditorOptionsGeneric::setup (lay::Dispatcher *root) mp_ui->edit_grid_le->setText (tl::to_qstring (egc.to_string (eg))); } grid_changed (mp_ui->grid_cb->currentIndex ()); + indicate_error (mp_ui->edit_grid_le, 0); // edit & move angle @@ -404,6 +390,7 @@ EditorOptionsGeneric::setup (lay::Dispatcher *root) unsigned int max_shapes = 1000; root->config_get (cfg_edit_max_shapes_of_instances, max_shapes); mp_ui->max_shapes_le->setText (tl::to_qstring (tl::to_string (max_shapes))); + indicate_error (mp_ui->max_shapes_le, 0); bool show_shapes = true; root->config_get (cfg_edit_show_shapes_of_instances, show_shapes); @@ -418,6 +405,11 @@ EditorOptionsText::EditorOptionsText (lay::Dispatcher *dispatcher) { mp_ui = new Ui::EditorOptionsText (); mp_ui->setupUi (this); + + connect (mp_ui->text_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->halign_cbx, SIGNAL (activated (int)), this, SLOT (edited ())); + connect (mp_ui->valign_cbx, SIGNAL (activated (int)), this, SLOT (edited ())); + connect (mp_ui->size_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); } EditorOptionsText::~EditorOptionsText () @@ -493,6 +485,11 @@ EditorOptionsPath::EditorOptionsPath (lay::Dispatcher *dispatcher) mp_ui->setupUi (this); connect (mp_ui->type_cb, SIGNAL (currentIndexChanged (int)), this, SLOT (type_changed (int))); + + connect (mp_ui->width_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->type_cb, SIGNAL (activated (int)), this, SLOT (edited ())); + connect (mp_ui->start_ext_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->end_ext_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); } EditorOptionsPath::~EditorOptionsPath () @@ -519,9 +516,7 @@ EditorOptionsPath::apply (lay::Dispatcher *root) { // width - double w = 0.0; - tl::from_string (tl::to_string (mp_ui->width_le->text ()), w); - root->config_set (cfg_edit_path_width, tl::to_string (w)); + configure_from_line_edit (root, mp_ui->width_le, cfg_edit_path_width); // path type and extensions @@ -535,14 +530,10 @@ EditorOptionsPath::apply (lay::Dispatcher *root) } else if (mp_ui->type_cb->currentIndex () == 2) { - double bgnext = 0.0, endext = 0.0; root->config_set (cfg_edit_path_ext_type, "variable"); - tl::from_string (tl::to_string (mp_ui->start_ext_le->text ()), bgnext); - root->config_set (cfg_edit_path_ext_var_begin, tl::to_string (bgnext)); - - tl::from_string (tl::to_string (mp_ui->end_ext_le->text ()), endext); - root->config_set (cfg_edit_path_ext_var_end, tl::to_string (endext)); + configure_from_line_edit (root, mp_ui->start_ext_le, cfg_edit_path_ext_var_begin); + configure_from_line_edit (root, mp_ui->end_ext_le, cfg_edit_path_ext_var_end); } else if (mp_ui->type_cb->currentIndex () == 3) { @@ -559,6 +550,7 @@ EditorOptionsPath::setup (lay::Dispatcher *root) double w = 0.0; root->config_get (cfg_edit_path_width, w); mp_ui->width_le->setText (tl::to_qstring (tl::to_string (w))); + indicate_error (mp_ui->width_le, 0); // path type and extensions @@ -579,7 +571,9 @@ EditorOptionsPath::setup (lay::Dispatcher *root) root->config_get (cfg_edit_path_ext_var_begin, bgnext); root->config_get (cfg_edit_path_ext_var_end, endext); mp_ui->start_ext_le->setText (tl::to_qstring (tl::to_string (bgnext))); + indicate_error (mp_ui->start_ext_le, 0); mp_ui->end_ext_le->setText (tl::to_qstring (tl::to_string (endext))); + indicate_error (mp_ui->end_ext_le, 0); } // ------------------------------------------------------------------ @@ -802,6 +796,7 @@ EditorOptionsInst::setup (lay::Dispatcher *root) double angle = 0.0; root->config_get (cfg_edit_inst_angle, angle); mp_ui->angle_le->setText (tl::to_qstring (tl::to_string (angle))); + indicate_error (mp_ui->angle_le, 0); bool mirror = false; root->config_get (cfg_edit_inst_mirror, mirror); @@ -810,6 +805,7 @@ EditorOptionsInst::setup (lay::Dispatcher *root) double scale = 1.0; root->config_get (cfg_edit_inst_scale, scale); mp_ui->scale_le->setText (tl::to_qstring (tl::to_string (scale))); + indicate_error (mp_ui->scale_le, 0); // array bool array = false; @@ -826,11 +822,17 @@ EditorOptionsInst::setup (lay::Dispatcher *root) root->config_get (cfg_edit_inst_column_y, column_y); mp_ui->rows_le->setText (tl::to_qstring (tl::to_string (rows))); + indicate_error (mp_ui->rows_le, 0); mp_ui->row_x_le->setText (tl::to_qstring (tl::to_string (row_x))); + indicate_error (mp_ui->row_x_le, 0); mp_ui->row_y_le->setText (tl::to_qstring (tl::to_string (row_y))); + indicate_error (mp_ui->row_y_le, 0); mp_ui->columns_le->setText (tl::to_qstring (tl::to_string (columns))); + indicate_error (mp_ui->columns_le, 0); mp_ui->column_x_le->setText (tl::to_qstring (tl::to_string (column_x))); + indicate_error (mp_ui->column_x_le, 0); mp_ui->column_y_le->setText (tl::to_qstring (tl::to_string (column_y))); + indicate_error (mp_ui->column_y_le, 0); // place origin of cell flag bool place_origin = false; From 9a2c091f65d5bab8823b98cb0e6eff5b2013424e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 14 Aug 2020 18:58:24 +0200 Subject: [PATCH 053/113] WIP: completer for cell name (for <10k cells) in instance editor options --- src/edt/edt/edtEditorOptionsPages.cc | 31 ++++++++++++++++++++++------ src/edt/edt/edtEditorOptionsPages.h | 1 - 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index f4859dbc2..164091103 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -44,6 +44,7 @@ #include #include #include +#include namespace edt { @@ -623,16 +624,16 @@ EditorOptionsInst::library_changed () edited (); } -void -EditorOptionsInst::cell_name_changed () -{ - update_cell_edits (); - edited (); -} +// Maximum number of cells for which to offer a cell name completer +const static size_t max_cells = 10000; void EditorOptionsInst::update_cell_edits () { + if (mp_ui->cell_le->completer ()) { + mp_ui->cell_le->completer ()->deleteLater (); + } + db::Layout *layout = 0; lay::LayoutView *view = lay::LayoutView::current (); @@ -648,6 +649,24 @@ EditorOptionsInst::update_cell_edits () return; } + QStringList cellnames; + if (layout->cells () < max_cells) { + for (db::Layout::iterator c = layout->begin (); c != layout->end (); ++c) { + cellnames.push_back (tl::to_qstring (layout->cell_name (c->cell_index ()))); + } + for (db::Layout::pcell_iterator pc = layout->begin_pcells (); pc != layout->end_pcells () && size_t (cellnames.size ()) < max_cells; ++pc) { + cellnames.push_back (tl::to_qstring (pc->first)); + } + } + + if (size_t (cellnames.size ()) < max_cells) { + QCompleter *completer = new QCompleter (cellnames, this); + completer->setCaseSensitivity (Qt::CaseSensitive); + mp_ui->cell_le->setCompleter (completer); + } else { + mp_ui->cell_le->setCompleter (0); + } + std::pair pc = layout->pcell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ()); std::pair cc = layout->cell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ()); diff --git a/src/edt/edt/edtEditorOptionsPages.h b/src/edt/edt/edtEditorOptionsPages.h index 017299841..1055d726b 100644 --- a/src/edt/edt/edtEditorOptionsPages.h +++ b/src/edt/edt/edtEditorOptionsPages.h @@ -235,7 +235,6 @@ private slots: void browse_cell (); void update_pcell_parameters (); void library_changed (); - void cell_name_changed (); void update_cell_edits (); private: From ee5cd9cb632e75a021e8fafda721326fddb1bd8b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 15 Aug 2020 00:13:17 +0200 Subject: [PATCH 054/113] WIP: first 'recent options' list, dragging a PCell variant into the canvas makes the editor take a PCell with these parameters. --- src/edt/edt/edt.pro | 8 +- src/edt/edt/edtEditorOptionsPage.cc | 65 ++++++++++++ src/edt/edt/edtEditorOptionsPage.h | 86 ++++++++++++++++ src/edt/edt/edtEditorOptionsPages.cc | 38 +------ src/edt/edt/edtEditorOptionsPages.h | 61 +---------- src/edt/edt/edtPlugin.cc | 31 ++++++ src/edt/edt/edtPlugin.h | 5 + src/edt/edt/edtRecentConfigurationPage.cc | 117 ++++++++++++++++++++++ src/edt/edt/edtRecentConfigurationPage.h | 97 ++++++++++++++++++ src/edt/edt/edtServiceImpl.cc | 7 ++ src/laybasic/laybasic/layCellTreeModel.cc | 54 ++++++++-- src/laybasic/laybasic/layViewObject.cc | 17 ++++ src/laybasic/laybasic/layViewObject.h | 13 ++- 13 files changed, 493 insertions(+), 106 deletions(-) create mode 100644 src/edt/edt/edtEditorOptionsPage.cc create mode 100644 src/edt/edt/edtEditorOptionsPage.h create mode 100644 src/edt/edt/edtRecentConfigurationPage.cc create mode 100644 src/edt/edt/edtRecentConfigurationPage.h diff --git a/src/edt/edt/edt.pro b/src/edt/edt/edt.pro index a10d63c9e..1c0e3a30f 100644 --- a/src/edt/edt/edt.pro +++ b/src/edt/edt/edt.pro @@ -21,7 +21,9 @@ HEADERS = \ edtServiceImpl.h \ edtUtils.h \ edtCommon.h \ - edtDistribute.h + edtDistribute.h \ + edtRecentConfigurationPage.h \ + edtEditorOptionsPage.h FORMS = \ AlignOptionsDialog.ui \ @@ -59,7 +61,9 @@ SOURCES = \ edtServiceImpl.cc \ edtUtils.cc \ gsiDeclEdt.cc \ - edtDistribute.cc + edtDistribute.cc \ + edtRecentConfigurationPage.cc \ + edtEditorOptionsPage.cc INCLUDEPATH += $$TL_INC $$GSI_INC $$LAYBASIC_INC $$DB_INC DEPENDPATH += $$TL_INC $$GSI_INC $$LAYBASIC_INC $$DB_INC diff --git a/src/edt/edt/edtEditorOptionsPage.cc b/src/edt/edt/edtEditorOptionsPage.cc new file mode 100644 index 000000000..92c61e469 --- /dev/null +++ b/src/edt/edt/edtEditorOptionsPage.cc @@ -0,0 +1,65 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "tlInternational.h" +#include "edtEditorOptionsPage.h" +#include "edtEditorOptionsPages.h" + +namespace edt +{ + +// ------------------------------------------------------------------ +// EditorOptionsPage implementation + +EditorOptionsPage::EditorOptionsPage (lay::Dispatcher *dispatcher) + : QWidget (0), mp_owner (0), m_active (true), mp_plugin_declaration (0), mp_dispatcher (dispatcher) +{ + // nothing yet .. +} + +EditorOptionsPage::~EditorOptionsPage () +{ + set_owner (0); +} + +void +EditorOptionsPage::set_owner (EditorOptionsPages *owner) +{ + if (mp_owner) { + mp_owner->unregister_page (this); + } + mp_owner = owner; +} + +void +EditorOptionsPage::activate (bool active) +{ + if (m_active != active) { + m_active = active; + if (mp_owner) { + mp_owner->activate_page (this); + } + } +} + +} diff --git a/src/edt/edt/edtEditorOptionsPage.h b/src/edt/edt/edtEditorOptionsPage.h new file mode 100644 index 000000000..4d52beb46 --- /dev/null +++ b/src/edt/edt/edtEditorOptionsPage.h @@ -0,0 +1,86 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_edtEditorOptionsPage +#define HDR_edtEditorOptionsPage + +#include + +namespace lay +{ + class PluginDeclaration; + class Dispatcher; + class Plugin; +} + +namespace edt +{ + +class EditorOptionsPages; + +/** + * @brief The base class for a object properties page + */ +class EditorOptionsPage + : public QWidget +{ +Q_OBJECT + +public: + EditorOptionsPage (lay::Dispatcher *dispatcher); + virtual ~EditorOptionsPage (); + + virtual std::string title () const = 0; + virtual int order () const = 0; + virtual void apply (lay::Dispatcher * /*root*/) { } + virtual void setup (lay::Dispatcher * /*root*/) { } + virtual void commit_recent (lay::Dispatcher * /*root*/) { } + + bool active () const { return m_active; } + void activate (bool active); + void set_owner (EditorOptionsPages *owner); + + const lay::PluginDeclaration *plugin_declaration () const { return mp_plugin_declaration; } + void set_plugin_declaration (const lay::PluginDeclaration *pd) { mp_plugin_declaration = pd; } + +protected slots: + void edited () + { + apply (dispatcher ()); + } + +protected: + lay::Dispatcher *dispatcher () const + { + return mp_dispatcher; + } + +private: + EditorOptionsPages *mp_owner; + bool m_active; + const lay::PluginDeclaration *mp_plugin_declaration; + lay::Dispatcher *mp_dispatcher; +}; + +} + +#endif diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index 164091103..89f16ab0a 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -49,40 +49,6 @@ namespace edt { -// ------------------------------------------------------------------ -// EditorOptionsPage implementation - -EditorOptionsPage::EditorOptionsPage (lay::Dispatcher *dispatcher) - : QWidget (0), mp_owner (0), m_active (true), mp_plugin_declaration (0), mp_dispatcher (dispatcher) -{ - // nothing yet .. -} - -EditorOptionsPage::~EditorOptionsPage () -{ - set_owner (0); -} - -void -EditorOptionsPage::set_owner (EditorOptionsPages *owner) -{ - if (mp_owner) { - mp_owner->unregister_page (this); - } - mp_owner = owner; -} - -void -EditorOptionsPage::activate (bool active) -{ - if (m_active != active) { - m_active = active; - if (mp_owner) { - mp_owner->activate_page (this); - } - } -} - // ------------------------------------------------------------------ // EditorOptionsPages implementation @@ -174,9 +140,9 @@ EditorOptionsPages::update (edt::EditorOptionsPage *page) if ((*p) == page) { index = mp_pages->count (); } - mp_pages->addTab ((*p)->q_frame (), tl::to_qstring ((*p)->title ())); + mp_pages->addTab (*p, tl::to_qstring ((*p)->title ())); } else { - (*p)->q_frame ()->setParent (0); + (*p)->setParent (0); } } if (index < 0) { diff --git a/src/edt/edt/edtEditorOptionsPages.h b/src/edt/edt/edtEditorOptionsPages.h index 1055d726b..652be507e 100644 --- a/src/edt/edt/edtEditorOptionsPages.h +++ b/src/edt/edt/edtEditorOptionsPages.h @@ -24,6 +24,8 @@ #ifndef HDR_edtEditorOptionsPages #define HDR_edtEditorOptionsPages +#include "edtEditorOptionsPage.h" + #include #include @@ -57,52 +59,6 @@ namespace edt class PCellParametersPage; -class EditorOptionsPages; - -/** - * @brief The base class for a object properties page - */ -class EditorOptionsPage - : public QWidget -{ -Q_OBJECT - -public: - EditorOptionsPage (lay::Dispatcher *dispatcher); - virtual ~EditorOptionsPage (); - - virtual QWidget *q_frame () = 0; - virtual std::string title () const = 0; - virtual int order () const = 0; - virtual void apply (lay::Dispatcher *root) = 0; - virtual void setup (lay::Dispatcher *root) = 0; - - bool active () const { return m_active; } - void activate (bool active); - void set_owner (EditorOptionsPages *owner); - - const lay::PluginDeclaration *plugin_declaration () const { return mp_plugin_declaration; } - void set_plugin_declaration (const lay::PluginDeclaration *pd) { mp_plugin_declaration = pd; } - -protected slots: - void edited () - { - apply (dispatcher ()); - } - -protected: - lay::Dispatcher *dispatcher () const - { - return mp_dispatcher; - } - -private: - EditorOptionsPages *mp_owner; - bool m_active; - const lay::PluginDeclaration *mp_plugin_declaration; - lay::Dispatcher *mp_dispatcher; -}; - /** * @brief The object properties dialog */ @@ -149,8 +105,6 @@ public: EditorOptionsGeneric (lay::Dispatcher *dispatcher); ~EditorOptionsGeneric (); - virtual QWidget *q_frame () { return this; } - virtual std::string title () const; virtual int order () const { return 0; } void apply (lay::Dispatcher *root); @@ -174,8 +128,6 @@ public: EditorOptionsText (lay::Dispatcher *dispatcher); ~EditorOptionsText (); - virtual QWidget *q_frame () { return this; } - virtual std::string title () const; virtual int order () const { return 10; } void apply (lay::Dispatcher *root); @@ -197,8 +149,6 @@ public: EditorOptionsPath (lay::Dispatcher *dispatcher); ~EditorOptionsPath (); - virtual QWidget *q_frame () { return this; } - virtual std::string title () const; virtual int order () const { return 30; } void apply (lay::Dispatcher *root); @@ -223,8 +173,6 @@ public: EditorOptionsInst (lay::Dispatcher *root); ~EditorOptionsInst (); - virtual QWidget *q_frame () { return this; } - virtual std::string title () const; virtual int order () const { return 20; } void apply (lay::Dispatcher *root); @@ -233,7 +181,6 @@ public: private slots: void array_changed (); void browse_cell (); - void update_pcell_parameters (); void library_changed (); void update_cell_edits (); @@ -241,8 +188,6 @@ private: Ui::EditorOptionsInst *mp_ui; edt::PCellParametersPage *mp_pcell_parameters; int m_cv_index; - - void update_pcell_parameters (const std::vector ¶meters); }; /** @@ -257,8 +202,6 @@ public: EditorOptionsInstPCellParam (lay::Dispatcher *root); ~EditorOptionsInstPCellParam (); - virtual QWidget *q_frame () { return this; } - virtual std::string title () const; virtual int order () const { return 21; } void apply (lay::Dispatcher *root); diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 89e1e48f6..5493273b5 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -29,6 +29,7 @@ #include "edtMainService.h" #include "edtPartialService.h" #include "edtEditorOptionsPages.h" +#include "edtRecentConfigurationPage.h" #include #include @@ -85,9 +86,26 @@ void get_inst_options (std::vector < std::pair > &opti options.push_back (std::pair (cfg_edit_show_shapes_of_instances, "true")); } +edt::RecentConfigurationPage::ConfigurationDescriptor inst_cfg_descriptors[] = +{ + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_cell_name, tl::to_string (tr ("Cell name")), edt::RecentConfigurationPage::Text), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_angle, tl::to_string (tr ("Angle")), edt::RecentConfigurationPage::Double), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_mirror, tl::to_string (tr ("Mirror")), edt::RecentConfigurationPage::Bool), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_scale, tl::to_string (tr ("Scale")), edt::RecentConfigurationPage::Double), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_array, tl::to_string (tr ("Array")), edt::RecentConfigurationPage::Bool), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_rows, tl::to_string (tr ("Rows")), edt::RecentConfigurationPage::Double), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_row_x, tl::to_string (tr ("Row step (x)")), edt::RecentConfigurationPage::Double), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_row_y, tl::to_string (tr ("Row step (y)")), edt::RecentConfigurationPage::Double), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_columns, tl::to_string (tr ("Columns")), edt::RecentConfigurationPage::Double), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_column_x, tl::to_string (tr ("Column step (x)")), edt::RecentConfigurationPage::Double), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_column_y, tl::to_string (tr ("Column step (y)")), edt::RecentConfigurationPage::Double) +}; + static void get_inst_editor_options_pages (std::vector &ret, lay::Dispatcher *dispatcher) { + ret.push_back (new RecentConfigurationPage (dispatcher, 22, tl::to_string (tr ("Recent")), + &inst_cfg_descriptors[0], &inst_cfg_descriptors[sizeof (inst_cfg_descriptors) / sizeof (inst_cfg_descriptors[0])])); ret.push_back (new EditorOptionsInstPCellParam (dispatcher)); ret.push_back (new EditorOptionsInst (dispatcher)); } @@ -422,6 +440,19 @@ setup_pages (lay::LayoutView *view) } } +void +commit_recent (lay::LayoutView *view) +{ + edt::EditorOptionsPages *eo_pages = get_pages_widget (view); + if (!eo_pages) { + return; + } + + for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { + (*op)->commit_recent (view); + } +} + class PartialPluginDeclaration : public lay::PluginDeclaration { diff --git a/src/edt/edt/edtPlugin.h b/src/edt/edt/edtPlugin.h index fe0c8a2c0..412f51d97 100644 --- a/src/edt/edt/edtPlugin.h +++ b/src/edt/edt/edtPlugin.h @@ -65,6 +65,11 @@ namespace edt * @brief Setup the editor option pages for the given view */ void setup_pages (lay::LayoutView *view); + + /** + * @brief Commits the current configuration for the recently used configuration list + */ + void commit_recent (lay::LayoutView *view); } #endif diff --git a/src/edt/edt/edtRecentConfigurationPage.cc b/src/edt/edt/edtRecentConfigurationPage.cc new file mode 100644 index 000000000..52d06d484 --- /dev/null +++ b/src/edt/edt/edtRecentConfigurationPage.cc @@ -0,0 +1,117 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "edtRecentConfigurationPage.h" +#include "layDispatcher.h" + +#include +#include + +namespace edt +{ + +void +RecentConfigurationPage::init () +{ + QVBoxLayout *ly = new QVBoxLayout (this); + ly->setMargin (0); + + mp_tree_widget = new QTreeWidget (this); + ly->addWidget (mp_tree_widget); + + mp_tree_widget->setColumnCount (int (m_cfg.size ())); + + QStringList column_labels; + for (std::list::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c) { + column_labels << tl::to_qstring (c->title); + } + mp_tree_widget->setHeaderLabels (column_labels); + + update_list (); +} + +RecentConfigurationPage::~RecentConfigurationPage () +{ + // .. nothing yet .. +} + +void +render_to (QTreeWidgetItem *item, int column, const std::string &v, RecentConfigurationPage::ConfigurationRendering rendering) +{ + + // @@@ rendering + item->setText (column, tl::to_qstring (v)); + +} + +void +RecentConfigurationPage::update_list () +{ + int row = 0; + for (std::list >::const_iterator v = m_stored_values.begin (); v != m_stored_values.end (); ++v, ++row) { + + QTreeWidgetItem *item = 0; + if (row < mp_tree_widget->topLevelItemCount ()) { + item = mp_tree_widget->topLevelItem (row); + } else { + item = new QTreeWidgetItem (mp_tree_widget); + mp_tree_widget->addTopLevelItem (item); + } + + int column = 0; + for (std::list::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c, ++column) { + if (column < int (v->size ())) { + render_to (item, column, (*v) [column], c->rendering); + } + } + + } + + while (mp_tree_widget->topLevelItemCount () > row) { + delete mp_tree_widget->takeTopLevelItem (row); + } + + mp_tree_widget->header ()->resizeSections (QHeaderView::ResizeToContents); +} + +void +RecentConfigurationPage::commit_recent (lay::Dispatcher *root) +{ + std::vector values; + values.reserve (m_cfg.size ()); + for (std::list::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c) { + values.push_back (root->config_get (c->cfg_name)); + } + + for (std::list >::iterator v = m_stored_values.begin (); v != m_stored_values.end (); ++v) { + if (*v == values) { + m_stored_values.erase (v); + break; + } + } + + m_stored_values.push_front (values); + + update_list (); +} + +} diff --git a/src/edt/edt/edtRecentConfigurationPage.h b/src/edt/edt/edtRecentConfigurationPage.h new file mode 100644 index 000000000..3cb290401 --- /dev/null +++ b/src/edt/edt/edtRecentConfigurationPage.h @@ -0,0 +1,97 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_edtRecentConfigurationPage +#define HDR_edtRecentConfigurationPage + +#include "edtEditorOptionsPage.h" + +#include +#include + +namespace edt +{ + +class PCellParametersPage; + +class EditorOptionsPages; + +/** + * @brief The base class for a object properties page + */ +class RecentConfigurationPage + : public EditorOptionsPage +{ +Q_OBJECT + +public: + enum ConfigurationRendering + { + Text = 0, + Bool = 1, + Double = 2, + Int = 3, + Layer = 4, + PCellParamters = 5 + }; + + struct ConfigurationDescriptor + { + ConfigurationDescriptor (const std::string &_cfg_name, const std::string &_title, ConfigurationRendering _rendering) + : cfg_name (_cfg_name), title (_title), rendering (_rendering) + { } + + std::string cfg_name, title; + ConfigurationRendering rendering; + }; + + template + RecentConfigurationPage (lay::Dispatcher *dispatcher, int order, const std::string &title, Iter begin_cfg, Iter end_cfg) + : EditorOptionsPage (dispatcher), m_title (title), m_order (order), m_cfg (begin_cfg, end_cfg) + { + init (); + } + + virtual ~RecentConfigurationPage (); + + virtual std::string title () const { return m_title; } + virtual int order () const { return m_order; } + virtual void apply (lay::Dispatcher * /*root*/) { } + virtual void setup (lay::Dispatcher * /*root*/) { } + virtual void commit_recent (lay::Dispatcher *root); + +private: + std::string m_title; + int m_order; + std::list m_cfg; + QTreeWidget *mp_tree_widget; + std::list > m_stored_values; + + void init (); + void update_list (); +}; + +} + +#endif + diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index fbf9096f1..8f65a7e7a 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -26,6 +26,7 @@ #include "edtPropertiesPages.h" #include "edtInstPropertiesPage.h" #include "edtService.h" +#include "edtPlugin.h" #include "dbEdge.h" #include "dbLibrary.h" #include "dbLibraryManager.h" @@ -1165,6 +1166,10 @@ InstService::drag_enter_event (const db::DPoint &p, const lay::DragDropDataBase m_cell_or_pcell_name = pcell_decl->name (); } + if (! cd->pcell_params ().empty ()) { + m_pcell_parameters = pcell_decl->named_parameters (cd->pcell_params ()); + } + } else if (cd->layout ()->is_valid_cell_index (cd->cell_index ())) { m_cell_or_pcell_name = cd->layout ()->cell_name (cd->cell_index ()); @@ -1452,6 +1457,8 @@ InstService::do_finish_edit () cv->layout ().cleanup (); manager ()->commit (); + commit_recent (view ()); + if (m_in_drag_drop) { lay::ObjectInstPath sel; diff --git a/src/laybasic/laybasic/layCellTreeModel.cc b/src/laybasic/laybasic/layCellTreeModel.cc index b1c91977e..52486cbec 100644 --- a/src/laybasic/laybasic/layCellTreeModel.cc +++ b/src/laybasic/laybasic/layCellTreeModel.cc @@ -28,6 +28,7 @@ #include "dbPCellVariant.h" #include "dbLibraryProxy.h" #include "dbLibrary.h" +#include "dbLibraryManager.h" #include #include @@ -604,14 +605,53 @@ CellTreeModel::mimeData(const QModelIndexList &indexes) const { for (QModelIndexList::const_iterator i = indexes.begin (); i != indexes.end (); ++i) { - if (i->isValid()) { + if (! i->isValid()) { + continue; + } + + if (is_pcell (*i)) { + + lay::CellDragDropData data (mp_layout, mp_library, pcell_id (*i), true); + return data.to_mime_data (); + + } else { + + const db::Cell *c = cell (*i); + if (c) { + + // resolve library proxies + const db::Layout *layout = mp_layout; + const db::Library *library = mp_library; + + const db::LibraryProxy *lib_proxy; + while (layout != 0 && (lib_proxy = dynamic_cast (c)) != 0) { + + const db::Library *lib = db::LibraryManager::instance ().lib (lib_proxy->lib_id ()); + if (! lib) { + break; + } + + library = lib; + layout = &lib->layout (); + + if (layout->is_valid_cell_index (lib_proxy->library_cell_index ())) { + c = &layout->cell (lib_proxy->library_cell_index ()); + } else { + c = 0; + } + + } + + // identify pcell variants and turn them into PCell drag targets + const db::PCellVariant *pcell_var = dynamic_cast (c); + if (pcell_var) { + lay::CellDragDropData data (layout, library, pcell_var->pcell_id (), true, pcell_var->parameters ()); + return data.to_mime_data (); + } else if (c) { + lay::CellDragDropData data (layout, library, c->cell_index (), false); + return data.to_mime_data (); + } - if (is_pcell (*i)) { - lay::CellDragDropData data (mp_layout, mp_library, pcell_id (*i), true); - return data.to_mime_data (); - } else if (cell (*i)) { - lay::CellDragDropData data (mp_layout, mp_library, cell_index (*i), false); - return data.to_mime_data (); } } diff --git a/src/laybasic/laybasic/layViewObject.cc b/src/laybasic/laybasic/layViewObject.cc index ef2a7dbfb..1ba1e5dfd 100644 --- a/src/laybasic/laybasic/layViewObject.cc +++ b/src/laybasic/laybasic/layViewObject.cc @@ -73,6 +73,10 @@ CellDragDropData::serialized () const stream << (quintptr) mp_library; stream << m_cell_index; stream << m_is_pcell; + stream << int (m_pcell_params.size ()); + for (std::vector::const_iterator i = m_pcell_params.begin (); i != m_pcell_params.end (); ++i) { + stream << tl::to_qstring (i->to_parsable_string ()); + } return data; } @@ -94,6 +98,19 @@ CellDragDropData::deserialize (const QByteArray &ba) mp_library = reinterpret_cast (p); stream >> m_cell_index; stream >> m_is_pcell; + + m_pcell_params.clear (); + int n = 0; + stream >> n; + while (n-- > 0) { + QString s; + stream >> s; + std::string stl_s = tl::to_string (s); + tl::Extractor ex (stl_s.c_str ()); + m_pcell_params.push_back (tl::Variant ()); + ex.read (m_pcell_params.back ()); + } + return true; } else { diff --git a/src/laybasic/laybasic/layViewObject.h b/src/laybasic/laybasic/layViewObject.h index 4522857cf..c89fb65cd 100644 --- a/src/laybasic/laybasic/layViewObject.h +++ b/src/laybasic/laybasic/layViewObject.h @@ -130,8 +130,8 @@ public: * @param layout the layout where the cell lives in * @param cell_index The index of the cell */ - CellDragDropData (const db::Layout *layout, const db::Library *library, db::cell_index_type cell_or_pcell_index, bool is_pcell) - : mp_layout (layout), mp_library (library), m_cell_index (cell_or_pcell_index), m_is_pcell (is_pcell) + CellDragDropData (const db::Layout *layout, const db::Library *library, db::cell_index_type cell_or_pcell_index, bool is_pcell, const std::vector &pcell_params = std::vector ()) + : mp_layout (layout), mp_library (library), m_cell_index (cell_or_pcell_index), m_is_pcell (is_pcell), m_pcell_params (pcell_params) { // .. nothing yet .. } @@ -152,6 +152,14 @@ public: return mp_library; } + /** + * @brief PCell parameters + */ + const std::vector &pcell_params () const + { + return m_pcell_params; + } + /** * @brief Gets the index of the cell */ @@ -185,6 +193,7 @@ private: const db::Library *mp_library; db::cell_index_type m_cell_index; bool m_is_pcell; + std::vector m_pcell_params; }; /** From 399e4f4fc73eaba05af059c72f57aba1786294e2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Aug 2020 09:51:04 +0200 Subject: [PATCH 055/113] WIP: some enhancements on recent options page, rotate with right mouse button now changes options too. --- src/edt/edt/edtPlugin.cc | 7 +- src/edt/edt/edtRecentConfigurationPage.cc | 76 ++++++++++- src/edt/edt/edtRecentConfigurationPage.h | 16 +-- src/edt/edt/edtService.cc | 39 ------ src/edt/edt/edtServiceImpl.cc | 151 ++++++++++++++++++---- src/edt/edt/edtUtils.cc | 39 ++++++ src/edt/edt/edtUtils.h | 10 ++ 7 files changed, 257 insertions(+), 81 deletions(-) diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 5493273b5..ec50d70c6 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -88,7 +88,10 @@ void get_inst_options (std::vector < std::pair > &opti edt::RecentConfigurationPage::ConfigurationDescriptor inst_cfg_descriptors[] = { - edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_cell_name, tl::to_string (tr ("Cell name")), edt::RecentConfigurationPage::Text), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_lib_name, tl::to_string (tr ("Library")), edt::RecentConfigurationPage::Text), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_cell_name, tl::to_string (tr ("Cell")), edt::RecentConfigurationPage::Text), + // encode this into the cell? + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_pcell_parameters, tl::to_string (tr ("PCell parameters")), edt::RecentConfigurationPage::PCellParamters), edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_angle, tl::to_string (tr ("Angle")), edt::RecentConfigurationPage::Double), edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_mirror, tl::to_string (tr ("Mirror")), edt::RecentConfigurationPage::Bool), edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_scale, tl::to_string (tr ("Scale")), edt::RecentConfigurationPage::Double), @@ -104,7 +107,7 @@ edt::RecentConfigurationPage::ConfigurationDescriptor inst_cfg_descriptors[] = static void get_inst_editor_options_pages (std::vector &ret, lay::Dispatcher *dispatcher) { - ret.push_back (new RecentConfigurationPage (dispatcher, 22, tl::to_string (tr ("Recent")), + ret.push_back (new RecentConfigurationPage (dispatcher, "edit-recent-inst-param", &inst_cfg_descriptors[0], &inst_cfg_descriptors[sizeof (inst_cfg_descriptors) / sizeof (inst_cfg_descriptors[0])])); ret.push_back (new EditorOptionsInstPCellParam (dispatcher)); ret.push_back (new EditorOptionsInst (dispatcher)); diff --git a/src/edt/edt/edtRecentConfigurationPage.cc b/src/edt/edt/edtRecentConfigurationPage.cc index 52d06d484..0d86a5ce5 100644 --- a/src/edt/edt/edtRecentConfigurationPage.cc +++ b/src/edt/edt/edtRecentConfigurationPage.cc @@ -29,6 +29,8 @@ namespace edt { +static const size_t max_entries = 100; + void RecentConfigurationPage::init () { @@ -36,6 +38,8 @@ RecentConfigurationPage::init () ly->setMargin (0); mp_tree_widget = new QTreeWidget (this); + mp_tree_widget->setRootIsDecorated (false); + mp_tree_widget->setUniformRowHeights (true); ly->addWidget (mp_tree_widget); mp_tree_widget->setColumnCount (int (m_cfg.size ())); @@ -46,7 +50,7 @@ RecentConfigurationPage::init () } mp_tree_widget->setHeaderLabels (column_labels); - update_list (); + update_list (get_stored_values ()); } RecentConfigurationPage::~RecentConfigurationPage () @@ -54,9 +58,59 @@ RecentConfigurationPage::~RecentConfigurationPage () // .. nothing yet .. } +std::string RecentConfigurationPage::title () const +{ + return tl::to_string (tr ("Recent")); +} + +int RecentConfigurationPage::order () const +{ + return 100; +} + +std::list > +RecentConfigurationPage::get_stored_values () const +{ + std::string serialized_list = dispatcher ()->config_get (m_recent_cfg_name); + + std::list > values; + tl::Extractor ex (serialized_list.c_str ()); + while (! ex.at_end ()) { + + values.push_back (std::vector ()); + while (! ex.at_end () && ! ex.test (";")) { + values.back ().push_back (std::string ()); + ex.read_word_or_quoted (values.back ().back ()); + ex.test (","); + } + + } + + return values; +} + +void +RecentConfigurationPage::set_stored_values (const std::list > &values) const +{ + std::string serialized_list; + for (std::list >::const_iterator v = values.begin (); v != values.end (); ++v) { + if (v != values.begin ()) { + serialized_list += ";"; + } + for (std::vector::const_iterator s = v->begin (); s != v->end (); ++s) { + serialized_list += tl::to_word_or_quoted_string (*s); + serialized_list += ","; + } + } + + dispatcher ()->config_set (m_recent_cfg_name, serialized_list); +} + void render_to (QTreeWidgetItem *item, int column, const std::string &v, RecentConfigurationPage::ConfigurationRendering rendering) { + // store original value + item->setData (column, Qt::UserRole, tl::to_qstring (v)); // @@@ rendering item->setText (column, tl::to_qstring (v)); @@ -64,10 +118,10 @@ render_to (QTreeWidgetItem *item, int column, const std::string &v, RecentConfig } void -RecentConfigurationPage::update_list () +RecentConfigurationPage::update_list (const std::list > &stored_values) { int row = 0; - for (std::list >::const_iterator v = m_stored_values.begin (); v != m_stored_values.end (); ++v, ++row) { + for (std::list >::const_iterator v = stored_values.begin (); v != stored_values.end (); ++v, ++row) { QTreeWidgetItem *item = 0; if (row < mp_tree_widget->topLevelItemCount ()) { @@ -102,16 +156,24 @@ RecentConfigurationPage::commit_recent (lay::Dispatcher *root) values.push_back (root->config_get (c->cfg_name)); } - for (std::list >::iterator v = m_stored_values.begin (); v != m_stored_values.end (); ++v) { + std::list > stored_values = get_stored_values (); + + for (std::list >::iterator v = stored_values.begin (); v != stored_values.end (); ++v) { if (*v == values) { - m_stored_values.erase (v); + stored_values.erase (v); break; } } - m_stored_values.push_front (values); + stored_values.push_front (values); - update_list (); + while (stored_values.size () > max_entries) { + stored_values.erase (--stored_values.end ()); + } + + set_stored_values (stored_values); + + update_list (stored_values); } } diff --git a/src/edt/edt/edtRecentConfigurationPage.h b/src/edt/edt/edtRecentConfigurationPage.h index 3cb290401..37bd35fcb 100644 --- a/src/edt/edt/edtRecentConfigurationPage.h +++ b/src/edt/edt/edtRecentConfigurationPage.h @@ -66,29 +66,29 @@ public: }; template - RecentConfigurationPage (lay::Dispatcher *dispatcher, int order, const std::string &title, Iter begin_cfg, Iter end_cfg) - : EditorOptionsPage (dispatcher), m_title (title), m_order (order), m_cfg (begin_cfg, end_cfg) + RecentConfigurationPage (lay::Dispatcher *dispatcher, const std::string &recent_cfg_name, Iter begin_cfg, Iter end_cfg) + : EditorOptionsPage (dispatcher), m_recent_cfg_name (recent_cfg_name), m_cfg (begin_cfg, end_cfg) { init (); } virtual ~RecentConfigurationPage (); - virtual std::string title () const { return m_title; } - virtual int order () const { return m_order; } + virtual std::string title () const; + virtual int order () const; virtual void apply (lay::Dispatcher * /*root*/) { } virtual void setup (lay::Dispatcher * /*root*/) { } virtual void commit_recent (lay::Dispatcher *root); private: - std::string m_title; - int m_order; + std::string m_recent_cfg_name; std::list m_cfg; QTreeWidget *mp_tree_widget; - std::list > m_stored_values; void init (); - void update_list (); + void update_list (const std::list > &stored_values); + std::list > get_stored_values () const; + void set_stored_values (const std::list > &values) const; }; } diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index f479b1c56..c035d6698 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -60,45 +60,6 @@ ac_from_buttons (unsigned int buttons) // ------------------------------------------------------------- -std::string pcell_parameters_to_string (const std::map ¶meters) -{ - std::string param; - - param = "!"; // flags PCells - for (std::map::const_iterator p = parameters.begin (); p != parameters.end (); ++p) { - param += tl::to_word_or_quoted_string (p->first); - param += ":"; - param += p->second.to_parsable_string (); - param += ";"; - } - - return param; -} - -std::map pcell_parameters_from_string (const std::string &s) -{ - tl::Extractor ex (s.c_str ()); - std::map pm; - - ex.test ("!"); - - try { - while (! ex.at_end ()) { - std::string n; - ex.read_word_or_quoted (n); - ex.test (":"); - ex.read (pm.insert (std::make_pair (n, tl::Variant ())).first->second); - ex.test (";"); - } - } catch (...) { - // ignore errors - } - - return pm; -} - -// ------------------------------------------------------------- - Service::Service (db::Manager *manager, lay::LayoutView *view, db::ShapeIterator::flags_type flags) : lay::ViewService (view->view_object_widget ()), lay::Editable (view), diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index 8f65a7e7a..637617ef4 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -1417,6 +1417,14 @@ InstService::do_mouse_transform (const db::DPoint &p, db::DFTrans trans) m_column_x = c.x (); m_column_y = c.y (); + dispatcher ()->config_set (cfg_edit_inst_angle, m_angle); + dispatcher ()->config_set (cfg_edit_inst_mirror, m_mirror); + dispatcher ()->config_set (cfg_edit_inst_row_x, m_row_x); + dispatcher ()->config_set (cfg_edit_inst_row_y, m_row_y); + dispatcher ()->config_set (cfg_edit_inst_column_x, m_column_x); + dispatcher ()->config_set (cfg_edit_inst_column_y, m_column_y); + dispatcher ()->config_end (); + // honour the new transformation do_mouse_move (p); } @@ -1535,78 +1543,171 @@ InstService::configure (const std::string &name, const std::string &value) if (name == cfg_edit_inst_pcell_parameters) { - m_pcell_parameters = pcell_parameters_from_string (value); - m_is_pcell = ! value.empty (); + std::map pcp = pcell_parameters_from_string (value); + if (pcp != m_pcell_parameters) { + + m_pcell_parameters = pcp; + m_is_pcell = ! value.empty (); + + m_needs_update = true; + + } - m_needs_update = true; return true; // taken } if (name == cfg_edit_inst_place_origin) { - tl::from_string (value, m_place_origin); - m_needs_update = true; + + bool f; + tl::from_string (value, f); + + if (f != m_place_origin) { + m_place_origin = f; + m_needs_update = true; + } + return true; // taken + } if (name == cfg_edit_inst_scale) { - tl::from_string (value, m_scale); - m_needs_update = true; + + double s; + tl::from_string (value, s); + + if (fabs (s - m_scale) > 1e-10) { + m_scale = s; + m_needs_update = true; + } + return true; // taken + } if (name == cfg_edit_inst_angle) { - tl::from_string (value, m_angle); - m_needs_update = true; + + double a; + tl::from_string (value, a); + + if (fabs (a - m_angle) > 1e-10) { + m_angle = a; + m_needs_update = true; + } + return true; // taken } if (name == cfg_edit_inst_mirror) { - tl::from_string (value, m_mirror); - m_needs_update = true; + + bool f; + tl::from_string (value, f); + + if (f != m_mirror) { + m_mirror = f; + m_needs_update = true; + } + return true; // taken + } if (name == cfg_edit_inst_array) { - tl::from_string (value, m_array); - m_needs_update = true; + + bool f; + tl::from_string (value, f); + + if (f != m_array) { + m_array = f; + m_needs_update = true; + } + return true; // taken + } if (name == cfg_edit_inst_rows) { - tl::from_string (value, m_rows); - m_needs_update = true; + + unsigned int v; + tl::from_string (value, v); + + if (v != m_rows) { + m_rows = v; + m_needs_update = true; + } + return true; // taken + } if (name == cfg_edit_inst_row_x) { - tl::from_string (value, m_row_x); - m_needs_update = true; + + double v; + tl::from_string (value, v); + + if (! db::coord_traits::equal (m_row_x, v)) { + m_row_x = v; + m_needs_update = true; + } + return true; // taken + } if (name == cfg_edit_inst_row_y) { - tl::from_string (value, m_row_y); - m_needs_update = true; + + double v; + tl::from_string (value, v); + + if (! db::coord_traits::equal (m_row_y, v)) { + m_row_y = v; + m_needs_update = true; + } + return true; // taken + } if (name == cfg_edit_inst_columns) { - tl::from_string (value, m_columns); - m_needs_update = true; + + unsigned int v; + tl::from_string (value, v); + + if (v != m_columns) { + m_columns = v; + m_needs_update = true; + } + return true; // taken + } if (name == cfg_edit_inst_column_x) { - tl::from_string (value, m_column_x); - m_needs_update = true; + + double v; + tl::from_string (value, v); + + if (! db::coord_traits::equal (m_column_x, v)) { + m_column_x = v; + m_needs_update = true; + } + return true; // taken + } if (name == cfg_edit_inst_column_y) { - tl::from_string (value, m_column_y); - m_needs_update = true; + + double v; + tl::from_string (value, v); + + if (! db::coord_traits::equal (m_column_y, v)) { + m_column_y = v; + m_needs_update = true; + } + return true; // taken + } return edt::Service::configure (name, value); diff --git a/src/edt/edt/edtUtils.cc b/src/edt/edt/edtUtils.cc index 7cc997bf7..a4833a0a1 100644 --- a/src/edt/edt/edtUtils.cc +++ b/src/edt/edt/edtUtils.cc @@ -35,6 +35,45 @@ namespace edt { +// ------------------------------------------------------------- + +std::string pcell_parameters_to_string (const std::map ¶meters) +{ + std::string param; + + param = "!"; // flags PCells + for (std::map::const_iterator p = parameters.begin (); p != parameters.end (); ++p) { + param += tl::to_word_or_quoted_string (p->first); + param += ":"; + param += p->second.to_parsable_string (); + param += ";"; + } + + return param; +} + +std::map pcell_parameters_from_string (const std::string &s) +{ + tl::Extractor ex (s.c_str ()); + std::map pm; + + ex.test ("!"); + + try { + while (! ex.at_end ()) { + std::string n; + ex.read_word_or_quoted (n); + ex.test (":"); + ex.read (pm.insert (std::make_pair (n, tl::Variant ())).first->second); + ex.test (";"); + } + } catch (...) { + // ignore errors + } + + return pm; +} + // ------------------------------------------------------------- // SelectionIterator implementation diff --git a/src/edt/edt/edtUtils.h b/src/edt/edt/edtUtils.h index a4a512e7a..460f9a12e 100644 --- a/src/edt/edt/edtUtils.h +++ b/src/edt/edt/edtUtils.h @@ -47,6 +47,16 @@ namespace edt { class Service; +/** + * @brief Serializes PCell parameters to a string + */ +std::string pcell_parameters_to_string (const std::map ¶meters); + +/** + * @brief Deerializes PCell parameters from a string + */ +std::map pcell_parameters_from_string (const std::string &s); + /** * @brief Fetch PCell parameters from a cell and merge the guiding shapes into them * From 9a19ced4e0333a31c9f205e843364313d4b89fa2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Aug 2020 00:01:59 +0200 Subject: [PATCH 056/113] WIP: recent configurations for instances, some bug fixes there. Views now reset tool if the tab changes. --- src/edt/edt/edtPlugin.cc | 21 ++-- src/edt/edt/edtRecentConfigurationPage.cc | 132 +++++++++++++++++++++- src/edt/edt/edtRecentConfigurationPage.h | 11 +- src/edt/edt/edtServiceImpl.cc | 50 +++++--- src/edt/edt/edtServiceImpl.h | 4 +- src/lay/lay/layMainWindow.cc | 2 + 6 files changed, 185 insertions(+), 35 deletions(-) diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index ec50d70c6..926325850 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -88,20 +88,19 @@ void get_inst_options (std::vector < std::pair > &opti edt::RecentConfigurationPage::ConfigurationDescriptor inst_cfg_descriptors[] = { - edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_lib_name, tl::to_string (tr ("Library")), edt::RecentConfigurationPage::Text), - edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_cell_name, tl::to_string (tr ("Cell")), edt::RecentConfigurationPage::Text), - // encode this into the cell? - edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_pcell_parameters, tl::to_string (tr ("PCell parameters")), edt::RecentConfigurationPage::PCellParamters), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_lib_name, tl::to_string (tr ("Library")), edt::RecentConfigurationPage::CellLibraryName), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_cell_name, tl::to_string (tr ("Cell")), edt::RecentConfigurationPage::CellDisplayName), edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_angle, tl::to_string (tr ("Angle")), edt::RecentConfigurationPage::Double), edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_mirror, tl::to_string (tr ("Mirror")), edt::RecentConfigurationPage::Bool), edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_scale, tl::to_string (tr ("Scale")), edt::RecentConfigurationPage::Double), - edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_array, tl::to_string (tr ("Array")), edt::RecentConfigurationPage::Bool), - edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_rows, tl::to_string (tr ("Rows")), edt::RecentConfigurationPage::Double), - edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_row_x, tl::to_string (tr ("Row step (x)")), edt::RecentConfigurationPage::Double), - edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_row_y, tl::to_string (tr ("Row step (y)")), edt::RecentConfigurationPage::Double), - edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_columns, tl::to_string (tr ("Columns")), edt::RecentConfigurationPage::Double), - edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_column_x, tl::to_string (tr ("Column step (x)")), edt::RecentConfigurationPage::Double), - edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_column_y, tl::to_string (tr ("Column step (y)")), edt::RecentConfigurationPage::Double) + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_array, tl::to_string (tr ("Array")), edt::RecentConfigurationPage::ArrayFlag), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_rows, tl::to_string (tr ("Rows")), edt::RecentConfigurationPage::IntIfArray), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_row_x, tl::to_string (tr ("Row step (x)")), edt::RecentConfigurationPage::DoubleIfArray), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_row_y, tl::to_string (tr ("Row step (y)")), edt::RecentConfigurationPage::DoubleIfArray), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_columns, tl::to_string (tr ("Columns")), edt::RecentConfigurationPage::IntIfArray), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_column_x, tl::to_string (tr ("Column step (x)")), edt::RecentConfigurationPage::DoubleIfArray), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_column_y, tl::to_string (tr ("Column step (y)")), edt::RecentConfigurationPage::DoubleIfArray), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_pcell_parameters, tl::to_string (tr ("PCell parameters")), edt::RecentConfigurationPage::PCellParameters) }; static diff --git a/src/edt/edt/edtRecentConfigurationPage.cc b/src/edt/edt/edtRecentConfigurationPage.cc index 0d86a5ce5..1d9bcec56 100644 --- a/src/edt/edt/edtRecentConfigurationPage.cc +++ b/src/edt/edt/edtRecentConfigurationPage.cc @@ -21,10 +21,14 @@ */ #include "edtRecentConfigurationPage.h" +#include "edtUtils.h" #include "layDispatcher.h" +#include "dbLibraryManager.h" +#include "dbLibrary.h" #include #include +#include namespace edt { @@ -37,11 +41,19 @@ RecentConfigurationPage::init () QVBoxLayout *ly = new QVBoxLayout (this); ly->setMargin (0); + QLabel *label = new QLabel (this); + label->setText (tr ("Click to select a recent configuration")); + ly->addWidget (label); + mp_tree_widget = new QTreeWidget (this); mp_tree_widget->setRootIsDecorated (false); mp_tree_widget->setUniformRowHeights (true); + mp_tree_widget->setSelectionMode (QAbstractItemView::NoSelection); + mp_tree_widget->setAllColumnsShowFocus (true); ly->addWidget (mp_tree_widget); + connect (mp_tree_widget, SIGNAL (itemClicked (QTreeWidgetItem *, int)), this, SLOT (item_clicked (QTreeWidgetItem *))); + mp_tree_widget->setColumnCount (int (m_cfg.size ())); QStringList column_labels; @@ -107,13 +119,112 @@ RecentConfigurationPage::set_stored_values (const std::list &values, RecentConfigurationPage::ConfigurationRendering rendering) { // store original value - item->setData (column, Qt::UserRole, tl::to_qstring (v)); + item->setData (column, Qt::UserRole, tl::to_qstring (values [column])); - // @@@ rendering - item->setText (column, tl::to_qstring (v)); + switch (rendering) { + + case RecentConfigurationPage::ArrayFlag: + case RecentConfigurationPage::Bool: + { + bool f = false; + tl::from_string (values [column], f); + static QString checkmark = QString::fromUtf8 ("\xe2\x9c\x93"); + item->setText (column, f ? checkmark : QString ()); // "checkmark" + } + break; + + case RecentConfigurationPage::Layer: + // @@@ + item->setText (column, tl::to_qstring (values [column])); + break; + + case RecentConfigurationPage::Int: + case RecentConfigurationPage::Double: + case RecentConfigurationPage::Text: + case RecentConfigurationPage::CellLibraryName: + item->setText (column, tl::to_qstring (values [column])); + break; + + case RecentConfigurationPage::IntIfArray: + case RecentConfigurationPage::DoubleIfArray: + { + bool is_array = false; + int flag_column = 0; + for (std::list::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c, ++flag_column) { + if (c->rendering == RecentConfigurationPage::ArrayFlag) { + tl::from_string (values [flag_column], is_array); + break; + } + } + + if (is_array) { + item->setText (column, tl::to_qstring (values [column])); + } else { + item->setText (column, QString ()); + } + } + break; + + case RecentConfigurationPage::CellDisplayName: + { + // search for a libname + int libname_column = 0; + const db::Library *lib = 0; + for (std::list::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c, ++libname_column) { + if (c->rendering == RecentConfigurationPage::CellLibraryName) { + lib = db::LibraryManager::instance ().lib_ptr_by_name (values [libname_column]); + break; + } + } + + if (lib) { + + // search for a PCell parameters + int pcp_column = 0; + std::map pcp; + for (std::list::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c, ++pcp_column) { + if (c->rendering == RecentConfigurationPage::PCellParameters) { + pcp = pcell_parameters_from_string (values [pcp_column]); + break; + } + } + + std::pair pcid = lib->layout ().pcell_by_name (values [column].c_str ()); + if (pcid.first) { + const db::PCellDeclaration *pc_decl = lib->layout ().pcell_declaration (pcid.second); + if (pc_decl) { + item->setText (column, tl::to_qstring (pc_decl->get_display_name (pc_decl->map_parameters (pcp)))); + break; + } + } + + } + + item->setText (column, tl::to_qstring (values [column])); + } + break; + + case RecentConfigurationPage::PCellParameters: + { + std::map pcp; + pcp = pcell_parameters_from_string (values [column]); + std::string r; + for (std::map::const_iterator p = pcp.begin (); p != pcp.end (); ++p) { + if (p != pcp.begin ()) { + r += ","; + } + r += p->first; + r += "="; + r += p->second.to_string (); + } + + item->setText (column, tl::to_qstring (r)); + } + break; + } } @@ -134,7 +245,7 @@ RecentConfigurationPage::update_list (const std::list > int column = 0; for (std::list::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c, ++column) { if (column < int (v->size ())) { - render_to (item, column, (*v) [column], c->rendering); + render_to (item, column, *v, c->rendering); } } @@ -147,6 +258,17 @@ RecentConfigurationPage::update_list (const std::list > mp_tree_widget->header ()->resizeSections (QHeaderView::ResizeToContents); } +void +RecentConfigurationPage::item_clicked (QTreeWidgetItem *item) +{ + int column = 0; + for (std::list::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c, ++column) { + std::string v = tl::to_string (item->data (column, Qt::UserRole).toString ()); + dispatcher ()->config_set (c->cfg_name, v); + } + dispatcher ()->config_end (); +} + void RecentConfigurationPage::commit_recent (lay::Dispatcher *root) { diff --git a/src/edt/edt/edtRecentConfigurationPage.h b/src/edt/edt/edtRecentConfigurationPage.h index 37bd35fcb..766a7f77f 100644 --- a/src/edt/edt/edtRecentConfigurationPage.h +++ b/src/edt/edt/edtRecentConfigurationPage.h @@ -52,7 +52,12 @@ public: Double = 2, Int = 3, Layer = 4, - PCellParamters = 5 + PCellParameters = 5, + CellLibraryName = 6, + CellDisplayName = 7, + ArrayFlag = 8, + DoubleIfArray = 9, + IntIfArray = 10 }; struct ConfigurationDescriptor @@ -80,6 +85,9 @@ public: virtual void setup (lay::Dispatcher * /*root*/) { } virtual void commit_recent (lay::Dispatcher *root); +private slots: + void item_clicked (QTreeWidgetItem *item); + private: std::string m_recent_cfg_name; std::list m_cfg; @@ -89,6 +97,7 @@ private: void update_list (const std::list > &stored_values); std::list > get_stored_values () const; void set_stored_values (const std::list > &values) const; + void render_to (QTreeWidgetItem *item, int column, const std::vector &values, RecentConfigurationPage::ConfigurationRendering rendering); }; } diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index 637617ef4..b4a339f0c 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -1093,7 +1093,7 @@ InstService::InstService (db::Manager *manager, lay::LayoutView *view) m_array (false), m_rows (1), m_columns (1), m_row_x (0.0), m_row_y (0.0), m_column_x (0.0), m_column_y (0.0), m_place_origin (false), m_reference_transaction_id (0), - m_needs_update (true), m_has_valid_cell (false), m_in_drag_drop (false), + m_needs_update (true), m_parameters_changed (false), m_has_valid_cell (false), m_in_drag_drop (false), m_current_cell (0), mp_current_layout (0), mp_pcell_decl (0), m_cv_index (-1) { // .. nothing yet .. @@ -1137,6 +1137,8 @@ InstService::drag_enter_event (const db::DPoint &p, const lay::DragDropDataBase view ()->cancel (); set_edit_marker (0); + bool switch_parameters = true; + // configure from the drag/drop data if (cd->library ()) { @@ -1168,6 +1170,7 @@ InstService::drag_enter_event (const db::DPoint &p, const lay::DragDropDataBase if (! cd->pcell_params ().empty ()) { m_pcell_parameters = pcell_decl->named_parameters (cd->pcell_params ()); + switch_parameters = false; } } else if (cd->layout ()->is_valid_cell_index (cd->cell_index ())) { @@ -1178,7 +1181,7 @@ InstService::drag_enter_event (const db::DPoint &p, const lay::DragDropDataBase return false; } - switch_cell_or_pcell (); + switch_cell_or_pcell (switch_parameters); sync_to_config (); m_in_drag_drop = true; @@ -1550,6 +1553,7 @@ InstService::configure (const std::string &name, const std::string &value) m_is_pcell = ! value.empty (); m_needs_update = true; + m_parameters_changed = true; } @@ -1714,7 +1718,7 @@ InstService::configure (const std::string &name, const std::string &value) } void -InstService::switch_cell_or_pcell () +InstService::switch_cell_or_pcell (bool switch_parameters) { // if the library or cell name has changed, store the current pcell parameters and try to reuse // an existing parameter set @@ -1722,11 +1726,15 @@ InstService::switch_cell_or_pcell () m_stored_pcell_parameters[std::make_pair (m_cell_or_pcell_name_previous, m_lib_name_previous)] = m_pcell_parameters; - std::map, std::map >::const_iterator p = m_stored_pcell_parameters.find (std::make_pair (m_cell_or_pcell_name, m_lib_name)); - if (p != m_stored_pcell_parameters.end ()) { - m_pcell_parameters = p->second; - } else { - m_pcell_parameters.clear (); + if (switch_parameters) { + + std::map, std::map >::const_iterator p = m_stored_pcell_parameters.find (std::make_pair (m_cell_or_pcell_name, m_lib_name)); + if (p != m_stored_pcell_parameters.end ()) { + m_pcell_parameters = p->second; + } else { + m_pcell_parameters.clear (); + } + } } @@ -1759,22 +1767,32 @@ InstService::config_finalize () { if (m_needs_update) { - switch_cell_or_pcell (); + // don't switch parameters if they have been updated explicitly + // since the last "config_finalize". This means the sender of the configuration events + // wants the parameters to be set in a specific way. Don't interfere. + bool switch_parameters = ! m_parameters_changed; + + switch_cell_or_pcell (switch_parameters); m_has_valid_cell = false; update_marker (); - m_needs_update = false; - // Reflects any changes in PCell parameters induced by reuse of make_cell (called from update_marker) - // in the configuration - if (m_is_pcell) { - dispatcher ()->config_set (cfg_edit_inst_pcell_parameters, pcell_parameters_to_string (m_pcell_parameters)); - } else { - dispatcher ()->config_set (cfg_edit_inst_pcell_parameters, std::string ()); + if (switch_parameters) { + // Reflects any changes in PCell parameters in the configuration + // TODO: it's somewhat questionable to do this inside "config_finalize" as this method is supposed + // to reflect changes rather than induce some. + if (m_is_pcell) { + dispatcher ()->config_set (cfg_edit_inst_pcell_parameters, pcell_parameters_to_string (m_pcell_parameters)); + } else { + dispatcher ()->config_set (cfg_edit_inst_pcell_parameters, std::string ()); + } } } + m_needs_update = false; + m_parameters_changed = false; + edt::Service::config_finalize (); } diff --git a/src/edt/edt/edtServiceImpl.h b/src/edt/edt/edtServiceImpl.h index f9545694a..d892bae43 100644 --- a/src/edt/edt/edtServiceImpl.h +++ b/src/edt/edt/edtServiceImpl.h @@ -239,7 +239,7 @@ private: double m_row_x, m_row_y, m_column_x, m_column_y; bool m_place_origin; db::Manager::transaction_id_t m_reference_transaction_id; - bool m_needs_update; + bool m_needs_update, m_parameters_changed; bool m_has_valid_cell; bool m_in_drag_drop; db::cell_index_type m_current_cell; @@ -253,7 +253,7 @@ private: std::pair make_cell (const lay::CellView &cv); tl::Variant get_default_layer_for_pcell (); void sync_to_config (); - void switch_cell_or_pcell (); + void switch_cell_or_pcell (bool switch_parameters); }; } diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 0a7bacd70..36b9bb96c 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -2603,6 +2603,8 @@ MainWindow::select_view (int index) try { + cancel (); + tl_assert (index >= 0 && index < int (views ())); mp_tab_bar->setCurrentIndex (index); From 263e04c7545030d9a779aebb7ed47dcf75f46cf1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Aug 2020 00:39:19 +0200 Subject: [PATCH 057/113] WIP: recent editor options also for paths. --- src/edt/edt/edtPlugin.cc | 33 ++++++++++------ src/edt/edt/edtPlugin.h | 2 +- src/edt/edt/edtRecentConfigurationPage.cc | 47 ++++++++++++++++++++++- src/edt/edt/edtRecentConfigurationPage.h | 10 ++++- src/edt/edt/edtServiceImpl.cc | 2 + 5 files changed, 78 insertions(+), 16 deletions(-) diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 926325850..46b3f664b 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -47,7 +47,7 @@ void get_text_options (std::vector < std::pair > &opti } static -void get_text_editor_options_pages (std::vector &ret, lay::Dispatcher *dispatcher) +void get_text_editor_options_pages (std::vector &ret, lay::LayoutView *, lay::Dispatcher *dispatcher) { ret.push_back (new edt::EditorOptionsText (dispatcher)); } @@ -61,9 +61,20 @@ void get_path_options (std::vector < std::pair > &opti options.push_back (std::pair (cfg_edit_path_ext_var_end, "0.0")); } -static -void get_path_editor_options_pages (std::vector &ret, lay::Dispatcher *dispatcher) +edt::RecentConfigurationPage::ConfigurationDescriptor path_cfg_descriptors[] = { + edt::RecentConfigurationPage::ConfigurationDescriptor ("", tl::to_string (tr ("Layer")), edt::RecentConfigurationPage::Layer), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_path_width, tl::to_string (tr ("Width")), edt::RecentConfigurationPage::Double), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_path_ext_type, tl::to_string (tr ("Ends")), edt::RecentConfigurationPage::Int), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_path_ext_var_begin, tl::to_string (tr ("Begin ext.")), edt::RecentConfigurationPage::Double), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_path_ext_var_end, tl::to_string (tr ("End ext.")), edt::RecentConfigurationPage::Double) +}; + +static +void get_path_editor_options_pages (std::vector &ret, lay::LayoutView *view, lay::Dispatcher *dispatcher) +{ + ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-path-param", + &path_cfg_descriptors[0], &path_cfg_descriptors[sizeof (path_cfg_descriptors) / sizeof (path_cfg_descriptors[0])])); ret.push_back (new EditorOptionsPath (dispatcher)); } @@ -103,10 +114,10 @@ edt::RecentConfigurationPage::ConfigurationDescriptor inst_cfg_descriptors[] = edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_pcell_parameters, tl::to_string (tr ("PCell parameters")), edt::RecentConfigurationPage::PCellParameters) }; -static -void get_inst_editor_options_pages (std::vector &ret, lay::Dispatcher *dispatcher) +static +void get_inst_editor_options_pages (std::vector &ret, lay::LayoutView *view, lay::Dispatcher *dispatcher) { - ret.push_back (new RecentConfigurationPage (dispatcher, "edit-recent-inst-param", + ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-inst-param", &inst_cfg_descriptors[0], &inst_cfg_descriptors[sizeof (inst_cfg_descriptors) / sizeof (inst_cfg_descriptors[0])])); ret.push_back (new EditorOptionsInstPCellParam (dispatcher)); ret.push_back (new EditorOptionsInst (dispatcher)); @@ -119,7 +130,7 @@ class PluginDeclaration public: PluginDeclaration (const std::string &title, const std::string &mouse_mode, void (*option_get_f) (std::vector < std::pair > &) = 0, - void (*pages_f) (std::vector &, lay::Dispatcher *) = 0) + void (*pages_f) (std::vector &, lay::LayoutView *, lay::Dispatcher *) = 0) : m_title (title), m_mouse_mode (mouse_mode), mp_option_get_f (option_get_f), mp_pages_f (pages_f) { // .. nothing yet .. @@ -142,11 +153,11 @@ public: // .. nothing yet .. } - virtual void get_editor_options_pages (std::vector &pages, lay::Dispatcher *root) const + virtual void get_editor_options_pages (std::vector &pages, lay::LayoutView *view, lay::Dispatcher *root) const { if (mp_pages_f != 0) { size_t nstart = pages.size (); - (*mp_pages_f) (pages, root); + (*mp_pages_f) (pages, view, root); while (nstart < pages.size ()) { pages [nstart++]->set_plugin_declaration (this); } @@ -177,7 +188,7 @@ private: std::string m_mouse_mode; void (*mp_option_get_f) (std::vector < std::pair > &options); - void (*mp_pages_f) (std::vector &, lay::Dispatcher *); + void (*mp_pages_f) (std::vector &, lay::LayoutView *, lay::Dispatcher *); }; static tl::RegisteredClass config_decl1 ( @@ -382,7 +393,7 @@ show_editor_options_page (lay::LayoutView *view) for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { const PluginDeclarationBase *pd_base = dynamic_cast (&*cls); if (pd_base) { - pd_base->get_editor_options_pages (prop_dialog_pages, view->dispatcher ()); + pd_base->get_editor_options_pages (prop_dialog_pages, view, view->dispatcher ()); } } diff --git a/src/edt/edt/edtPlugin.h b/src/edt/edt/edtPlugin.h index 412f51d97..c0ede8408 100644 --- a/src/edt/edt/edtPlugin.h +++ b/src/edt/edt/edtPlugin.h @@ -44,7 +44,7 @@ namespace edt : public lay::PluginDeclaration { public: - virtual void get_editor_options_pages (std::vector &, lay::Dispatcher *) const = 0; + virtual void get_editor_options_pages (std::vector &, lay::LayoutView *, lay::Dispatcher *) const = 0; }; /** diff --git a/src/edt/edt/edtRecentConfigurationPage.cc b/src/edt/edt/edtRecentConfigurationPage.cc index 1d9bcec56..3d329c5c0 100644 --- a/src/edt/edt/edtRecentConfigurationPage.cc +++ b/src/edt/edt/edtRecentConfigurationPage.cc @@ -23,6 +23,7 @@ #include "edtRecentConfigurationPage.h" #include "edtUtils.h" #include "layDispatcher.h" +#include "layLayoutView.h" #include "dbLibraryManager.h" #include "dbLibrary.h" @@ -263,9 +264,28 @@ RecentConfigurationPage::item_clicked (QTreeWidgetItem *item) { int column = 0; for (std::list::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c, ++column) { + std::string v = tl::to_string (item->data (column, Qt::UserRole).toString ()); - dispatcher ()->config_set (c->cfg_name, v); + + if (c->rendering == Layer) { + + // "getting" a layer means making it current + db::LayerProperties lp; + tl::Extractor ex (v.c_str ()); + lp.read (ex); + int cv_index = 0; + if (ex.test ("@")) { + ex.read (cv_index); + } + + mp_view->set_current_layer (cv_index, lp); + + } else { + dispatcher ()->config_set (c->cfg_name, v); + } + } + dispatcher ()->config_end (); } @@ -275,7 +295,30 @@ RecentConfigurationPage::commit_recent (lay::Dispatcher *root) std::vector values; values.reserve (m_cfg.size ()); for (std::list::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c) { - values.push_back (root->config_get (c->cfg_name)); + if (c->rendering == Layer) { + + std::string s; + + if (mp_view->current_layer ()->is_visual ()) { + + int cv_index = mp_view->current_layer ()->cellview_index (); + const lay::CellView &cv = mp_view->cellview (cv_index); + int li = mp_view->current_layer ()->layer_index (); + if (cv.is_valid () && cv->layout ().is_valid_layer (li)) { + s = cv->layout ().get_properties (li).to_string (); + if (cv_index > 0) { + s += "@" + tl::to_string (cv_index); + } + } + + } + + values.push_back (s); + + } else { + values.push_back (root->config_get (c->cfg_name)); + } + } std::list > stored_values = get_stored_values (); diff --git a/src/edt/edt/edtRecentConfigurationPage.h b/src/edt/edt/edtRecentConfigurationPage.h index 766a7f77f..81b6b493f 100644 --- a/src/edt/edt/edtRecentConfigurationPage.h +++ b/src/edt/edt/edtRecentConfigurationPage.h @@ -29,6 +29,11 @@ #include #include +namespace lay +{ + class LayoutView; +} + namespace edt { @@ -71,8 +76,8 @@ public: }; template - RecentConfigurationPage (lay::Dispatcher *dispatcher, const std::string &recent_cfg_name, Iter begin_cfg, Iter end_cfg) - : EditorOptionsPage (dispatcher), m_recent_cfg_name (recent_cfg_name), m_cfg (begin_cfg, end_cfg) + RecentConfigurationPage (lay::LayoutView *view, lay::Dispatcher *dispatcher, const std::string &recent_cfg_name, Iter begin_cfg, Iter end_cfg) + : EditorOptionsPage (dispatcher), mp_view (view), m_recent_cfg_name (recent_cfg_name), m_cfg (begin_cfg, end_cfg) { init (); } @@ -89,6 +94,7 @@ private slots: void item_clicked (QTreeWidgetItem *item); private: + lay::LayoutView *mp_view; std::string m_recent_cfg_name; std::list m_cfg; QTreeWidget *mp_tree_widget; diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index b4a339f0c..2cd448fdc 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -965,6 +965,8 @@ PathService::do_finish_edit () m_points.pop_back (); deliver_shape (get_path ()); + + commit_recent (view ()); } void From 59d66676b0931eb13c90387839f5e298754f79a1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Aug 2020 00:57:04 +0200 Subject: [PATCH 058/113] WIP: icons for layers --- src/edt/edt/edtRecentConfigurationPage.cc | 38 +++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/edt/edt/edtRecentConfigurationPage.cc b/src/edt/edt/edtRecentConfigurationPage.cc index 3d329c5c0..da0cc6b7c 100644 --- a/src/edt/edt/edtRecentConfigurationPage.cc +++ b/src/edt/edt/edtRecentConfigurationPage.cc @@ -24,6 +24,7 @@ #include "edtUtils.h" #include "layDispatcher.h" #include "layLayoutView.h" +#include "layLayerTreeModel.h" #include "dbLibraryManager.h" #include "dbLibrary.h" @@ -119,6 +120,31 @@ RecentConfigurationPage::set_stored_values (const std::listconfig_set (m_recent_cfg_name, serialized_list); } +static lay::LayerPropertiesConstIterator +lp_iter_from_string (lay::LayoutView *view, const std::string &s) +{ + // parse the layer spec ([@]) + db::LayerProperties lp; + tl::Extractor ex (s.c_str ()); + lp.read (ex); + int cv_index = 0; + if (ex.test ("@")) { + ex.read (cv_index); + } + + // rename the ones that got shifted. + lay::LayerPropertiesConstIterator l = view->begin_layers (); + while (! l.at_end ()) { + if (l->source (true).cv_index () == int (cv_index) && l->source (true).layer_props ().log_equal (lp)) { + return l; + } + ++l; + } + + return l; +} + + void RecentConfigurationPage::render_to (QTreeWidgetItem *item, int column, const std::vector &values, RecentConfigurationPage::ConfigurationRendering rendering) { @@ -138,8 +164,15 @@ RecentConfigurationPage::render_to (QTreeWidgetItem *item, int column, const std break; case RecentConfigurationPage::Layer: - // @@@ - item->setText (column, tl::to_qstring (values [column])); + { + // @@@ TODO: icons should be updated if style changes? + int icon_size = mp_view->style ()->pixelMetric (QStyle::PM_ButtonIconSize); + lay::LayerPropertiesConstIterator l = lp_iter_from_string (mp_view, values [column]); + if (! l.at_end ()) { + item->setIcon (column, lay::LayerTreeModel::icon_for_layer (l, mp_view, icon_size, icon_size, 0, true)); + } + item->setText (column, tl::to_qstring (values [column])); + } break; case RecentConfigurationPage::Int: @@ -295,6 +328,7 @@ RecentConfigurationPage::commit_recent (lay::Dispatcher *root) std::vector values; values.reserve (m_cfg.size ()); for (std::list::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c) { + if (c->rendering == Layer) { std::string s; From 77ef3e049bb025a3587048e3456d59214f29d9d8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Aug 2020 21:09:05 +0200 Subject: [PATCH 059/113] WIP: recent options for paths - now update on layer style change, some bug fixes, change of current layer will immediately change the drawing layer. --- src/edt/edt/edtPlugin.cc | 4 +- src/edt/edt/edtRecentConfigurationPage.cc | 26 ++++-- src/edt/edt/edtRecentConfigurationPage.h | 4 +- src/edt/edt/edtServiceImpl.cc | 79 +++++++++++++++++-- src/edt/edt/edtServiceImpl.h | 3 + src/laybasic/laybasic/layLayerControlPanel.cc | 18 +++-- src/laybasic/laybasic/layLayerControlPanel.h | 2 + src/laybasic/laybasic/layLayoutView.cc | 7 ++ src/laybasic/laybasic/layLayoutView.h | 26 ++++-- 9 files changed, 143 insertions(+), 26 deletions(-) diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 46b3f664b..05748747b 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -462,7 +462,9 @@ commit_recent (lay::LayoutView *view) } for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { - (*op)->commit_recent (view); + if ((*op)->active ()) { + (*op)->commit_recent (view); + } } } diff --git a/src/edt/edt/edtRecentConfigurationPage.cc b/src/edt/edt/edtRecentConfigurationPage.cc index da0cc6b7c..6c4b998cc 100644 --- a/src/edt/edt/edtRecentConfigurationPage.cc +++ b/src/edt/edt/edtRecentConfigurationPage.cc @@ -55,6 +55,7 @@ RecentConfigurationPage::init () ly->addWidget (mp_tree_widget); connect (mp_tree_widget, SIGNAL (itemClicked (QTreeWidgetItem *, int)), this, SLOT (item_clicked (QTreeWidgetItem *))); + mp_view->layer_list_changed_event.add (this, &RecentConfigurationPage::layers_changed); mp_tree_widget->setColumnCount (int (m_cfg.size ())); @@ -165,23 +166,32 @@ RecentConfigurationPage::render_to (QTreeWidgetItem *item, int column, const std case RecentConfigurationPage::Layer: { - // @@@ TODO: icons should be updated if style changes? int icon_size = mp_view->style ()->pixelMetric (QStyle::PM_ButtonIconSize); lay::LayerPropertiesConstIterator l = lp_iter_from_string (mp_view, values [column]); - if (! l.at_end ()) { + if (! l.is_null () && ! l.at_end ()) { item->setIcon (column, lay::LayerTreeModel::icon_for_layer (l, mp_view, icon_size, icon_size, 0, true)); + item->setText (column, tl::to_qstring (values [column])); + } else { + item->setIcon (column, QIcon ()); + item->setText (column, tl::to_qstring ("(" + values [column] + ")")); } - item->setText (column, tl::to_qstring (values [column])); } break; case RecentConfigurationPage::Int: case RecentConfigurationPage::Double: case RecentConfigurationPage::Text: - case RecentConfigurationPage::CellLibraryName: item->setText (column, tl::to_qstring (values [column])); break; + case RecentConfigurationPage::CellLibraryName: + if (values [column].empty ()) { + item->setText (column, tr ("(local)")); + } else { + item->setText (column, tl::to_qstring (values [column])); + } + break; + case RecentConfigurationPage::IntIfArray: case RecentConfigurationPage::DoubleIfArray: { @@ -262,6 +272,12 @@ RecentConfigurationPage::render_to (QTreeWidgetItem *item, int column, const std } +void +RecentConfigurationPage::layers_changed (int) +{ + update_list (get_stored_values ()); +} + void RecentConfigurationPage::update_list (const std::list > &stored_values) { @@ -333,7 +349,7 @@ RecentConfigurationPage::commit_recent (lay::Dispatcher *root) std::string s; - if (mp_view->current_layer ()->is_visual ()) { + if (!(mp_view->current_layer ().is_null () || mp_view->current_layer ().at_end ()) && mp_view->current_layer ()->is_visual ()) { int cv_index = mp_view->current_layer ()->cellview_index (); const lay::CellView &cv = mp_view->cellview (cv_index); diff --git a/src/edt/edt/edtRecentConfigurationPage.h b/src/edt/edt/edtRecentConfigurationPage.h index 81b6b493f..6f4adaea2 100644 --- a/src/edt/edt/edtRecentConfigurationPage.h +++ b/src/edt/edt/edtRecentConfigurationPage.h @@ -25,6 +25,7 @@ #define HDR_edtRecentConfigurationPage #include "edtEditorOptionsPage.h" +#include "tlObject.h" #include #include @@ -45,7 +46,7 @@ class EditorOptionsPages; * @brief The base class for a object properties page */ class RecentConfigurationPage - : public EditorOptionsPage + : public EditorOptionsPage, public tl::Object { Q_OBJECT @@ -104,6 +105,7 @@ private: std::list > get_stored_values () const; void set_stored_values (const std::list > &values) const; void render_to (QTreeWidgetItem *item, int column, const std::vector &values, RecentConfigurationPage::ConfigurationRendering rendering); + void layers_changed (int); }; } diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index 2cd448fdc..f105d1031 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -51,7 +51,7 @@ ShapeEditService::ShapeEditService (db::Manager *manager, lay::LayoutView *view, : edt::Service (manager, view, shape_types), m_layer (0), m_cv_index (0), mp_cell (0), mp_layout (0), m_combine_mode (CM_Add) { - // .. nothing yet .. + view->current_layer_changed_event.add (this, &ShapeEditService::update_edit_layer); } bool @@ -74,13 +74,6 @@ ShapeEditService::get_edit_layer () throw tl::Exception (tl::to_string (QObject::tr ("Please select a layer first"))); } - if (! cl->visible (true)) { - lay::TipDialog td (QApplication::activeWindow (), - tl::to_string (QObject::tr ("You are about to draw on a hidden layer. The result won't be visible.")), - "drawing-on-invisible-layer"); - td.exec_dialog (); - } - int cv_index = cl->cellview_index (); const lay::CellView &cv = view ()->cellview (cv_index); int layer = cl->layer_index (); @@ -89,6 +82,13 @@ ShapeEditService::get_edit_layer () throw tl::Exception (tl::to_string (QObject::tr ("Please select a cell first"))); } + if (! cl->visible (true)) { + lay::TipDialog td (QApplication::activeWindow (), + tl::to_string (QObject::tr ("You are about to draw on a hidden layer. The result won't be visible.")), + "drawing-on-invisible-layer"); + td.exec_dialog (); + } + if (layer < 0 || ! cv->layout ().is_valid_layer ((unsigned int) layer)) { if (cl->has_children ()) { @@ -129,6 +129,69 @@ ShapeEditService::get_edit_layer () } } +void +ShapeEditService::update_edit_layer (const lay::LayerPropertiesConstIterator &cl) +{ + if (! editing ()) { + return; + } + + if (cl.is_null () || cl->has_children ()) { + return; + } + + int cv_index = cl->cellview_index (); + const lay::CellView &cv = view ()->cellview (cv_index); + int layer = cl->layer_index (); + + if (cv_index < 0 || ! cv.is_valid ()) { + return; + } + + if (cv->layout ().cell (cv.cell_index ()).is_proxy ()) { + return; + } + + if (! cl->visible (true)) { + lay::TipDialog td (QApplication::activeWindow (), + tl::to_string (QObject::tr ("You are now drawing on a hidden layer. The result won't be visible.")), + "drawing-on-invisible-layer"); + td.exec_dialog (); + } + + if (layer < 0 || ! cv->layout ().is_valid_layer ((unsigned int) layer)) { + + // create this layer now + const lay::ParsedLayerSource &source = cl->source (true /*real*/); + + db::LayerProperties db_lp; + if (source.has_name ()) { + db_lp.name = source.name (); + } + db_lp.layer = source.layer (); + db_lp.datatype = source.datatype (); + + cv->layout ().insert_layer (db_lp); + + // update the layer index inside the layer view + cl->realize_source (); + + // Hint: we could have taken the new index from insert_layer, but this + // is a nice test: + layer = cl->layer_index (); + tl_assert (layer >= 0); + + } + + m_layer = (unsigned int) layer; + m_cv_index = (unsigned int) cv_index; + m_trans = (cl->trans ().front () * db::CplxTrans (cv->layout ().dbu ()) * cv.context_trans ()).inverted (); + mp_layout = &(cv->layout ()); + mp_cell = &(mp_layout->cell (cv.cell_index ())); + + current_layer_changed (); +} + void ShapeEditService::tap (const db::DPoint &initial) { diff --git a/src/edt/edt/edtServiceImpl.h b/src/edt/edt/edtServiceImpl.h index d892bae43..37c8cf035 100644 --- a/src/edt/edt/edtServiceImpl.h +++ b/src/edt/edt/edtServiceImpl.h @@ -66,6 +66,7 @@ protected: void deliver_shape (const db::Polygon &poly); void deliver_shape (const db::Path &path); void deliver_shape (const db::Box &box); + virtual void current_layer_changed () { } private: db::VCplxTrans m_trans; @@ -74,6 +75,8 @@ private: db::Cell *mp_cell; db::Layout *mp_layout; combine_mode_type m_combine_mode; + + void update_edit_layer (const lay::LayerPropertiesConstIterator &iter); }; /** diff --git a/src/laybasic/laybasic/layLayerControlPanel.cc b/src/laybasic/laybasic/layLayerControlPanel.cc index 28ab6a8bc..4c5c8f884 100644 --- a/src/laybasic/laybasic/layLayerControlPanel.cc +++ b/src/laybasic/laybasic/layLayerControlPanel.cc @@ -299,17 +299,12 @@ LayerControlPanel::LayerControlPanel (lay::LayoutView *view, db::Manager *manage mp_model->set_font (mp_layer_list->font ()); mp_layer_list->setIconSize (mp_model->icon_size ()); - /* - * At least with Qt 4.2.x setting uniform row heights has a strange side effect: - * If a range is selected and the first selection is scrolled out of view, the - * range does not include the first element after having clicked at the second. - mp_layer_list->setUniformRowHeights (true); - */ l->addWidget (mp_layer_list); connect (mp_layer_list, SIGNAL (double_clicked (const QModelIndex &, Qt::KeyboardModifiers)), this, SLOT (double_clicked (const QModelIndex &, Qt::KeyboardModifiers))); connect (mp_layer_list, SIGNAL (collapsed (const QModelIndex &)), this, SLOT (group_collapsed (const QModelIndex &))); connect (mp_layer_list, SIGNAL (expanded (const QModelIndex &)), this, SLOT (group_expanded (const QModelIndex &))); connect (mp_layer_list, SIGNAL (search_triggered (const QString &)), this, SLOT (search_triggered (const QString &))); + connect (mp_layer_list->selectionModel (), SIGNAL (currentChanged (const QModelIndex &, const QModelIndex &)), this, SLOT (current_index_changed (const QModelIndex &))); mp_layer_list->setContextMenuPolicy (Qt::CustomContextMenu); connect (mp_layer_list, SIGNAL(customContextMenuRequested (const QPoint &)), this, SLOT (context_menu (const QPoint &))); mp_layer_list->header ()->hide (); @@ -2041,6 +2036,17 @@ LayerControlPanel::update_required (int f) m_do_update_content_dm (); } +void +LayerControlPanel::current_index_changed (const QModelIndex &index) +{ + lay::LayerPropertiesConstIterator iter = mp_model->iterator (index); + if (! iter.is_null () && ! iter.at_end ()) { + emit current_layer_changed (iter); + } else { + emit current_layer_changed (lay::LayerPropertiesConstIterator ()); + } +} + void LayerControlPanel::group_collapsed (const QModelIndex &index) { diff --git a/src/laybasic/laybasic/layLayerControlPanel.h b/src/laybasic/laybasic/layLayerControlPanel.h index d7caea7a5..6fd767e74 100644 --- a/src/laybasic/laybasic/layLayerControlPanel.h +++ b/src/laybasic/laybasic/layLayerControlPanel.h @@ -284,6 +284,7 @@ public: signals: void order_changed (); void tab_changed (); + void current_layer_changed (const lay::LayerPropertiesConstIterator &iter); public slots: void cm_new_tab (); @@ -321,6 +322,7 @@ public slots: void tab_context_menu (const QPoint &pt); void group_collapsed (const QModelIndex &index); void group_expanded (const QModelIndex &index); + void current_index_changed (const QModelIndex &index); void up_clicked (); void upup_clicked (); void down_clicked (); diff --git a/src/laybasic/laybasic/layLayoutView.cc b/src/laybasic/laybasic/layLayoutView.cc index 9302623f3..56b8ce57c 100644 --- a/src/laybasic/laybasic/layLayoutView.cc +++ b/src/laybasic/laybasic/layLayoutView.cc @@ -595,6 +595,7 @@ LayoutView::init (db::Manager *mgr, QWidget * /*parent*/) connect (mp_control_frame, SIGNAL (destroyed ()), this, SLOT (side_panel_destroyed ())); connect (mp_control_panel, SIGNAL (tab_changed ()), this, SLOT (layer_tab_changed ())); connect (mp_control_panel, SIGNAL (order_changed ()), this, SLOT (layer_order_changed ())); + connect (mp_control_panel, SIGNAL (current_layer_changed (const lay::LayerPropertiesConstIterator &)), this, SLOT (current_layer_changed_slot (const lay::LayerPropertiesConstIterator &))); /* connect (mp_control_panel, SIGNAL (marked_changed ()), this, SLOT (prop_changed ())); connect (mp_control_panel, SIGNAL (width_changed ()), this, SLOT (prop_changed ())); @@ -5324,6 +5325,12 @@ LayoutView::layer_snapshot () const return state; } +void +LayoutView::current_layer_changed_slot (const lay::LayerPropertiesConstIterator &iter) +{ + current_layer_changed_event (iter); +} + void LayoutView::add_new_layers (const LayerState &state) { diff --git a/src/laybasic/laybasic/layLayoutView.h b/src/laybasic/laybasic/layLayoutView.h index 46938886a..3b545fa82 100644 --- a/src/laybasic/laybasic/layLayoutView.h +++ b/src/laybasic/laybasic/layLayoutView.h @@ -744,6 +744,12 @@ public: */ tl::event current_layer_list_changed_event; + /** + * @brief An event signalling that the current layer has changed + */ + tl::event current_layer_changed_event; + + /** * @brief An event signalling that the visibility of some cells has changed */ @@ -2538,6 +2544,14 @@ public: */ void create_plugins (const lay::PluginDeclaration *except_this = 0); + /** + * @brief Gets the full field box + * + * This is the box to which the view will zoom on zoom_fit(). + * This box is supposed to cover everything inside the view. + */ + db::DBox full_box () const; + public slots: /** * @brief Store the current state on the "previous states" stack @@ -2593,12 +2607,9 @@ public slots: void select_cell_dispatch (const cell_path_type &path, int cellview_index); /** - * @brief Gets the full field box - * - * This is the box to which the view will zoom on zoom_fit(). - * This box is supposed to cover everything inside the view. + * @brief Called when the current layer changed */ - db::DBox full_box () const; + void current_layer_changed_slot (const lay::LayerPropertiesConstIterator &iter); // zoom slots void zoom_fit (); @@ -2685,6 +2696,11 @@ signals: */ void mode_change (int m); + /** + * @brief The current layer changed + */ + void current_layer_changed (const lay::LayerPropertiesConstIterator &l); + protected: /** * @brief Establish the view operations From 73787eed8a1d36f74dba28f823e904d9664de02c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Aug 2020 21:44:20 +0200 Subject: [PATCH 060/113] WIP: recent options of boxes, polygons, styling --- src/edt/edt/EditorOptionsGeneric.ui | 84 ++--- src/edt/edt/EditorOptionsPath.ui | 450 ++++++++++++------------ src/edt/edt/EditorOptionsText.ui | 453 ++++++++++++++----------- src/edt/edt/edtPlugin.cc | 33 +- src/edt/edt/edtServiceImpl.cc | 4 + src/lay/lay/layMainWindow.cc | 1 + src/laybasic/laybasic/layLayoutView.cc | 16 +- 7 files changed, 568 insertions(+), 473 deletions(-) diff --git a/src/edt/edt/EditorOptionsGeneric.ui b/src/edt/edt/EditorOptionsGeneric.ui index 4ba0b27e1..8de52d009 100644 --- a/src/edt/edt/EditorOptionsGeneric.ui +++ b/src/edt/edt/EditorOptionsGeneric.ui @@ -105,7 +105,7 @@ - + 1 0 @@ -204,45 +204,10 @@ - - - - Connections - - - - - - - - 1 - 0 - - - - QComboBox::AdjustToContents - - - - Any Angle - - - - - Diagonal - - - - - Manhattan - - - - - + 1 0 @@ -267,15 +232,50 @@ - - + + + + Connections + + + + + + + + 1 + 0 + + + + QComboBox::AdjustToContents + + + + Any Angle + + + + + Diagonal + + + + + Manhattan + + + + + + Qt::Horizontal - 257 - 10 + 40 + 20 @@ -324,7 +324,7 @@ - + 1 0 diff --git a/src/edt/edt/EditorOptionsPath.ui b/src/edt/edt/EditorOptionsPath.ui index 0b2769df6..c50521d5a 100644 --- a/src/edt/edt/EditorOptionsPath.ui +++ b/src/edt/edt/EditorOptionsPath.ui @@ -18,240 +18,256 @@ 6 - 4 + 0 - 4 + 0 - 4 + 0 - 4 + 0 - + QFrame::NoFrame - - QFrame::Raised + + true - - - 6 + + + + 0 + 0 + 357 + 289 + - - 0 - - - 0 - - - 0 - - - 0 - - - - - Width - - - - - - - - 0 - 0 - - - - - - - - µm - - - - - - - Qt::Horizontal - - - - 21 - 20 - - - - - + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Width + + + + + + + + 0 + 0 + + + + + + + + µm + + + + + + + Qt::Horizontal + + + + 21 + 20 + + + + + + + + + + + + 0 + 0 + + + + Extensions + + + + 4 + + + 4 + + + 4 + + + 4 + + + 6 + + + 2 + + + + + end = + + + + + + + + 1 + 0 + + + + + + + + µm + + + + + + + + 1 + 0 + + + + + + + + + 0 + 0 + + + + + Flush + + + + + Square + + + + + Variable + + + + + Round + + + + + + + + Variable + + + + + + + µm + + + + + + + start = + + + + + + + Type + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Vertical + + + + 271 + 123 + + + + + + - - - - - 0 - 0 - - - - Extensions - - - - 4 - - - 4 - - - 4 - - - 4 - - - 6 - - - 2 - - - - - Qt::Horizontal - - - - 21 - 10 - - - - - - - - µm - - - - - - - µm - - - - - - - - 1 - 0 - - - - - - - - end = - - - - - - - start = - - - - - - - - 1 - 0 - - - - - - - - Variable - - - - - - - - 0 - 0 - - - - - Flush - - - - - Square - - - - - Variable - - - - - Round - - - - - - - - Type - - - - - - - - - - Qt::Vertical - - - - 271 - 63 - - - - - - width_le - type_cb - start_ext_le - end_ext_le - diff --git a/src/edt/edt/EditorOptionsText.ui b/src/edt/edt/EditorOptionsText.ui index bd19c5e98..1cc821fd4 100644 --- a/src/edt/edt/EditorOptionsText.ui +++ b/src/edt/edt/EditorOptionsText.ui @@ -18,227 +18,280 @@ 2 - 4 + 0 - 4 + 0 - 4 + 0 - 4 + 0 - + QFrame::NoFrame - - QFrame::Raised + + true - - - 4 + + + + 0 + 0 + 542 + 243 + - - 4 - - - 4 - - - 4 - - - 6 - - - 2 - - - - - Alignment - - - - - - - - 0 - 0 - - - - h = - - - - - - - Text size - - - - - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - Text - - - - - - - Hint: orientation, alignments and size cannot be saved to OASIS files. Enable a vector font and text scaling in the setup dialog to show text objects scaled and rotated. - - - true - - - - - - - - - - (empty for default) - - - - - - - - 1 - 0 - - - - - (Default) + + + + + QFrame::NoFrame - - - - Top + + QFrame::Raised - - - - Center + + + 4 + + + 4 + + + 4 + + + 4 + + + 6 + + + 2 + + + + + + 0 + 0 + + + + v = + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 5 + + + + + + + + Text size + + + + + + + + 0 + 0 + + + + Text + + + + + + + + 1 + 0 + + + + + (Default) + + + + + Top + + + + + Center + + + + + Bottom + + + + + + + + + 1 + 0 + + + + + (Default) + + + + + Left + + + + + Center + + + + + Right + + + + + + + + + 0 + 0 + + + + h = + + + + + + + Alignment + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + (empty for default) + + + + + + + + + + Hint: orientation, alignments and size cannot be saved to OASIS files. Enable a vector font and text scaling in the setup dialog to show text objects scaled and rotated. + + + true + + + + + + + + + + Qt::Vertical - - - - Bottom + + + 20 + 77 + - - - - - - - - 0 - 0 - - - - v = - - - - - - - - 1 - 0 - - - - - (Default) - - - - - Left - - - - - Center - - - - - Right - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 5 - - - - - + + + + - - - - Qt::Vertical - - - - 20 - 40 - - - - - - text_le - halign_cbx - valign_cbx - size_le - diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 05748747b..975cf2ccc 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -37,7 +37,19 @@ namespace edt { -static +edt::RecentConfigurationPage::ConfigurationDescriptor shape_cfg_descriptors[] = +{ + edt::RecentConfigurationPage::ConfigurationDescriptor ("", tl::to_string (tr ("Layer")), edt::RecentConfigurationPage::Layer), +}; + +static +void get_shape_editor_options_pages (std::vector &ret, lay::LayoutView *view, lay::Dispatcher *dispatcher) +{ + ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-shape-param", + &shape_cfg_descriptors[0], &shape_cfg_descriptors[sizeof (shape_cfg_descriptors) / sizeof (shape_cfg_descriptors[0])])); +} + +static void get_text_options (std::vector < std::pair > &options) { options.push_back (std::pair (cfg_edit_text_string, "ABC")); @@ -46,9 +58,20 @@ void get_text_options (std::vector < std::pair > &opti options.push_back (std::pair (cfg_edit_text_valign, "bottom")); } -static -void get_text_editor_options_pages (std::vector &ret, lay::LayoutView *, lay::Dispatcher *dispatcher) +edt::RecentConfigurationPage::ConfigurationDescriptor text_cfg_descriptors[] = { + edt::RecentConfigurationPage::ConfigurationDescriptor ("", tl::to_string (tr ("Layer")), edt::RecentConfigurationPage::Layer), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_text_string, tl::to_string (tr ("Text")), edt::RecentConfigurationPage::Text), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_text_size, tl::to_string (tr ("Size")), edt::RecentConfigurationPage::Double), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_text_halign, tl::to_string (tr ("Hor. align")), edt::RecentConfigurationPage::Text), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_text_valign, tl::to_string (tr ("Vert. align")), edt::RecentConfigurationPage::Text) +}; + +static +void get_text_editor_options_pages (std::vector &ret, lay::LayoutView *view, lay::Dispatcher *dispatcher) +{ + ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-text-param", + &text_cfg_descriptors[0], &text_cfg_descriptors[sizeof (text_cfg_descriptors) / sizeof (text_cfg_descriptors[0])])); ret.push_back (new edt::EditorOptionsText (dispatcher)); } @@ -192,12 +215,12 @@ private: }; static tl::RegisteredClass config_decl1 ( - new edt::PluginDeclaration (tl::to_string (QObject::tr ("Polygons")), "polygon:edit_mode\t" + tl::to_string (QObject::tr ("Polygon")) + "<:polygon.png>" + tl::to_string (QObject::tr ("{Create a polygon}"))), + new edt::PluginDeclaration (tl::to_string (QObject::tr ("Polygons")), "polygon:edit_mode\t" + tl::to_string (QObject::tr ("Polygon")) + "<:polygon.png>" + tl::to_string (QObject::tr ("{Create a polygon}")), 0, &get_shape_editor_options_pages), 4010, "edt::Service(Polygons)" ); static tl::RegisteredClass config_decl2 ( - new edt::PluginDeclaration (tl::to_string (QObject::tr ("Boxes")), "box:edit_mode\t" + tl::to_string (QObject::tr ("Box")) + "\t<:box.png>" + tl::to_string (QObject::tr ("{Create a box}"))), + new edt::PluginDeclaration (tl::to_string (QObject::tr ("Boxes")), "box:edit_mode\t" + tl::to_string (QObject::tr ("Box")) + "\t<:box.png>" + tl::to_string (QObject::tr ("{Create a box}")), 0, &get_shape_editor_options_pages), 4011, "edt::Service(Boxes)" ); diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index f105d1031..59fbdd3de 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -440,6 +440,7 @@ void PolygonService::do_finish_edit () { deliver_shape (get_polygon ()); + commit_recent (view ()); } db::Polygon @@ -737,6 +738,7 @@ void BoxService::do_finish_edit () { deliver_shape (get_box ()); + commit_recent (view ()); } void @@ -854,6 +856,8 @@ TextService::do_finish_edit () cell ().shapes (layer ()).insert (get_text ()); manager ()->commit (); + commit_recent (view ()); + if (! view ()->text_visible ()) { lay::TipDialog td (QApplication::activeWindow (), diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 36b9bb96c..a75141306 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -551,6 +551,7 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha mp_eo_dock_widget = new QDockWidget (QObject::tr ("Editor Options"), this); mp_eo_dock_widget->setObjectName (QString::fromUtf8 ("eo_dock_widget")); + mp_eo_dock_widget->setMinimumHeight (150); mp_eo_stack = new ControlWidgetStack (mp_eo_dock_widget, "eo_stack"); mp_eo_dock_widget->setWidget (mp_eo_stack); mp_eo_dock_widget->setFocusProxy (mp_eo_stack); diff --git a/src/laybasic/laybasic/layLayoutView.cc b/src/laybasic/laybasic/layLayoutView.cc index 56b8ce57c..e1ff1d99d 100644 --- a/src/laybasic/laybasic/layLayoutView.cc +++ b/src/laybasic/laybasic/layLayoutView.cc @@ -543,14 +543,13 @@ LayoutView::init (db::Manager *mgr, QWidget * /*parent*/) if ((m_options & LV_NoLibrariesView) == 0 && (m_options & LV_Naked) == 0) { - QFrame *libraries_frame = new QFrame (0); - libraries_frame->setObjectName (QString::fromUtf8 ("libs_frame")); - mp_libraries_frame = libraries_frame; - QVBoxLayout *left_frame_ly = new QVBoxLayout (libraries_frame); + mp_libraries_frame = new QFrame (0); + mp_libraries_frame->setObjectName (QString::fromUtf8 ("libs_frame")); + QVBoxLayout *left_frame_ly = new QVBoxLayout (mp_libraries_frame); left_frame_ly->setMargin (0); left_frame_ly->setSpacing (0); - mp_libraries_view = new lay::LibrariesView (this, libraries_frame, "libs"); + mp_libraries_view = new lay::LibrariesView (this, mp_libraries_frame, "libs"); left_frame_ly->addWidget (mp_libraries_view, 1 /*stretch*/); connect (mp_libraries_view, SIGNAL (active_library_changed (int)), this, SLOT (active_library_changed (int))); @@ -560,11 +559,10 @@ LayoutView::init (db::Manager *mgr, QWidget * /*parent*/) if (is_editable () && (m_options & LV_NoEditorOptionsPanel) == 0 && (m_options & LV_Naked) == 0) { - QFrame *editor_options_frame = new QFrame (0); - editor_options_frame->setObjectName (QString::fromUtf8 ("editor_options_frame")); - mp_editor_options_frame = editor_options_frame; + mp_editor_options_frame = new QFrame (0); + mp_editor_options_frame->setObjectName (QString::fromUtf8 ("editor_options_frame")); - QVBoxLayout *left_frame_ly = new QVBoxLayout (editor_options_frame); + QVBoxLayout *left_frame_ly = new QVBoxLayout (mp_editor_options_frame); left_frame_ly->setMargin (0); left_frame_ly->setSpacing (0); From e36f6913de0eb66b8b1309b678941df4c70b1c85 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Aug 2020 23:39:18 +0200 Subject: [PATCH 061/113] Recent file lists for sessions, layer properties and bookmarks. --- src/lay/lay/layConfig.h | 3 + src/lay/lay/layMainConfigPages.cc | 3 + src/lay/lay/layMainWindow.cc | 286 +++++++++++++++++- src/lay/lay/layMainWindow.h | 21 +- .../laybasic/layLayoutViewFunctions.cc | 1 + 5 files changed, 304 insertions(+), 10 deletions(-) diff --git a/src/lay/lay/layConfig.h b/src/lay/lay/layConfig.h index 9c5e9d58a..a915366cf 100644 --- a/src/lay/lay/layConfig.h +++ b/src/lay/lay/layConfig.h @@ -40,6 +40,9 @@ static const std::string cfg_synchronized_views ("synchronized-views"); static const std::string cfg_edit_mode ("edit-mode"); static const std::string cfg_custom_macro_paths ("custom-macro-paths"); static const std::string cfg_mru ("mru"); +static const std::string cfg_mru_layer_properties ("mru-layer-properties"); +static const std::string cfg_mru_sessions ("mru-sessions"); +static const std::string cfg_mru_bookmarks ("mru-bookmarks"); static const std::string cfg_technologies ("technology-data"); static const std::string cfg_key_bindings ("key-bindings"); static const std::string cfg_menu_items_hidden ("menu-items-hidden"); diff --git a/src/lay/lay/layMainConfigPages.cc b/src/lay/lay/layMainConfigPages.cc index d906a89be..d5a219cee 100644 --- a/src/lay/lay/layMainConfigPages.cc +++ b/src/lay/lay/layMainConfigPages.cc @@ -61,6 +61,9 @@ public: options.push_back (std::pair (cfg_synchronized_views, "false")); options.push_back (std::pair (cfg_default_grids, "0.01,0.005,0.001")); options.push_back (std::pair (cfg_mru, "")); + options.push_back (std::pair (cfg_mru_sessions, "")); + options.push_back (std::pair (cfg_mru_layer_properties, "")); + options.push_back (std::pair (cfg_mru_bookmarks, "")); options.push_back (std::pair (cfg_technologies, "")); options.push_back (std::pair (cfg_show_navigator, "false")); options.push_back (std::pair (cfg_navigator_all_hier_levels, "false")); diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index a75141306..58cb94d21 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -473,7 +473,7 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha m_disable_tab_selected (false), m_exited (false), dm_do_update_menu (this, &MainWindow::do_update_menu), - dm_do_update_file_menu (this, &MainWindow::do_update_file_menu), + dm_do_update_mru_menus (this, &MainWindow::do_update_mru_menus), dm_exit (this, &MainWindow::exit), m_grid_micron (0.001), m_default_grids_updated (true), @@ -1334,7 +1334,49 @@ MainWindow::configure (const std::string &name, const std::string &value) } } - dm_do_update_file_menu (); + dm_do_update_mru_menus (); + + return true; + + } else if (name == cfg_mru_sessions) { + + tl::Extractor ex (value.c_str ()); + + m_mru_sessions.clear (); + while (! ex.at_end ()) { + m_mru_sessions.push_back (value); + ex.read_quoted (m_mru_sessions.back ()); + } + + dm_do_update_mru_menus (); + + return true; + + } else if (name == cfg_mru_layer_properties) { + + tl::Extractor ex (value.c_str ()); + + m_mru_layer_properties.clear (); + while (! ex.at_end ()) { + m_mru_layer_properties.push_back (value); + ex.read_quoted (m_mru_layer_properties.back ()); + } + + dm_do_update_mru_menus (); + + return true; + + } else if (name == cfg_mru_bookmarks) { + + tl::Extractor ex (value.c_str ()); + + m_mru_bookmarks.clear (); + while (! ex.at_end ()) { + m_mru_bookmarks.push_back (value); + ex.read_quoted (m_mru_bookmarks.back ()); + } + + dm_do_update_mru_menus (); return true; @@ -2249,6 +2291,7 @@ MainWindow::cm_save_layer_props () std::string fn; if (mp_lprops_fdia->get_save (fn, tl::to_string (QObject::tr ("Save Layer Properties File")))) { current_view ()->save_layer_props (fn); + add_to_other_mru (fn, cfg_mru_layer_properties); } } else { throw tl::Exception (tl::to_string (QObject::tr ("No view open to save the layer properties from"))); @@ -2291,6 +2334,8 @@ MainWindow::cm_load_layer_props () load_layer_properties (fn, false /*current view only*/, false /*don't add default*/); } + add_to_other_mru (fn, cfg_mru_layer_properties); + } } else { throw tl::Exception (tl::to_string (QObject::tr ("No view open to load the layer properties for"))); @@ -2332,6 +2377,7 @@ MainWindow::cm_save_session () std::string fn = m_current_session; if (mp_session_fdia->get_save (fn, tl::to_string (QObject::tr ("Save Session File")))) { save_session (fn); + add_to_other_mru (fn, cfg_mru_sessions); } } @@ -2347,7 +2393,10 @@ MainWindow::cm_restore_session () int dirty_layouts = dirty_files (df_list); if (dirty_layouts == 0) { + restore_session (fn); + add_to_other_mru (fn, cfg_mru_sessions); + } else { QMessageBox mbox (this); @@ -2361,6 +2410,7 @@ MainWindow::cm_restore_session () if (mbox.clickedButton() == discard_button) { restore_session (fn); + add_to_other_mru (fn, cfg_mru_sessions); } } @@ -2375,6 +2425,7 @@ MainWindow::cm_save_bookmarks () std::string fn; if (mp_bookmarks_fdia->get_save (fn, tl::to_string (QObject::tr ("Save Bookmarks File")))) { current_view ()->bookmarks ().save (fn); + add_to_other_mru (fn, cfg_mru_bookmarks); } } else { throw tl::Exception (tl::to_string (QObject::tr ("No view open to save the bookmarks from"))); @@ -2390,6 +2441,7 @@ MainWindow::cm_load_bookmarks () BookmarkList bookmarks; bookmarks.load (fn); current_view ()->bookmarks (bookmarks); + add_to_other_mru (fn, cfg_mru_bookmarks); } } else { throw tl::Exception (tl::to_string (QObject::tr ("No view open to load the bookmarks for"))); @@ -3118,6 +3170,8 @@ MainWindow::add_mru (const std::string &fn_rel) add_mru (fn_rel, m_initial_technology); } +const size_t max_mru = 16; + void MainWindow::add_mru (const std::string &fn_rel, const std::string &tech) { @@ -3133,7 +3187,7 @@ MainWindow::add_mru (const std::string &fn_rel, const std::string &tech) new_mru.push_back (std::make_pair (fn, tech)); - if (new_mru.size () > 10) { + if (new_mru.size () > max_mru) { new_mru.erase (new_mru.begin ()); } @@ -3152,6 +3206,47 @@ MainWindow::add_mru (const std::string &fn_rel, const std::string &tech) dispatcher ()->config_set (cfg_mru, config_str); } +void +MainWindow::add_to_other_mru (const std::string &fn_rel, const std::string &cfg) +{ + std::vector *mru_ptr; + if (cfg == cfg_mru_sessions) { + mru_ptr = &m_mru_sessions; + } else if (cfg == cfg_mru_layer_properties) { + mru_ptr = &m_mru_layer_properties; + } else if (cfg == cfg_mru_bookmarks) { + mru_ptr = &m_mru_bookmarks; + } else { + tl_assert (false); + } + + std::vector new_mru = *mru_ptr; + std::string fn (tl::InputStream::absolute_path (fn_rel)); + + for (std::vector::iterator mru = new_mru.begin (); mru != new_mru.end (); ++mru) { + if (*mru == fn) { + new_mru.erase (mru); + break; + } + } + + new_mru.push_back (fn); + + if (new_mru.size () > max_mru) { + new_mru.erase (new_mru.begin ()); + } + + std::string config_str; + for (std::vector::const_iterator mru = new_mru.begin (); mru != new_mru.end (); ++mru) { + if (! config_str.empty ()) { + config_str += " "; + } + config_str += tl::to_quoted_string (*mru); + } + + dispatcher ()->config_set (cfg, config_str); +} + namespace { @@ -3173,10 +3268,64 @@ private: size_t m_n; }; +class OpenRecentSessionAction + : public lay::Action +{ +public: + OpenRecentSessionAction (lay::MainWindow *mw, size_t n) + : lay::Action (), mp_mw (mw), m_n (n) + { } + + void triggered () + { + mp_mw->open_recent_session (m_n); + } + +private: + lay::MainWindow *mp_mw; + size_t m_n; +}; + +class OpenRecentLayerPropertiesAction + : public lay::Action +{ +public: + OpenRecentLayerPropertiesAction (lay::MainWindow *mw, size_t n) + : lay::Action (), mp_mw (mw), m_n (n) + { } + + void triggered () + { + mp_mw->open_recent_layer_properties (m_n); + } + +private: + lay::MainWindow *mp_mw; + size_t m_n; +}; + +class OpenRecentBookmarksAction + : public lay::Action +{ +public: + OpenRecentBookmarksAction (lay::MainWindow *mw, size_t n) + : lay::Action (), mp_mw (mw), m_n (n) + { } + + void triggered () + { + mp_mw->open_recent_bookmarks (m_n); + } + +private: + lay::MainWindow *mp_mw; + size_t m_n; +}; + } void -MainWindow::do_update_file_menu () +MainWindow::do_update_mru_menus () { std::string mru_menu = "file_menu.open_recent_menu"; @@ -3203,6 +3352,84 @@ MainWindow::do_update_file_menu () } } + + mru_menu = "file_menu.open_recent_menu_sessions"; + + if (menu ()->is_valid (mru_menu)) { + + Action *open_recent_action = menu ()->action (mru_menu); + open_recent_action->set_enabled (true); + + if (m_mru_sessions.size () > 0 && edits_enabled ()) { + + // rebuild MRU menu + menu ()->clear_menu (mru_menu); + + for (std::vector::iterator mru = m_mru_sessions.end (); mru != m_mru_sessions.begin (); ) { + --mru; + size_t i = std::distance (m_mru_sessions.begin (), mru); + Action *action = new OpenRecentSessionAction (this, i); + action->set_title (*mru); + menu ()->insert_item (mru_menu + ".end", tl::sprintf ("open_recent_%d", i + 1), action); + } + + } else { + open_recent_action->set_enabled (false); + } + + } + + mru_menu = "file_menu.open_recent_menu_layer_props"; + + if (menu ()->is_valid (mru_menu)) { + + Action *open_recent_action = menu ()->action (mru_menu); + open_recent_action->set_enabled (true); + + if (m_mru_layer_properties.size () > 0 && edits_enabled ()) { + + // rebuild MRU menu + menu ()->clear_menu (mru_menu); + + for (std::vector::iterator mru = m_mru_layer_properties.end (); mru != m_mru_layer_properties.begin (); ) { + --mru; + size_t i = std::distance (m_mru_layer_properties.begin (), mru); + Action *action = new OpenRecentLayerPropertiesAction (this, i); + action->set_title (*mru); + menu ()->insert_item (mru_menu + ".end", tl::sprintf ("open_recent_%d", i + 1), action); + } + + } else { + open_recent_action->set_enabled (false); + } + + } + + mru_menu = "bookmark_menu.open_recent_menu_bookmarks"; + + if (menu ()->is_valid (mru_menu)) { + + Action *open_recent_action = menu ()->action (mru_menu); + open_recent_action->set_enabled (true); + + if (m_mru_bookmarks.size () > 0 && edits_enabled ()) { + + // rebuild MRU menu + menu ()->clear_menu (mru_menu); + + for (std::vector::iterator mru = m_mru_bookmarks.end (); mru != m_mru_bookmarks.begin (); ) { + --mru; + size_t i = std::distance (m_mru_bookmarks.begin (), mru); + Action *action = new OpenRecentBookmarksAction (this, i); + action->set_title (*mru); + menu ()->insert_item (mru_menu + ".end", tl::sprintf ("open_recent_%d", i + 1), action); + } + + } else { + open_recent_action->set_enabled (false); + } + + } } void @@ -3259,6 +3486,52 @@ MainWindow::open_recent (size_t n) END_PROTECTED } +void +MainWindow::open_recent_session (size_t n) +{ + BEGIN_PROTECTED + + if (n < m_mru_sessions.size ()) { + std::string fn = m_mru_sessions [n]; + restore_session (fn); + add_to_other_mru (fn, cfg_mru_sessions); // make it the latest + } + + END_PROTECTED +} + +void +MainWindow::open_recent_layer_properties (size_t n) +{ + BEGIN_PROTECTED + + if (n < m_mru_layer_properties.size ()) { + std::string fn = m_mru_layer_properties [n]; + load_layer_properties (fn, false /*current view only*/, false /*don't add default*/); + add_to_other_mru (fn, cfg_mru_layer_properties); // make it the latest + } + + END_PROTECTED +} + +void +MainWindow::open_recent_bookmarks (size_t n) +{ + BEGIN_PROTECTED + + if (n < m_mru_bookmarks.size ()) { + std::string fn = m_mru_bookmarks [n]; + if (current_view ()) { + BookmarkList bookmarks; + bookmarks.load (fn); + current_view ()->bookmarks (bookmarks); + add_to_other_mru (fn, cfg_mru_bookmarks); + } + } + + END_PROTECTED +} + void MainWindow::open (int mode) { @@ -4094,10 +4367,12 @@ MainWindow::dropEvent(QDropEvent *event) if (suffix == QString::fromUtf8 ("lyp")) { load_layer_properties (tl::to_string (path), false /*current view only*/, false /*don't add a default*/); + add_to_other_mru (tl::to_string (path), cfg_mru_layer_properties); } else if (suffix == QString::fromUtf8 ("lys")) { restore_session (tl::to_string (path)); + add_to_other_mru (tl::to_string (path), cfg_mru_sessions); } else if (suffix == QString::fromUtf8 ("lyb")) { @@ -4105,6 +4380,7 @@ MainWindow::dropEvent(QDropEvent *event) BookmarkList bookmarks; bookmarks.load (tl::to_string (path)); current_view ()->bookmarks (bookmarks); + add_to_other_mru (tl::to_string (path), cfg_mru_bookmarks); } } else { @@ -4340,9 +4616,11 @@ public: menu_entries.push_back (lay::separator ("layer_group", at)); menu_entries.push_back (lay::menu_item ("cm_load_layer_props", "load_layer_props:edit", at, tl::to_string (QObject::tr ("Load Layer Properties")))); menu_entries.push_back (lay::menu_item ("cm_save_layer_props", "save_layer_props:edit", at, tl::to_string (QObject::tr ("Save Layer Properties")))); + menu_entries.push_back (lay::submenu ("open_recent_menu_layer_props:edit", at, tl::to_string (QObject::tr ("Recent Layer Properties")))); menu_entries.push_back (lay::separator ("session_group", at)); menu_entries.push_back (lay::menu_item ("cm_restore_session", "restore_session:edit", at, tl::to_string (QObject::tr ("Restore Session")))); menu_entries.push_back (lay::menu_item ("cm_save_session", "save_session", at, tl::to_string (QObject::tr ("Save Session")))); + menu_entries.push_back (lay::submenu ("open_recent_menu_sessions:edit", at, tl::to_string (QObject::tr ("Recent Sessions")))); menu_entries.push_back (lay::separator ("log_group", at)); menu_entries.push_back (lay::menu_item ("cm_view_log", "view_log", at, tl::to_string (QObject::tr ("Log Viewer")))); menu_entries.push_back (lay::separator ("print_group", at)); diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index eb24021d5..5ef8ccddf 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -564,17 +564,22 @@ public: tl::event view_created_event; /** - * @brief Add an entry to the MRU list with the initial technology + * @brief Adds an entry to the MRU list with the initial technology */ void add_mru (const std::string &fn); /** - * @brief Add an entry to the MRU list + * @brief Adds an entry to the MRU list */ void add_mru (const std::string &fn, const std::string &tech); /** - * @brief Get the technology used for loading or creating layouts + * @brief Adds an entry to a specific MRU list given by the "cfg" configuration option + */ + void add_to_other_mru (const std::string &fn_rel, const std::string &cfg); + + /** + * @brief Gets the technology used for loading or creating layouts */ const std::string &initial_technology () { @@ -582,7 +587,7 @@ public: } /** - * @brief Set the initial technology used for loading or creating layouts + * @brief Sets the initial technology used for loading or creating layouts */ void set_initial_technology (const std::string &tech) { @@ -637,6 +642,9 @@ public slots: void close_current_view (); void tab_close_requested (int); void open_recent (size_t n); + void open_recent_session (size_t n); + void open_recent_layer_properties (size_t n); + void open_recent_bookmarks (size_t n); void view_selected (int index); void view_title_changed (); @@ -670,7 +678,7 @@ protected slots: protected: void update_content (); void do_update_menu (); - void do_update_file_menu (); + void do_update_mru_menus (); private: TextProgressDelegate m_text_progress; @@ -700,6 +708,7 @@ private: std::vector mp_views; int m_open_mode; std::vector > m_mru; + std::vector m_mru_sessions, m_mru_layer_properties, m_mru_bookmarks; QStatusBar *mp_status_bar; QStackedWidget *mp_main_stack_widget; ProgressWidget *mp_progress_widget; @@ -712,7 +721,7 @@ private: bool m_disable_tab_selected; bool m_exited; tl::DeferredMethod dm_do_update_menu; - tl::DeferredMethod dm_do_update_file_menu; + tl::DeferredMethod dm_do_update_mru_menus; tl::DeferredMethod dm_exit; QTimer m_message_timer; QTimer m_file_changed_timer; diff --git a/src/laybasic/laybasic/layLayoutViewFunctions.cc b/src/laybasic/laybasic/layLayoutViewFunctions.cc index 2f63cbd34..dc774dd26 100644 --- a/src/laybasic/laybasic/layLayoutViewFunctions.cc +++ b/src/laybasic/laybasic/layLayoutViewFunctions.cc @@ -2160,6 +2160,7 @@ public: menu_entries.push_back (lay::menu_item ("cm_manage_bookmarks", "manage_bookmarks", at, tl::to_string (QObject::tr ("Manage Bookmarks")))); menu_entries.push_back (lay::menu_item ("cm_load_bookmarks", "load_bookmarks", at, tl::to_string (QObject::tr ("Load Bookmarks")))); menu_entries.push_back (lay::menu_item ("cm_save_bookmarks", "save_bookmarks", at, tl::to_string (QObject::tr ("Save Bookmarks")))); + menu_entries.push_back (lay::submenu ("open_recent_menu_bookmarks", at, tl::to_string (QObject::tr ("Recent Bookmark Files")))); at = "zoom_menu.end"; menu_entries.push_back (lay::submenu ("global_trans", at, tl::to_string (QObject::tr ("Global Transformation")))); From ac10c72df48213801427783ae384106883277631 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 24 Aug 2020 22:40:01 +0200 Subject: [PATCH 062/113] WIP: immediate update of property changes - instances so far. --- src/edt/edt/edtEditorOptionsPages.cc | 19 +- src/edt/edt/edtInstPropertiesPage.cc | 212 +++++++++++++++---- src/edt/edt/edtInstPropertiesPage.h | 1 + src/edt/edt/edtPCellParametersPage.cc | 19 +- src/edt/edt/edtPropertiesPageUtils.cc | 21 ++ src/edt/edt/edtPropertiesPageUtils.h | 7 + src/laybasic/laybasic/layEditable.cc | 13 +- src/laybasic/laybasic/layProperties.h | 10 +- src/laybasic/laybasic/layPropertiesDialog.cc | 61 +++++- src/laybasic/laybasic/layPropertiesDialog.h | 12 +- 10 files changed, 280 insertions(+), 95 deletions(-) diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index 89f16ab0a..669ae0503 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -29,6 +29,7 @@ #include "edtConfig.h" #include "edtService.h" #include "edtEditorOptionsPages.h" +#include "edtPropertiesPageUtils.h" #include "tlExceptions.h" #include "layPlugin.h" #include "layLayoutView.h" @@ -196,24 +197,6 @@ END_PROTECTED_W (this) // ------------------------------------------------------------------ // Indicates an error on a line edit -static void indicate_error (QLineEdit *le, const tl::Exception *ex) -{ - // by the way, update the foreground color of the cell edit box as well (red, if not valid) - QPalette pl = le->palette (); - if (ex) { - pl.setColor (QPalette::Active, QPalette::Text, Qt::red); - pl.setColor (QPalette::Active, QPalette::Base, QColor (Qt::red).lighter (180)); - le->setToolTip (tl::to_qstring (ex->msg ())); - } else { - QWidget *pw = dynamic_cast (le->parent ()); - tl_assert (pw != 0); - pl.setColor (QPalette::Active, QPalette::Text, pw->palette ().color (QPalette::Text)); - pl.setColor (QPalette::Active, QPalette::Base, pw->palette ().color (QPalette::Base)); - le->setToolTip (QString ()); - } - le->setPalette (pl); -} - template static void configure_from_line_edit (lay::Dispatcher *dispatcher, QLineEdit *le, const std::string &cfg_name) { diff --git a/src/edt/edt/edtInstPropertiesPage.cc b/src/edt/edt/edtInstPropertiesPage.cc index fa791a381..84aaf83ca 100644 --- a/src/edt/edt/edtInstPropertiesPage.cc +++ b/src/edt/edt/edtInstPropertiesPage.cc @@ -65,6 +65,21 @@ InstPropertiesPage::InstPropertiesPage (edt::Service *service, db::Manager *mana connect (lib_cbx, SIGNAL (currentIndexChanged (int)), this, SLOT (library_changed (int))); connect (cell_name_le, SIGNAL (textChanged (const QString &)), this, SLOT (cell_name_changed (const QString &))); + connect (lib_cbx, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (cell_name_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (array_grp, SIGNAL (clicked ()), this, SIGNAL (edited ())); + connect (rows_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (columns_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (row_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (row_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (column_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (column_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (pos_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (pos_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (angle_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (mag_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (mirror_cbx, SIGNAL (clicked ()), this, SIGNAL (edited ())); + QHBoxLayout *layout = new QHBoxLayout (pcell_tab); layout->setMargin (0); pcell_tab->setLayout (layout); @@ -125,12 +140,19 @@ BEGIN_PROTECTED } if (form.exec ()) { + + cell_name_le->blockSignals (true); if (form.selected_cell_is_pcell ()) { cell_name_le->setText (tl::to_qstring (layout->pcell_header (form.selected_pcell_id ())->get_name ())); } else if (layout->is_valid_cell_index (form.selected_cell_index ())) { cell_name_le->setText (tl::to_qstring (layout->cell_name (form.selected_cell_index ()))); } + cell_name_le->blockSignals (false); + update_pcell_parameters (); + + emit edited (); + } END_PROTECTED @@ -140,7 +162,9 @@ void InstPropertiesPage::show_props () { lay::UserPropertiesForm props_form (this); - props_form.show (mp_service->view (), m_selection_ptrs [m_index]->cv_index (), m_prop_id); + if (props_form.show (mp_service->view (), m_selection_ptrs [m_index]->cv_index (), m_prop_id)) { + emit edited (); + } } void @@ -222,6 +246,7 @@ InstPropertiesPage::update () db::cell_index_type def_cell_index = pos->back ().inst_ptr.cell_index (); const db::Cell &def_cell = def_layout->cell (def_cell_index); + lib_cbx->blockSignals (true); std::pair dl = def_layout->defining_library (def_cell_index); lib_cbx->set_technology_filter (cv->tech_name (), true); lib_cbx->set_current_library (dl.first); @@ -229,13 +254,16 @@ InstPropertiesPage::update () def_layout = &dl.first->layout (); def_cell_index = dl.second; } + lib_cbx->blockSignals (false); std::pair pci = def_layout->is_pcell_instance (def_cell_index); + cell_name_le->blockSignals (true); if (pci.first && def_layout->pcell_declaration (pci.second)) { cell_name_le->setText (tl::to_qstring (def_layout->pcell_header (pci.second)->get_name ())); } else { cell_name_le->setText (tl::to_qstring (def_layout->cell_name (def_cell_index))); } + cell_name_le->blockSignals (false); db::Vector rowv, columnv; unsigned long rows, columns; @@ -325,6 +353,9 @@ InstPropertiesPage::readonly () ChangeApplicator * InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance & /*inst*/, double dbu) { + bool has_error = false; + bool has_pcell_error = false; + std::auto_ptr appl (new CombinedChangeApplicator ()); edt::Service::obj_iterator pos = m_selection_ptrs [m_index]; @@ -343,34 +374,69 @@ InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance & layout = &cv->layout (); } - std::pair ci = layout->cell_by_name (tl::to_string (cell_name_le->text ()).c_str ()); - std::pair pci = layout->pcell_by_name (tl::to_string (cell_name_le->text ()).c_str ()); - if (! ci.first && ! pci.first) { - throw tl::Exception (tl::to_string (QObject::tr ("Not a valid cell name: %s")).c_str (), tl::to_string (cell_name_le->text ()).c_str ()); + try { + + std::pair ci = layout->cell_by_name (tl::to_string (cell_name_le->text ()).c_str ()); + std::pair pci = layout->pcell_by_name (tl::to_string (cell_name_le->text ()).c_str ()); + if (! ci.first && ! pci.first) { + throw tl::Exception (tl::to_string (QObject::tr ("Not a valid cell or PCell name: %s")).c_str (), tl::to_string (cell_name_le->text ()).c_str ()); + } + + indicate_error (cell_name_le, 0); + + } catch (tl::Exception &ex) { + indicate_error (cell_name_le, &ex); + has_error = true; } - db::cell_index_type inst_cell_index = ci.second; + try { - // instantiate the PCell - if (pci.first) { - tl_assert (mp_pcell_parameters != 0); - tl_assert (layout->pcell_declaration (pci.second) == mp_pcell_parameters->pcell_decl ()); - inst_cell_index = layout->get_pcell_variant (pci.second, mp_pcell_parameters->get_parameters ()); - } + std::pair ci = layout->cell_by_name (tl::to_string (cell_name_le->text ()).c_str ()); + std::pair pci = layout->pcell_by_name (tl::to_string (cell_name_le->text ()).c_str ()); + tl_assert (ci.first || pci.first); - // reference the library - if (lib) { - layout = & cv->layout (); - inst_cell_index = layout->get_lib_proxy (lib, inst_cell_index); - } + db::cell_index_type inst_cell_index = 0; - if (inst_cell_index != pos->back ().inst_ptr.cell_index ()) { - appl->add (new ChangeTargetCellApplicator (inst_cell_index)); + // instantiate the PCell + if (pci.first) { + tl_assert (mp_pcell_parameters != 0); + tl_assert (layout->pcell_declaration (pci.second) == mp_pcell_parameters->pcell_decl ()); + inst_cell_index = layout->get_pcell_variant (pci.second, mp_pcell_parameters->get_parameters ()); + } else { + inst_cell_index = ci.second; + } + + // reference the library + if (lib) { + layout = & cv->layout (); + inst_cell_index = layout->get_lib_proxy (lib, inst_cell_index); + } + + if (inst_cell_index != pos->back ().inst_ptr.cell_index ()) { + appl->add (new ChangeTargetCellApplicator (inst_cell_index)); + } + + } catch (tl::Exception &ex) { + has_pcell_error = true; } double x = 0.0, y = 0.0; - tl::from_string (tl::to_string (pos_x_le->text ()), x); - tl::from_string (tl::to_string (pos_y_le->text ()), y); + + try { + tl::from_string (tl::to_string (pos_x_le->text ()), x); + indicate_error (pos_x_le, 0); + } catch (tl::Exception &ex) { + indicate_error (pos_x_le, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (pos_y_le->text ()), y); + indicate_error (pos_y_le, 0); + } catch (tl::Exception &ex) { + indicate_error (pos_y_le, &ex); + has_error = true; + } db::DCplxTrans t; if (abs_cb->isChecked ()) { @@ -381,20 +447,32 @@ InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance & bool mirror = mirror_cbx->isChecked (); double angle = 0.0; - tl::from_string (tl::to_string (angle_le->text ()), angle); + try { + tl::from_string (tl::to_string (angle_le->text ()), angle); + indicate_error (angle_le, 0); + } catch (tl::Exception &ex) { + indicate_error (angle_le, &ex); + has_error = true; + } double mag = 0.0; - tl::from_string (tl::to_string (mag_le->text ()), mag); + try { + tl::from_string (tl::to_string (mag_le->text ()), mag); + indicate_error (mag_le, 0); + } catch (tl::Exception &ex) { + indicate_error (mag_le, &ex); + has_error = true; + } angle -= (floor (angle / 360.0) + 1.0) * 360.0; while (angle < -1e-6) { angle += 360.0; } - db::CellInstArray::complex_trans_type tr = pos->back ().inst_ptr.complex_trans (); + db::CellInstArray::complex_trans_type trans = pos->back ().inst_ptr.complex_trans (); - if (fabs (angle - tr.angle ()) > 1e-6 || mirror != tr.is_mirror () || fabs (mag - tr.mag ()) > 1e-6 || ! disp.equal (tr.disp () * dbu)) { - appl->add (new ChangeInstanceTransApplicator (angle, tr.angle (), mirror, tr.is_mirror (), mag, tr.mag (), disp, tr.disp () * dbu)); + if (fabs (angle - trans.angle ()) > 1e-6 || mirror != trans.is_mirror () || fabs (mag - trans.mag ()) > 1e-6 || ! disp.equal (trans.disp () * dbu)) { + appl->add (new ChangeInstanceTransApplicator (angle, trans.angle (), mirror, trans.is_mirror (), mag, trans.mag (), disp, trans.disp () * dbu)); } db::CellInstArray::vector_type a_org, b_org; @@ -407,12 +485,53 @@ InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance & double rx = 0.0, ry = 0.0; unsigned long rows = 0, cols = 0; - tl::from_string (tl::to_string (column_x_le->text ()), cx); - tl::from_string (tl::to_string (column_y_le->text ()), cy); - tl::from_string (tl::to_string (row_x_le->text ()), rx); - tl::from_string (tl::to_string (row_y_le->text ()), ry); - tl::from_string (tl::to_string (rows_le->text ()), rows); - tl::from_string (tl::to_string (columns_le->text ()), cols); + try { + tl::from_string (tl::to_string (column_x_le->text ()), cx); + indicate_error (column_x_le, 0); + } catch (tl::Exception &ex) { + indicate_error (column_x_le, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (column_y_le->text ()), cy); + indicate_error (column_y_le, 0); + } catch (tl::Exception &ex) { + indicate_error (column_y_le, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (row_x_le->text ()), rx); + indicate_error (row_x_le, 0); + } catch (tl::Exception &ex) { + indicate_error (row_x_le, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (row_y_le->text ()), ry); + indicate_error (row_y_le, 0); + } catch (tl::Exception &ex) { + indicate_error (row_y_le, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (rows_le->text ()), rows); + indicate_error (rows_le, 0); + } catch (tl::Exception &ex) { + indicate_error (rows_le, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (columns_le->text ()), cols); + indicate_error (columns_le, 0); + } catch (tl::Exception &ex) { + indicate_error (columns_le, &ex); + has_error = true; + } db::DVector rv = db::DVector (dpoint_from_dpoint (db::DPoint (rx, ry), dbu, du, t)); db::DVector cv = db::DVector (dpoint_from_dpoint (db::DPoint (cx, cy), dbu, du, t)); @@ -432,6 +551,14 @@ InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance & } + if (has_error || has_pcell_error) { + throw tl::Exception (tl::to_string (tr ("At least one value and PCell parameter is not correct - see hightlighted entry fields or the PCell error indicator"))); + } else if (has_error) { + throw tl::Exception (tl::to_string (tr ("At least one value is not correct - see hightlighted entry fields"))); + } else if (has_pcell_error) { + throw tl::Exception (tl::to_string (tr ("At least one PCell parameter is not correct - see hightlighted entry fields or the PCell error indicator"))); + } + return appl.release (); } @@ -565,6 +692,9 @@ InstPropertiesPage::do_apply (bool current_only) size_t index = p - m_selection_ptrs.begin (); + // save previous selection so we can restore it + m_saved_selection.push_back (std::make_pair (index, new_sel[index])); + // change selection to new instance new_sel[index].back ().inst_ptr = new_inst; @@ -636,16 +766,13 @@ InstPropertiesPage::update_pcell_parameters () std::pair pc = layout->pcell_by_name (tl::to_string (cell_name_le->text ()).c_str ()); std::pair cc = layout->cell_by_name (tl::to_string (cell_name_le->text ()).c_str ()); - // by the way, update the foreground color of the cell edit box as well (red, if not valid) - QPalette pl = cell_name_le->palette (); + // indicate an invalid cell name if (! pc.first && ! cc.first) { - pl.setColor (QPalette::Text, Qt::red); - pl.setColor (QPalette::Base, QColor (Qt::red).lighter (180)); + tl::Exception ex (tl::to_string (QObject::tr ("Not a valid cell or PCell name: %s")).c_str (), tl::to_string (cell_name_le->text ()).c_str ()); + indicate_error (cell_name_le, &ex); } else { - pl.setColor (QPalette::Text, palette ().color (QPalette::Text)); - pl.setColor (QPalette::Base, palette ().color (QPalette::Base)); + indicate_error (cell_name_le, 0); } - cell_name_le->setPalette (pl); if (pc.first && layout->pcell_declaration (pc.second)) { @@ -687,13 +814,13 @@ InstPropertiesPage::update_pcell_parameters () } mp_pcell_parameters = new PCellParametersPage (pcell_tab); + connect (mp_pcell_parameters, SIGNAL (edited ()), this, SIGNAL (edited ())); mp_pcell_parameters->setup (&cv->layout (), mp_service->view (), pos->cv_index (), layout->pcell_declaration (pc.second), parameters); pcell_tab->layout ()->addWidget (mp_pcell_parameters); } param_tab_widget->setTabEnabled (1, true); - param_tab_widget->setCurrentIndex (1); } else { @@ -705,8 +832,11 @@ InstPropertiesPage::update_pcell_parameters () mp_pcell_parameters = 0; - param_tab_widget->setCurrentIndex (0); + if (param_tab_widget->currentIndex () == 1) { + param_tab_widget->setCurrentIndex (0); + } param_tab_widget->setTabEnabled (1, false); + } } diff --git a/src/edt/edt/edtInstPropertiesPage.h b/src/edt/edt/edtInstPropertiesPage.h index f648482dc..ca4acb932 100644 --- a/src/edt/edt/edtInstPropertiesPage.h +++ b/src/edt/edt/edtInstPropertiesPage.h @@ -65,6 +65,7 @@ protected: bool m_enable_cb_callback; db::properties_id_type m_prop_id; edt::PCellParametersPage *mp_pcell_parameters; + std::list > m_saved_selection; virtual bool readonly (); virtual void apply (); diff --git a/src/edt/edt/edtPCellParametersPage.cc b/src/edt/edt/edtPCellParametersPage.cc index 670610982..f55ce5c28 100644 --- a/src/edt/edt/edtPCellParametersPage.cc +++ b/src/edt/edt/edtPCellParametersPage.cc @@ -22,6 +22,7 @@ #include "edtPCellParametersPage.h" +#include "edtPropertiesPageUtils.h" #include "layWidgets.h" #include "tlScriptError.h" @@ -38,24 +39,6 @@ namespace edt { -static void indicate_error (QLineEdit *le, const tl::Exception *ex) -{ - // by the way, update the foreground color of the cell edit box as well (red, if not valid) - QPalette pl = le->palette (); - if (ex) { - pl.setColor (QPalette::Active, QPalette::Text, Qt::red); - pl.setColor (QPalette::Active, QPalette::Base, QColor (Qt::red).lighter (180)); - le->setToolTip (tl::to_qstring (ex->msg ())); - } else { - QWidget *pw = dynamic_cast (le->parent ()); - tl_assert (pw != 0); - pl.setColor (QPalette::Active, QPalette::Text, pw->palette ().color (QPalette::Text)); - pl.setColor (QPalette::Active, QPalette::Base, pw->palette ().color (QPalette::Base)); - le->setToolTip (QString ()); - } - le->setPalette (pl); -} - static void set_value (const db::PCellParameterDeclaration &p, const db::Layout * /*layout*/, QWidget *widget, const tl::Variant &value) { if (p.get_choices ().empty ()) { diff --git a/src/edt/edt/edtPropertiesPageUtils.cc b/src/edt/edt/edtPropertiesPageUtils.cc index 1bb1c9477..8b1490af5 100644 --- a/src/edt/edt/edtPropertiesPageUtils.cc +++ b/src/edt/edt/edtPropertiesPageUtils.cc @@ -26,6 +26,8 @@ #include "dbShapes.h" #include "dbLayout.h" +#include + namespace edt { @@ -784,5 +786,24 @@ coords_to_string (const db::DPoint &dp, double dbu, bool du, const char *sep) return coord_to_string (dp.x (), dbu, du) + sep + coord_to_string (dp.y (), dbu, du); } +void +indicate_error (QLineEdit *le, const tl::Exception *ex) +{ + // by the way, update the foreground color of the cell edit box as well (red, if not valid) + QPalette pl = le->palette (); + if (ex) { + pl.setColor (QPalette::Active, QPalette::Text, Qt::red); + pl.setColor (QPalette::Active, QPalette::Base, QColor (Qt::red).lighter (180)); + le->setToolTip (tl::to_qstring (ex->msg ())); + } else { + QWidget *pw = dynamic_cast (le->parent ()); + tl_assert (pw != 0); + pl.setColor (QPalette::Active, QPalette::Text, pw->palette ().color (QPalette::Text)); + pl.setColor (QPalette::Active, QPalette::Base, pw->palette ().color (QPalette::Base)); + le->setToolTip (QString ()); + } + le->setPalette (pl); +} + } diff --git a/src/edt/edt/edtPropertiesPageUtils.h b/src/edt/edt/edtPropertiesPageUtils.h index fc43c71ae..8735e5074 100644 --- a/src/edt/edt/edtPropertiesPageUtils.h +++ b/src/edt/edt/edtPropertiesPageUtils.h @@ -33,6 +33,8 @@ #include +class QLineEdit; + namespace edt { @@ -478,6 +480,11 @@ db::Point point_from_dpoint (const db::DPoint &dp, double dbu, bool du, const db */ db::Coord coord_from_string (const char *txt, double dbu, bool du, const db::VCplxTrans &t); +/** + * @brief Configures a QLineEdit to indicate a format error + */ +void indicate_error (QLineEdit *le, const tl::Exception *ex); + } #endif diff --git a/src/laybasic/laybasic/layEditable.cc b/src/laybasic/laybasic/layEditable.cc index 8b75008f1..e09044dde 100644 --- a/src/laybasic/laybasic/layEditable.cc +++ b/src/laybasic/laybasic/layEditable.cc @@ -80,6 +80,11 @@ Editables::Editables (db::Manager *manager) Editables::~Editables () { cancel_edits (); + + if (mp_properties_dialog) { + delete mp_properties_dialog; + mp_properties_dialog = 0; + } } void @@ -610,9 +615,8 @@ Editables::cancel_edits () { // close the property dialog if (mp_properties_dialog) { - delete mp_properties_dialog; + mp_properties_dialog->hide (); } - mp_properties_dialog = 0; // cancel any edit operations for (iterator e = begin (); e != end (); ++e) { @@ -623,7 +627,10 @@ Editables::cancel_edits () void Editables::show_properties (QWidget *parent) { - cancel_edits (); + // re-create a new properties dialog + if (mp_properties_dialog) { + delete mp_properties_dialog; + } mp_properties_dialog = new lay::PropertiesDialog (parent, manager (), this); mp_properties_dialog->show (); } diff --git a/src/laybasic/laybasic/layProperties.h b/src/laybasic/laybasic/layProperties.h index 6c918b8c0..a8fba1677 100644 --- a/src/laybasic/laybasic/layProperties.h +++ b/src/laybasic/laybasic/layProperties.h @@ -48,6 +48,8 @@ class Editable; class LAYBASIC_PUBLIC PropertiesPage : public QFrame { +Q_OBJECT + public: /** * @brief The constructor attaching the properties page to a parent widget @@ -140,7 +142,7 @@ public: */ virtual void operator++ () = 0; - /** + /** * @brief Update the display * * This method is called by the dialog to transfer data from the @@ -219,6 +221,12 @@ public: return mp_manager; } +signals: + /** + * @brief This signal is emitted if a value has been changed + */ + void edited (); + private: db::Manager *mp_manager; lay::Editable *mp_editable; diff --git a/src/laybasic/laybasic/layPropertiesDialog.cc b/src/laybasic/laybasic/layPropertiesDialog.cc index 31bdb36ce..1c823c561 100644 --- a/src/laybasic/laybasic/layPropertiesDialog.cc +++ b/src/laybasic/laybasic/layPropertiesDialog.cc @@ -35,7 +35,7 @@ namespace lay PropertiesDialog::PropertiesDialog (QWidget * /*parent*/, db::Manager *manager, lay::Editables *editables) : QDialog (0 /*parent*/), - mp_manager (manager), mp_editables (editables), m_index (-1) + mp_manager (manager), mp_editables (editables), m_index (-1), m_auto_applied (false), m_transaction_id (0) { mp_editables->enable_edits (false); @@ -49,6 +49,7 @@ PropertiesDialog::PropertiesDialog (QWidget * /*parent*/, db::Manager *manager, mp_properties_pages.push_back (e->properties_page (mp_manager, content_frame)); if (mp_properties_pages.back ()) { mp_stack->addWidget (mp_properties_pages.back ()); + connect (mp_properties_pages.back (), SIGNAL (edited ()), this, SLOT (apply ())); } } @@ -130,8 +131,9 @@ PropertiesDialog::next_pressed () BEGIN_PROTECTED if (! mp_properties_pages [m_index]->readonly ()) { - db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes"))); + db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); mp_properties_pages [m_index]->apply (); + m_transaction_id = t.id (); } // advance the current entry @@ -171,8 +173,9 @@ PropertiesDialog::prev_pressed () BEGIN_PROTECTED if (! mp_properties_pages [m_index]->readonly ()) { - db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes"))); + db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); mp_properties_pages [m_index]->apply (); + m_transaction_id = t.id (); } if (mp_properties_pages [m_index]->at_begin ()) { @@ -256,30 +259,68 @@ PropertiesDialog::apply_to_all_pressed () { BEGIN_PROTECTED - db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes to all"))); + { + db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes to all")), m_transaction_id); - mp_properties_pages [m_index]->apply_to_all (); - mp_properties_pages [m_index]->update (); + mp_properties_pages [m_index]->apply_to_all (); + mp_properties_pages [m_index]->update (); + + m_transaction_id = t.id (); + } END_PROTECTED } -void +void +PropertiesDialog::apply () +{ +BEGIN_PROTECTED + + db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Auto-apply changes")), m_transaction_id); + + try { + + mp_properties_pages [m_index]->apply (); + mp_properties_pages [m_index]->update (); + + } catch (tl::Exception &) { + // we assume the page somehow indicates the error and does not apply the values + } + + m_transaction_id = t.id (); + +END_PROTECTED +} + +void PropertiesDialog::apply_pressed () { BEGIN_PROTECTED - db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes"))); + db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); mp_properties_pages [m_index]->apply (); mp_properties_pages [m_index]->update (); + m_transaction_id = t.id (); + END_PROTECTED } void PropertiesDialog::cancel_pressed () { + // undo whatever we've done so far + if (m_transaction_id > 0) { + + // because undo does not maintain a valid selection we clear it + mp_editables->clear_selection (); + + mp_manager->undo (); + m_transaction_id = 0; + + } + // make sure that the property pages are no longer used .. disconnect (); // close the dialog @@ -293,11 +334,13 @@ BEGIN_PROTECTED if (! mp_properties_pages [m_index]->readonly ()) { - db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes"))); + db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); mp_properties_pages [m_index]->apply (); mp_properties_pages [m_index]->update (); + m_transaction_id = t.id (); + } // make sure that the property pages are no longer used .. diff --git a/src/laybasic/laybasic/layPropertiesDialog.h b/src/laybasic/laybasic/layPropertiesDialog.h index c06efcb69..5dcb209b7 100644 --- a/src/laybasic/laybasic/layPropertiesDialog.h +++ b/src/laybasic/laybasic/layPropertiesDialog.h @@ -32,12 +32,11 @@ #include #include +#include + #include "ui_PropertiesDialog.h" -namespace db -{ - class Manager; -} +#include class QStackedLayout; @@ -74,13 +73,15 @@ public: ~PropertiesDialog (); private: - std::vector mp_properties_pages; + std::vector mp_properties_pages; db::Manager *mp_manager; lay::Editables *mp_editables; int m_index; QStackedLayout *mp_stack; lay::MainWindow *mp_mw; size_t m_objects, m_current_object; + bool m_auto_applied; + db::Manager::transaction_id_t m_transaction_id; void disconnect (); bool any_prev () const; @@ -88,6 +89,7 @@ private: void update_title (); public slots: + void apply (); void next_pressed (); void prev_pressed (); void apply_pressed (); From 6e860361c093eae9af272e10fa113a6b66cf4b22 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 24 Aug 2020 22:44:17 +0200 Subject: [PATCH 063/113] WIP: show properties - use transient selection if nothing else is selected. --- src/laybasic/laybasic/layEditable.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/laybasic/laybasic/layEditable.cc b/src/laybasic/laybasic/layEditable.cc index e09044dde..0a86a8c3e 100644 --- a/src/laybasic/laybasic/layEditable.cc +++ b/src/laybasic/laybasic/layEditable.cc @@ -627,6 +627,11 @@ Editables::cancel_edits () void Editables::show_properties (QWidget *parent) { + if (selection_size () == 0) { + // try to use the transient selection for the real one + transient_to_selection (); + } + // re-create a new properties dialog if (mp_properties_dialog) { delete mp_properties_dialog; From 3003eb96e73f8ee6d35bbfad5ad46e200ceb5b60 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 26 Aug 2020 00:38:41 +0200 Subject: [PATCH 064/113] WIP: life updated on most shape properties, some bug fixes. --- src/edt/edt/edtInstPropertiesPage.cc | 88 ++-- src/edt/edt/edtInstPropertiesPage.h | 5 +- src/edt/edt/edtPropertiesPageUtils.cc | 2 +- src/edt/edt/edtPropertiesPageUtils.h | 2 +- src/edt/edt/edtPropertiesPages.cc | 477 +++++++++++++------ src/edt/edt/edtPropertiesPages.h | 21 +- src/laybasic/laybasic/PropertiesDialog.ui | 94 +++- src/laybasic/laybasic/layLayoutView.cc | 2 +- src/laybasic/laybasic/layProperties.h | 2 +- src/laybasic/laybasic/layPropertiesDialog.cc | 68 +-- src/laybasic/laybasic/layPropertiesDialog.h | 2 - 11 files changed, 482 insertions(+), 281 deletions(-) diff --git a/src/edt/edt/edtInstPropertiesPage.cc b/src/edt/edt/edtInstPropertiesPage.cc index 84aaf83ca..b3f8cc1c7 100644 --- a/src/edt/edt/edtInstPropertiesPage.cc +++ b/src/edt/edt/edtInstPropertiesPage.cc @@ -65,20 +65,42 @@ InstPropertiesPage::InstPropertiesPage (edt::Service *service, db::Manager *mana connect (lib_cbx, SIGNAL (currentIndexChanged (int)), this, SLOT (library_changed (int))); connect (cell_name_le, SIGNAL (textChanged (const QString &)), this, SLOT (cell_name_changed (const QString &))); - connect (lib_cbx, SIGNAL (activated (int)), this, SIGNAL (edited ())); - connect (cell_name_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); - connect (array_grp, SIGNAL (clicked ()), this, SIGNAL (edited ())); - connect (rows_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); - connect (columns_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); - connect (row_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); - connect (row_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); - connect (column_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); - connect (column_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); - connect (pos_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); - connect (pos_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); - connect (angle_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); - connect (mag_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); - connect (mirror_cbx, SIGNAL (clicked ()), this, SIGNAL (edited ())); + if (! readonly ()) { + + connect (lib_cbx, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (cell_name_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (array_grp, SIGNAL (clicked ()), this, SIGNAL (edited ())); + connect (rows_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (columns_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (row_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (row_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (column_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (column_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (pos_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (pos_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (angle_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (mag_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (mirror_cbx, SIGNAL (clicked ()), this, SIGNAL (edited ())); + + } else { + + browse_pb->setEnabled (false); + cell_name_le->setReadOnly (true); + rows_le->setReadOnly (true); + columns_le->setReadOnly (true); + row_x_le->setReadOnly (true); + row_y_le->setReadOnly (true); + column_x_le->setReadOnly (true); + column_y_le->setReadOnly (true); + pos_x_le->setReadOnly (true); + pos_y_le->setReadOnly (true); + angle_le->setReadOnly (true); + mag_le->setReadOnly (true); + lib_cbx->setEnabled (false); + array_grp->setEnabled (false); + mirror_cbx->setEnabled (false); + + } QHBoxLayout *layout = new QHBoxLayout (pcell_tab); layout->setMargin (0); @@ -580,7 +602,7 @@ InstPropertiesPage::recompute_selection_ptrs (const std::vectorview ()->layer_snapshot (); unsigned int cv_index = m_selection_ptrs [m_index]->cv_index (); @@ -609,32 +631,7 @@ InstPropertiesPage::do_apply (bool current_only) bool relative_mode = false; if (! current_only && applicator->supports_relative_mode ()) { - - static bool s_relative_mode = true; - - QMessageBox mb (QMessageBox::Question, - tr ("Apply Changes To All"), - tr ("For this operation absolute or relative mode is available which affects the way parameters of the selected objects are changed:\n\n" - "In absolute mode, they will be set to the given value. In relative mode, they will be adjusted by the same amount.\n"), - QMessageBox::NoButton, this); - - mb.addButton (tr ("Cancel"), QMessageBox::RejectRole); - QPushButton *absolute = mb.addButton (tr ("Absolute"), QMessageBox::NoRole); - QPushButton *relative = mb.addButton (tr ("Relative"), QMessageBox::YesRole); - - mb.setDefaultButton (s_relative_mode ? relative : absolute); - - mb.exec (); - - if (mb.clickedButton () == absolute) { - s_relative_mode = relative_mode = false; - } else if (mb.clickedButton () == relative) { - s_relative_mode = relative_mode = true; - } else { - // Cancel pressed - return; - } - + relative_mode = relative; } // Note: using the apply-all scheme for applying a single change may look like overhead. @@ -692,9 +689,6 @@ InstPropertiesPage::do_apply (bool current_only) size_t index = p - m_selection_ptrs.begin (); - // save previous selection so we can restore it - m_saved_selection.push_back (std::make_pair (index, new_sel[index])); - // change selection to new instance new_sel[index].back ().inst_ptr = new_inst; @@ -729,7 +723,7 @@ InstPropertiesPage::do_apply (bool current_only) void InstPropertiesPage::apply () { - do_apply (true); + do_apply (true, false); } bool @@ -739,9 +733,9 @@ InstPropertiesPage::can_apply_to_all () const } void -InstPropertiesPage::apply_to_all () +InstPropertiesPage::apply_to_all (bool relative) { - do_apply (false); + do_apply (false, relative); } void diff --git a/src/edt/edt/edtInstPropertiesPage.h b/src/edt/edt/edtInstPropertiesPage.h index ca4acb932..28394f4b3 100644 --- a/src/edt/edt/edtInstPropertiesPage.h +++ b/src/edt/edt/edtInstPropertiesPage.h @@ -65,13 +65,12 @@ protected: bool m_enable_cb_callback; db::properties_id_type m_prop_id; edt::PCellParametersPage *mp_pcell_parameters; - std::list > m_saved_selection; virtual bool readonly (); virtual void apply (); - virtual void apply_to_all (); + virtual void apply_to_all (bool relative); virtual bool can_apply_to_all () const; - void do_apply (bool current_only); + void do_apply (bool current_only, bool relative); virtual ChangeApplicator *create_applicator (db::Cell &cell, const db::Instance &inst, double dbu); protected slots: diff --git a/src/edt/edt/edtPropertiesPageUtils.cc b/src/edt/edt/edtPropertiesPageUtils.cc index 8b1490af5..801d0c57e 100644 --- a/src/edt/edt/edtPropertiesPageUtils.cc +++ b/src/edt/edt/edtPropertiesPageUtils.cc @@ -787,7 +787,7 @@ coords_to_string (const db::DPoint &dp, double dbu, bool du, const char *sep) } void -indicate_error (QLineEdit *le, const tl::Exception *ex) +indicate_error (QWidget *le, const tl::Exception *ex) { // by the way, update the foreground color of the cell edit box as well (red, if not valid) QPalette pl = le->palette (); diff --git a/src/edt/edt/edtPropertiesPageUtils.h b/src/edt/edt/edtPropertiesPageUtils.h index 8735e5074..74a33b672 100644 --- a/src/edt/edt/edtPropertiesPageUtils.h +++ b/src/edt/edt/edtPropertiesPageUtils.h @@ -483,7 +483,7 @@ db::Coord coord_from_string (const char *txt, double dbu, bool du, const db::VCp /** * @brief Configures a QLineEdit to indicate a format error */ -void indicate_error (QLineEdit *le, const tl::Exception *ex); +void indicate_error (QWidget *le, const tl::Exception *ex); } diff --git a/src/edt/edt/edtPropertiesPages.cc b/src/edt/edt/edtPropertiesPages.cc index 8250241bb..2cf797f2e 100644 --- a/src/edt/edt/edtPropertiesPages.cc +++ b/src/edt/edt/edtPropertiesPages.cc @@ -24,6 +24,7 @@ #include "edtPropertiesPages.h" #include "edtPropertiesPageUtils.h" #include "edtDialogs.h" +#include "edtPropertiesPageUtils.h" #include "layDialogs.h" #include "layObjectInstPath.h" #include "layLayoutView.h" @@ -174,7 +175,7 @@ ShapePropertiesPage::recompute_selection_ptrs (const std::vector applicator; @@ -203,32 +204,7 @@ ShapePropertiesPage::do_apply (bool current_only) // Ask whether to use relative or absolute mode bool relative_mode = false; if (! current_only && applicator->supports_relative_mode ()) { - - static bool s_relative_mode = true; - - QMessageBox mb (QMessageBox::Question, - tr ("Apply Changes To All"), - tr ("For this operation absolute or relative mode is available which affects the way parameters of the selected objects are changed:\n\n" - "In absolute mode, they will be set to the given value. In relative mode, they will be adjusted by the same amount.\n"), - QMessageBox::NoButton, this); - - mb.addButton (tr ("Cancel"), QMessageBox::RejectRole); - QPushButton *absolute = mb.addButton (tr ("Absolute"), QMessageBox::NoRole); - QPushButton *relative = mb.addButton (tr ("Relative"), QMessageBox::YesRole); - - mb.setDefaultButton (s_relative_mode ? relative : absolute); - - mb.exec (); - - if (mb.clickedButton () == absolute) { - s_relative_mode = relative_mode = false; - } else if (mb.clickedButton () == relative) { - s_relative_mode = relative_mode = true; - } else { - // Cancel pressed - return; - } - + relative_mode = relative; } // Note: using the apply-all scheme for applying a single change may look like overhead. @@ -333,7 +309,7 @@ ShapePropertiesPage::do_apply (bool current_only) void ShapePropertiesPage::apply () { - do_apply (true); + do_apply (true, false); } bool @@ -343,9 +319,9 @@ ShapePropertiesPage::can_apply_to_all () const } void -ShapePropertiesPage::apply_to_all () +ShapePropertiesPage::apply_to_all (bool relative) { - do_apply (false); + do_apply (false, relative); } void @@ -391,7 +367,9 @@ void ShapePropertiesPage::show_props () { lay::UserPropertiesForm props_form (this); - props_form.show (mp_service->view (), m_selection_ptrs [m_index]->cv_index (), m_prop_id); + if (props_form.show (mp_service->view (), m_selection_ptrs [m_index]->cv_index (), m_prop_id)) { + emit edited (); + } } bool @@ -404,13 +382,19 @@ ShapePropertiesPage::readonly () // PolygonPropertiesPage implementation PolygonPropertiesPage::PolygonPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent) - : ShapePropertiesPage (service, manager, parent) + : ShapePropertiesPage (service, manager, parent), m_in_text_changed (false) { setupUi (this); setup (); connect (inst_pb, SIGNAL (clicked ()), this, SLOT (show_inst ())); connect (prop_pb, SIGNAL (clicked ()), this, SLOT (show_props ())); + + if (! readonly ()) { + connect (pointListEdit, SIGNAL (textChanged ()), this, SLOT (text_changed ())); + } else { + pointListEdit->setReadOnly (true); + } } void @@ -448,61 +432,87 @@ PolygonPropertiesPage::do_update (const db::Shape &shape, double dbu, const std: } - pointListEdit->setText (tl::to_qstring (ptlist)); + if (! m_in_text_changed) { + pointListEdit->blockSignals (true); + pointListEdit->setText (tl::to_qstring (ptlist)); + pointListEdit->blockSignals (false); + } + pointCountLabel->setText (tl::to_qstring (tl::sprintf (tl::to_string (QObject::tr ("(%lu points)")), poly.vertices ()))); } +void +PolygonPropertiesPage::text_changed () +{ + m_in_text_changed = true; + try { + emit edited (); + } catch (tl::Exception &) { + // ignore exceptions + } + m_in_text_changed = false; +} + ChangeApplicator * PolygonPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape &shape, double dbu) { - std::string text (tl::to_string (pointListEdit->toPlainText ())); - tl::Extractor ex (text.c_str ()); - - db::VCplxTrans t = db::CplxTrans (trans ()).inverted (); - bool du = dbu_units (); - db::Polygon poly; - if (*ex.skip () == '(') { + try { - db::DPolygon dp; - ex.read (dp); + std::string text (tl::to_string (pointListEdit->toPlainText ())); + tl::Extractor ex (text.c_str ()); - poly = db::Polygon (dp.transformed (db::DCplxTrans (t) * db::DCplxTrans (du ? 1.0 : 1.0 / dbu))); + db::VCplxTrans t = db::CplxTrans (trans ()).inverted (); + bool du = dbu_units (); - } else { + if (*ex.skip () == '(') { - unsigned int h = 0; - while (! ex.at_end ()) { + db::DPolygon dp; + ex.read (dp); - std::vector points; + poly = db::Polygon (dp.transformed (db::DCplxTrans (t) * db::DCplxTrans (du ? 1.0 : 1.0 / dbu))); - while (! ex.at_end () && ! ex.test ("/")) { + } else { - double dx = 0.0, dy = 0.0; - ex.read (dx); - ex.test (","); - ex.read (dy); - ex.test (";"); + unsigned int h = 0; + while (! ex.at_end ()) { - points.push_back (point_from_dpoint (db::DPoint (dx, dy), dbu, du, t)); + std::vector points; + + while (! ex.at_end () && ! ex.test ("/")) { + + double dx = 0.0, dy = 0.0; + ex.read (dx); + ex.test (","); + ex.read (dy); + ex.test (";"); + + points.push_back (point_from_dpoint (db::DPoint (dx, dy), dbu, du, t)); + + } + + if (points.size () < 3) { + throw tl::Exception (tl::to_string (QObject::tr ("Polygon must have at least three points"))); + } + + if (h == 0) { + poly.assign_hull (points.begin (), points.end (), false /*not compressed*/); + } else { + poly.insert_hole (points.begin (), points.end (), false /*not compressed*/); + } + + ++h; } - if (points.size () < 3) { - throw tl::Exception (tl::to_string (QObject::tr ("Polygon must have at least three points"))); - } - - if (h == 0) { - poly.assign_hull (points.begin (), points.end (), false /*not compressed*/); - } else { - poly.insert_hole (points.begin (), points.end (), false /*not compressed*/); - } - - ++h; - } + indicate_error (pointListEdit, 0); + + } catch (tl::Exception &ex) { + indicate_error (pointListEdit, &ex); + throw; } db::Polygon org_poly; @@ -525,15 +535,32 @@ BoxPropertiesPage::BoxPropertiesPage (edt::Service *service, db::Manager *manage setup (); mode_tab->setCurrentIndex (s_coordinateMode ? 0 : 1); - connect (mode_tab, SIGNAL (currentChanged (int)), this, SLOT (changed ())); - connect (x1_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ())); - connect (y1_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ())); - connect (x2_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ())); - connect (y2_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ())); - connect (w_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ())); - connect (h_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ())); - connect (cx_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ())); - connect (cy_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ())); + + if (! readonly ()) { + + connect (mode_tab, SIGNAL (currentChanged (int)), this, SLOT (changed ())); + connect (x1_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ())); + connect (y1_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ())); + connect (x2_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ())); + connect (y2_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ())); + connect (w_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ())); + connect (h_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ())); + connect (cx_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ())); + connect (cy_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ())); + + } else { + + x1_le_1->setReadOnly (true); + y1_le_1->setReadOnly (true); + x2_le_1->setReadOnly (true); + y2_le_1->setReadOnly (true); + w_le_2->setReadOnly (true); + h_le_2->setReadOnly (true); + cx_le_2->setReadOnly (true); + cy_le_2->setReadOnly (true); + + } + connect (inst_pb, SIGNAL (clicked ()), this, SLOT (show_inst ())); connect (prop_pb, SIGNAL (clicked ()), this, SLOT (show_props ())); } @@ -576,11 +603,44 @@ BoxPropertiesPage::get_box (int mode) const { if (mode == 0) { + bool has_error = false; double x1 = 0.0, y1 = 0.0, x2 = 0.0, y2 = 0.0; - tl::from_string (tl::to_string (x1_le_1->text ()), x1); - tl::from_string (tl::to_string (y1_le_1->text ()), y1); - tl::from_string (tl::to_string (x2_le_1->text ()), x2); - tl::from_string (tl::to_string (y2_le_1->text ()), y2); + + try { + tl::from_string (tl::to_string (x1_le_1->text ()), x1); + indicate_error (x1_le_1, 0); + } catch (tl::Exception &ex) { + indicate_error (x1_le_1, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (y1_le_1->text ()), y1); + indicate_error (y1_le_1, 0); + } catch (tl::Exception &ex) { + indicate_error (y1_le_1, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (x2_le_1->text ()), x2); + indicate_error (x2_le_1, 0); + } catch (tl::Exception &ex) { + indicate_error (x2_le_1, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (y2_le_1->text ()), y2); + indicate_error (y2_le_1, 0); + } catch (tl::Exception &ex) { + indicate_error (y2_le_1, &ex); + has_error = true; + } + + if (has_error) { + throw tl::Exception (tl::to_string (tr ("Invalid values - see highlighted entry boxes"))); + } if (m_lr_swapped) { std::swap (x1, x2); @@ -603,11 +663,44 @@ BoxPropertiesPage::get_box (int mode) const } else { + bool has_error = false; double cx = 0.0, cy = 0.0, w = 0.0, h = 0.0; - tl::from_string (tl::to_string (cx_le_2->text ()), cx); - tl::from_string (tl::to_string (cy_le_2->text ()), cy); - tl::from_string (tl::to_string (w_le_2->text ()), w); - tl::from_string (tl::to_string (h_le_2->text ()), h); + + try { + tl::from_string (tl::to_string (cx_le_2->text ()), cx); + indicate_error (cx_le_2, 0); + } catch (tl::Exception &ex) { + indicate_error (cx_le_2, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (cy_le_2->text ()), cy); + indicate_error (cy_le_2, 0); + } catch (tl::Exception &ex) { + indicate_error (cy_le_2, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (w_le_2->text ()), w); + indicate_error (w_le_2, 0); + } catch (tl::Exception &ex) { + indicate_error (w_le_2, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (h_le_2->text ()), h); + indicate_error (h_le_2, 0); + } catch (tl::Exception &ex) { + indicate_error (h_le_2, &ex); + has_error = true; + } + + if (has_error) { + throw tl::Exception (tl::to_string (tr ("Invalid values - see highlighted entry boxes"))); + } db::VCplxTrans t = db::VCplxTrans (trans ().inverted ()); bool du = dbu_units (); @@ -664,6 +757,8 @@ BoxPropertiesPage::changed () set_box (get_box (m_tab_index)); } catch (...) { } + + emit edited (); } // ------------------------------------------------------------------------- @@ -677,6 +772,28 @@ TextPropertiesPage::TextPropertiesPage (edt::Service *service, db::Manager *mana connect (inst_pb, SIGNAL (clicked ()), this, SLOT (show_inst ())); connect (prop_pb, SIGNAL (clicked ()), this, SLOT (show_props ())); + + if (! readonly ()) { + + connect (text_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (size_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (orient_cbx, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (halign_cbx, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (valign_cbx, SIGNAL (activated (int)), this, SIGNAL (edited ())); + + } else { + + text_le->setReadOnly (true); + x_le->setReadOnly (true); + y_le->setReadOnly (true); + size_le->setReadOnly (true); + orient_cbx->setEnabled (false); + halign_cbx->setEnabled (false); + valign_cbx->setEnabled (false); + + } } void @@ -707,12 +824,28 @@ TextPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::st ChangeApplicator * TextPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape &shape, double dbu) { + bool has_error = false; + db::VCplxTrans t = db::CplxTrans (trans ()).inverted (); bool du = dbu_units (); double x = 0.0, y = 0.0; - tl::from_string (tl::to_string (x_le->text ()), x); - tl::from_string (tl::to_string (y_le->text ()), y); + + try { + tl::from_string (tl::to_string (x_le->text ()), x); + indicate_error (x_le, 0); + } catch (tl::Exception &ex) { + indicate_error (x_le, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (y_le->text ()), y); + indicate_error (y_le, 0); + } catch (tl::Exception &ex) { + indicate_error (y_le, &ex); + has_error = true; + } db::Vector tp = db::Vector (point_from_dpoint (db::DPoint (x, y), dbu, du, t)); db::Trans tt (orient_cbx->currentIndex (), tp); @@ -739,7 +872,13 @@ TextPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape db::Coord size = 0; if (! size_le->text ().isEmpty ()) { - size = coord_from_string (tl::to_string (size_le->text ()).c_str (), dbu, du, t); + try { + size = coord_from_string (tl::to_string (size_le->text ()).c_str (), dbu, du, t); + indicate_error (size_le, 0); + } catch (tl::Exception &ex) { + indicate_error (size_le, &ex); + has_error = true; + } } if (size != org_text.size ()) { appl->add (new TextSizeChangeApplicator (size)); @@ -749,6 +888,10 @@ TextPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape appl->add (new TextStringChangeApplicator (str)); } + if (has_error) { + throw tl::Exception (tl::to_string (tr ("Invalid values - see highlighted entry boxes"))); + } + return appl.release (); } @@ -756,16 +899,22 @@ TextPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape // PathPropertiesPage implementation PathPropertiesPage::PathPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent) - : ShapePropertiesPage (service, manager, parent) + : ShapePropertiesPage (service, manager, parent), m_in_text_changed (false) { setupUi (this); setup (); connect (inst_pb, SIGNAL (clicked ()), this, SLOT (show_inst ())); connect (prop_pb, SIGNAL (clicked ()), this, SLOT (show_props ())); + + ptlist_le->setReadOnly (true); + width_le->setReadOnly (true); + start_ext_le->setReadOnly (true); + end_ext_le->setReadOnly (true); + round_cb->setEnabled (false); } -void +void PathPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::string &lname) { layer_lbl->setText (tl::to_qstring (lname)); @@ -788,7 +937,12 @@ PathPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::st } ptlist += coords_to_string (t * *pt, dbu, du); } - ptlist_le->setText (tl::to_qstring (ptlist)); + + if (! m_in_text_changed) { + ptlist_le->blockSignals (true); + ptlist_le->setText (tl::to_qstring (ptlist)); + ptlist_le->blockSignals (false); + } width_le->setText (tl::to_qstring (coord_to_string (t.ctrans (path.width ()), dbu, du))); start_ext_le->setText (tl::to_qstring (coord_to_string (t.ctrans (path.extensions ().first), dbu, du))); @@ -797,68 +951,16 @@ PathPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::st } ChangeApplicator * -PathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape &shape, double dbu) +PathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape & /*shape*/, double /*dbu*/) { - db::VCplxTrans t = db::CplxTrans (trans ()).inverted (); - bool du = dbu_units (); - - std::string text (tl::to_string (ptlist_le->toPlainText ())); - tl::Extractor ex (text.c_str ()); - - std::vector points; - - while (! ex.at_end ()) { - - double dx = 0.0, dy = 0.0; - ex.read (dx); - ex.read (dy); - - points.push_back (point_from_dpoint (db::DPoint (dx, dy), dbu, du, t)); - - } - - if (points.size () < 1) { - throw tl::Exception (tl::to_string (QObject::tr ("The path must have at least one point"))); - } - - db::Coord w = coord_from_string (tl::to_string (width_le->text ()).c_str (), dbu, du, t); - db::Coord se = coord_from_string (tl::to_string (start_ext_le->text ()).c_str (), dbu, du, t); - db::Coord ee = coord_from_string (tl::to_string (end_ext_le->text ()).c_str (), dbu, du, t); - bool round = round_cb->isChecked (); - - std::auto_ptr appl; - - db::Path org_path; - shape.path (org_path); - std::vector org_points; - for (db::Path::iterator p = org_path.begin (); p != org_path.end (); ++p) { - org_points.push_back (*p); - } - - if (org_points != points) { - appl->add (new PathPointsChangeApplicator (points, org_points)); - } - if (w != org_path.width ()) { - appl->add (new PathWidthChangeApplicator (w, org_path.width ())); - } - if (se != org_path.extensions ().first) { - appl->add (new PathStartExtensionChangeApplicator (se)); - } - if (ee != org_path.extensions ().second) { - appl->add (new PathEndExtensionChangeApplicator (ee)); - } - if (round != org_path.round ()) { - appl->add (new PathRoundEndChangeApplicator (round)); - } - - return appl.release (); + return 0; } // ------------------------------------------------------------------------- // EditablePathPropertiesPage implementation EditablePathPropertiesPage::EditablePathPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent) - : ShapePropertiesPage (service, manager, parent) + : ShapePropertiesPage (service, manager, parent), m_in_text_changed (false) { setupUi (this); setup (); @@ -866,6 +968,12 @@ EditablePathPropertiesPage::EditablePathPropertiesPage (edt::Service *service, d connect (inst_pb, SIGNAL (clicked ()), this, SLOT (show_inst ())); connect (prop_pb, SIGNAL (clicked ()), this, SLOT (show_props ())); connect (type_cb, SIGNAL (currentIndexChanged (int)), this, SLOT (type_selected (int))); + + connect (ptlist_le, SIGNAL (textChanged ()), this, SLOT (text_changed ())); + connect (width_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (start_ext_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (end_ext_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (type_cb, SIGNAL (activated (int)), this, SIGNAL (edited ())); } static int @@ -886,7 +994,19 @@ path_type_choice (const db::Path &path) } } -void +void +EditablePathPropertiesPage::text_changed () +{ + m_in_text_changed = true; + try { + emit edited (); + } catch (tl::Exception &) { + // ignore exceptions + } + m_in_text_changed = false; +} + +void EditablePathPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::string &lname) { layer_lbl->setText (tl::to_qstring (lname)); @@ -909,7 +1029,12 @@ EditablePathPropertiesPage::do_update (const db::Shape &shape, double dbu, const } ptlist += coords_to_string (t * *pt, dbu, du); } - ptlist_le->setText (tl::to_qstring (ptlist)); + + if (! m_in_text_changed) { + ptlist_le->blockSignals (true); + ptlist_le->setText (tl::to_qstring (ptlist)); + ptlist_le->blockSignals (false); + } db::Coord w = path.width (); db::Coord se = path.extensions ().first; @@ -921,6 +1046,10 @@ EditablePathPropertiesPage::do_update (const db::Shape &shape, double dbu, const end_ext_le->setText (tl::to_qstring (coord_to_string (t.ctrans (ee), dbu, du))); int type_choice = path_type_choice (path); + if (type_cb->currentIndex () == 2) { + // keep "variable" mode, otherwise if's difficult to switch to it + type_choice = 2; + } type_cb->setCurrentIndex (type_choice); type_selected (type_choice); } @@ -928,6 +1057,8 @@ EditablePathPropertiesPage::do_update (const db::Shape &shape, double dbu, const ChangeApplicator * EditablePathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape &shape, double dbu) { + bool has_error = false; + db::VCplxTrans t = db::CplxTrans (trans ()).inverted (); bool du = dbu_units (); @@ -936,22 +1067,38 @@ EditablePathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db std::vector points; - while (! ex.at_end ()) { + try { - double dx = 0.0, dy = 0.0; - ex.read (dx); - ex.read (dy); + while (! ex.at_end ()) { - points.push_back (point_from_dpoint (db::DPoint (dx, dy), dbu, du, t)); + double dx = 0.0, dy = 0.0; + ex.read (dx); + ex.read (dy); + points.push_back (point_from_dpoint (db::DPoint (dx, dy), dbu, du, t)); + + } + + if (points.size () < 1) { + throw tl::Exception (tl::to_string (QObject::tr ("The path must have at least one point"))); + } + + indicate_error (ptlist_le, 0); + + } catch (tl::Exception &ex) { + indicate_error (ptlist_le, &ex); + has_error = true; } - if (points.size () < 1) { - throw tl::Exception (tl::to_string (QObject::tr ("The path must have at least one point"))); + db::Coord w = 0; + try { + w = coord_from_string (tl::to_string (width_le->text ()).c_str (), dbu, du, t); + indicate_error (width_le, 0); + } catch (tl::Exception &ex) { + indicate_error (width_le, &ex); + has_error = true; } - db::Coord w = coord_from_string (tl::to_string (width_le->text ()).c_str (), dbu, du, t); - db::Coord se = 0, ee = 0; switch (type_cb->currentIndex ()) { case 0: // flush @@ -961,8 +1108,20 @@ EditablePathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db se = ee = std::numeric_limits ::min (); // force to half width break; case 2: // variable - se = coord_from_string (tl::to_string (start_ext_le->text ()).c_str (), dbu, du, t); - ee = coord_from_string (tl::to_string (end_ext_le->text ()).c_str (), dbu, du, t); + try { + se = coord_from_string (tl::to_string (start_ext_le->text ()).c_str (), dbu, du, t); + indicate_error (start_ext_le, 0); + } catch (tl::Exception &ex) { + indicate_error (start_ext_le, &ex); + has_error = true; + } + try { + ee = coord_from_string (tl::to_string (end_ext_le->text ()).c_str (), dbu, du, t); + indicate_error (end_ext_le, 0); + } catch (tl::Exception &ex) { + indicate_error (end_ext_le, &ex); + has_error = true; + } break; } @@ -989,6 +1148,10 @@ EditablePathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db appl->add (new PathRoundEndChangeApplicator (type_cb->currentIndex () == 3)); } + if (has_error) { + throw tl::Exception (tl::to_string (tr ("Invalid values - see highlighted entry boxes"))); + } + return appl.release (); } diff --git a/src/edt/edt/edtPropertiesPages.h b/src/edt/edt/edtPropertiesPages.h index 5b612326c..42e283cc2 100644 --- a/src/edt/edt/edtPropertiesPages.h +++ b/src/edt/edt/edtPropertiesPages.h @@ -56,13 +56,15 @@ public: virtual void operator++ (); virtual void leave (); +protected: + virtual bool readonly (); + private: virtual void update (); virtual void apply (); - virtual void apply_to_all (); + virtual void apply_to_all (bool relative); virtual bool can_apply_to_all () const; - virtual void do_apply (bool current_only); - virtual bool readonly (); + virtual void do_apply (bool current_only, bool relative); void recompute_selection_ptrs (const std::vector &new_sel); protected: @@ -104,6 +106,12 @@ public: protected: virtual QCheckBox *dbu_checkbox () const { return dbu_cb; } virtual QCheckBox *abs_checkbox () const { return abs_cb; } + +public slots: + void text_changed (); + +private: + bool m_in_text_changed; }; class BoxPropertiesPage @@ -167,6 +175,9 @@ public: protected: virtual QCheckBox *dbu_checkbox () const { return dbu_cb; } virtual QCheckBox *abs_checkbox () const { return abs_cb; } + +private: + bool m_in_text_changed; }; class EditablePathPropertiesPage @@ -187,6 +198,10 @@ protected: public slots: void type_selected (int); + void text_changed (); + +private: + bool m_in_text_changed; }; } diff --git a/src/laybasic/laybasic/PropertiesDialog.ui b/src/laybasic/laybasic/PropertiesDialog.ui index dc1e57646..4edb7171e 100644 --- a/src/laybasic/laybasic/PropertiesDialog.ui +++ b/src/laybasic/laybasic/PropertiesDialog.ui @@ -17,7 +17,16 @@ 6 - + + 9 + + + 9 + + + 9 + + 9 @@ -54,7 +63,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -69,7 +87,7 @@ Previous - + :/left.png:/left.png @@ -89,7 +107,7 @@ Next - + :/right.png:/right.png @@ -97,6 +115,39 @@ + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 20 + + + + + + + + Change all + + + + + + + false + + + Relative + + + @@ -120,20 +171,6 @@ - - - - Apply - - - - - - - Apply To All - - - @@ -148,7 +185,24 @@ - + - + + + apply_to_all_cbx + toggled(bool) + relative_cbx + setEnabled(bool) + + + 260 + 372 + + + 320 + 372 + + + + diff --git a/src/laybasic/laybasic/layLayoutView.cc b/src/laybasic/laybasic/layLayoutView.cc index e1ff1d99d..fde47bc44 100644 --- a/src/laybasic/laybasic/layLayoutView.cc +++ b/src/laybasic/laybasic/layLayoutView.cc @@ -557,7 +557,7 @@ LayoutView::init (db::Manager *mgr, QWidget * /*parent*/) } - if (is_editable () && (m_options & LV_NoEditorOptionsPanel) == 0 && (m_options & LV_Naked) == 0) { + if ((m_options & LV_NoEditorOptionsPanel) == 0 && (m_options & LV_Naked) == 0) { mp_editor_options_frame = new QFrame (0); mp_editor_options_frame->setObjectName (QString::fromUtf8 ("editor_options_frame")); diff --git a/src/laybasic/laybasic/layProperties.h b/src/laybasic/laybasic/layProperties.h index a8fba1677..9eedcdafc 100644 --- a/src/laybasic/laybasic/layProperties.h +++ b/src/laybasic/laybasic/layProperties.h @@ -198,7 +198,7 @@ public: * If nothing was changed, the objects may be left untouched. * The dialog will start a transaction on the manager object. */ - virtual void apply_to_all () + virtual void apply_to_all (bool /*relative*/) { // default implementation is empty. } diff --git a/src/laybasic/laybasic/layPropertiesDialog.cc b/src/laybasic/laybasic/layPropertiesDialog.cc index 1c823c561..cc40f5aba 100644 --- a/src/laybasic/laybasic/layPropertiesDialog.cc +++ b/src/laybasic/laybasic/layPropertiesDialog.cc @@ -64,8 +64,8 @@ PropertiesDialog::PropertiesDialog (QWidget * /*parent*/, db::Manager *manager, content_frame->setLayout (mp_stack); // disable the apply button for first .. - apply_button->setEnabled (false); - apply_to_all_button->setEnabled (false); + apply_to_all_cbx->setEnabled (false); + relative_cbx->setEnabled (false); ok_button->setEnabled (false); // as a proposal, the start button can be enabled in most cases @@ -87,22 +87,28 @@ PropertiesDialog::PropertiesDialog (QWidget * /*parent*/, db::Manager *manager, // if at end disable the "Next" button and return (this may only happen at the first call) if (m_index >= int (mp_properties_pages.size ())) { + next_button->setEnabled (false); mp_stack->setCurrentWidget (dummy); - apply_button->setEnabled (false); - apply_to_all_button->setEnabled (false); + apply_to_all_cbx->setEnabled (false); + apply_to_all_cbx->setChecked (false); + relative_cbx->setEnabled (false); + relative_cbx->setChecked (false); ok_button->setEnabled (false); + } else { + next_button->setEnabled (any_next ()); mp_properties_pages [m_index]->update (); mp_stack->setCurrentWidget (mp_properties_pages [m_index]); - apply_button->setEnabled (! mp_properties_pages [m_index]->readonly ()); - apply_to_all_button->setEnabled (! mp_properties_pages [m_index]->readonly () && mp_properties_pages [m_index]->can_apply_to_all ()); + apply_to_all_cbx->setEnabled (! mp_properties_pages [m_index]->readonly () && mp_properties_pages [m_index]->can_apply_to_all ()); + apply_to_all_cbx->setChecked (false); + relative_cbx->setEnabled (apply_to_all_cbx->isEnabled () && apply_to_all_cbx->isChecked ()); + relative_cbx->setChecked (true); ok_button->setEnabled (! mp_properties_pages [m_index]->readonly ()); + } - connect (apply_button, SIGNAL (clicked ()), this, SLOT (apply_pressed ())); - connect (apply_to_all_button, SIGNAL (clicked ()), this, SLOT (apply_to_all_pressed ())); connect (ok_button, SIGNAL (clicked ()), this, SLOT (ok_pressed ())); connect (cancel_button, SIGNAL (clicked ()), this, SLOT (cancel_pressed ())); connect (prev_button, SIGNAL (clicked ()), this, SLOT (prev_pressed ())); @@ -159,8 +165,8 @@ BEGIN_PROTECTED prev_button->setEnabled (true); next_button->setEnabled (any_next ()); - apply_button->setEnabled (! mp_properties_pages [m_index]->readonly ()); - apply_to_all_button->setEnabled (! mp_properties_pages [m_index]->readonly () && mp_properties_pages [m_index]->can_apply_to_all ()); + apply_to_all_cbx->setEnabled (! mp_properties_pages [m_index]->readonly () && mp_properties_pages [m_index]->can_apply_to_all ()); + relative_cbx->setEnabled (apply_to_all_cbx->isEnabled () && apply_to_all_cbx->isChecked ()); ok_button->setEnabled (! mp_properties_pages [m_index]->readonly ()); mp_properties_pages [m_index]->update (); @@ -203,8 +209,8 @@ BEGIN_PROTECTED next_button->setEnabled (true); prev_button->setEnabled (any_prev ()); - apply_button->setEnabled (! mp_properties_pages [m_index]->readonly ()); - apply_to_all_button->setEnabled (! mp_properties_pages [m_index]->readonly () && mp_properties_pages [m_index]->can_apply_to_all ()); + apply_to_all_cbx->setEnabled (! mp_properties_pages [m_index]->readonly () && mp_properties_pages [m_index]->can_apply_to_all ()); + relative_cbx->setEnabled (apply_to_all_cbx->isEnabled () && apply_to_all_cbx->isChecked ()); ok_button->setEnabled (! mp_properties_pages [m_index]->readonly ()); mp_properties_pages [m_index]->update (); @@ -254,23 +260,6 @@ PropertiesDialog::any_prev () const return (index >= 0); } -void -PropertiesDialog::apply_to_all_pressed () -{ -BEGIN_PROTECTED - - { - db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes to all")), m_transaction_id); - - mp_properties_pages [m_index]->apply_to_all (); - mp_properties_pages [m_index]->update (); - - m_transaction_id = t.id (); - } - -END_PROTECTED -} - void PropertiesDialog::apply () { @@ -280,7 +269,11 @@ BEGIN_PROTECTED try { - mp_properties_pages [m_index]->apply (); + if (apply_to_all_cbx->isChecked () && mp_properties_pages [m_index]->can_apply_to_all ()) { + mp_properties_pages [m_index]->apply_to_all (relative_cbx->isChecked ()); + } else { + mp_properties_pages [m_index]->apply (); + } mp_properties_pages [m_index]->update (); } catch (tl::Exception &) { @@ -292,21 +285,6 @@ BEGIN_PROTECTED END_PROTECTED } -void -PropertiesDialog::apply_pressed () -{ -BEGIN_PROTECTED - - db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); - - mp_properties_pages [m_index]->apply (); - mp_properties_pages [m_index]->update (); - - m_transaction_id = t.id (); - -END_PROTECTED -} - void PropertiesDialog::cancel_pressed () { diff --git a/src/laybasic/laybasic/layPropertiesDialog.h b/src/laybasic/laybasic/layPropertiesDialog.h index 5dcb209b7..26a342dad 100644 --- a/src/laybasic/laybasic/layPropertiesDialog.h +++ b/src/laybasic/laybasic/layPropertiesDialog.h @@ -92,8 +92,6 @@ public slots: void apply (); void next_pressed (); void prev_pressed (); - void apply_pressed (); - void apply_to_all_pressed (); void cancel_pressed (); void ok_pressed (); From 994731c1f0d2437295171810c699c14f689b949e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Aug 2020 19:28:27 +0200 Subject: [PATCH 065/113] WIP: more consistent editor options --- src/edt/edt/edtEditorOptionsPages.cc | 2 ++ src/edt/edt/edtPlugin.cc | 18 +++++++++++++---- src/edt/edt/edtPlugin.h | 9 ++++++--- src/edt/edt/edtService.cc | 2 ++ src/lay/lay/layMainWindow.cc | 30 +++++++++++++++++++++++----- 5 files changed, 49 insertions(+), 12 deletions(-) diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index 669ae0503..1856c7d91 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -153,6 +153,8 @@ EditorOptionsPages::update (edt::EditorOptionsPage *page) index = mp_pages->count () - 1; } mp_pages->setCurrentIndex (index); + + setVisible (mp_pages->count () > 0); } void diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 975cf2ccc..68747b3d4 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -424,16 +424,26 @@ show_editor_options_page (lay::LayoutView *view) (*op)->activate (false); } + remove_editor_options_page (view); + + edt::EditorOptionsPages *pages = new edt::EditorOptionsPages (view->editor_options_frame (), prop_dialog_pages, view); + view->editor_options_frame ()->layout ()->addWidget (pages); + view->editor_options_frame ()->setFocusProxy (pages); +} + +void +remove_editor_options_page (lay::LayoutView *view) +{ + if (! view->editor_options_frame ()) { + return; + } + QObjectList children = view->editor_options_frame ()->children (); for (QObjectList::iterator c = children.begin (); c != children.end (); ++c) { if (dynamic_cast (*c)) { delete *c; } } - - edt::EditorOptionsPages *pages = new edt::EditorOptionsPages (view->editor_options_frame (), prop_dialog_pages, view); - view->editor_options_frame ()->layout ()->addWidget (pages); - view->editor_options_frame ()->setFocusProxy (pages); } static diff --git a/src/edt/edt/edtPlugin.h b/src/edt/edt/edtPlugin.h index c0ede8408..745f4c8a7 100644 --- a/src/edt/edt/edtPlugin.h +++ b/src/edt/edt/edtPlugin.h @@ -48,12 +48,15 @@ namespace edt }; /** - * @brief Show the editor options dialog - * - * This dialog is a global resource which is managed by the main plugin declaration + * @brief Creates an editor options page and installs it inside the view */ void show_editor_options_page (lay::LayoutView *view); + /** + * @brief Removes the editor options page from the view + */ + void remove_editor_options_page (lay::LayoutView *view); + /** * @brief Activate or deactivate a certain service * diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index c035d6698..aa5cf2b53 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -818,6 +818,8 @@ Service::deactivated () edit_cancel (); + remove_editor_options_page (view ()); + m_immediate = false; } diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 58cb94d21..70156fe2c 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -165,7 +165,7 @@ class ControlWidgetStack { public: ControlWidgetStack (QWidget *parent = 0, const char *name = 0) - : QFrame (parent) + : QFrame (parent), mp_current_widget (0) { setObjectName (QString::fromUtf8 (name)); @@ -217,6 +217,9 @@ public: void removeWidget (size_t index) { if (index < m_widgets.size ()) { + if (mp_current_widget == m_widgets [index]) { + mp_current_widget = 0; + } m_widgets.erase (m_widgets.begin () + index); } if (m_widgets.size () == 0) { @@ -224,13 +227,20 @@ public: } } + QWidget *currentWidget () const + { + return mp_current_widget; + } + void raiseWidget (size_t index) { + mp_current_widget = 0; bool any_visible = false; for (size_t i = 0; i < m_widgets.size (); ++i) { if (m_widgets [i]) { if (i == index) { m_widgets [i]->show (); + mp_current_widget = m_widgets [i]; any_visible = true; } else { m_widgets [i]->hide (); @@ -259,6 +269,11 @@ public: return mp_bglabel; } + size_t count () const + { + return m_widgets.size (); + } + protected: virtual void resizeEvent (QResizeEvent *) { @@ -277,6 +292,7 @@ protected: } std::vector m_widgets; + QWidget *mp_current_widget; QLabel *mp_bglabel; }; @@ -1990,13 +2006,17 @@ MainWindow::select_mode (int m) } } - bool editable = false; + bool eo_visible = false; if (pd_sel) { - editable = pd_sel->editable_enabled (); + eo_visible = pd_sel->editable_enabled (); + } + if (eo_visible && (!mp_eo_stack->currentWidget () || !mp_eo_stack->currentWidget ()->findChild ())) { + // + eo_visible = false; } - if (editable != m_eo_visible) { - m_eo_visible = editable; + if (eo_visible != m_eo_visible) { + m_eo_visible = eo_visible; show_dock_widget (mp_eo_dock_widget, m_eo_visible); } From 25588c10c80b4aa2563361b5152de79d2e62c04d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Aug 2020 20:32:37 +0200 Subject: [PATCH 066/113] WIP: partial editing now opens general options --- src/edt/edt/edtPartialService.cc | 13 ++++++++++++- src/edt/edt/edtPlugin.cc | 7 ++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index f66da5b94..96dc5c437 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -32,6 +32,7 @@ #include "edtService.h" #include "edtConfig.h" #include "edtDialogs.h" +#include "edtPlugin.h" #include "edtEditorOptionsPages.h" #include @@ -1061,15 +1062,25 @@ PartialService::move_ac () const void PartialService::deactivated () { + // make all editor option pages visible + activate_service (view (), plugin_declaration (), false); + // clear selection when this mode is left partial_select (db::DBox (), lay::Editable::Reset); clear_partial_transient_selection (); + remove_editor_options_page (view ()); } void PartialService::activated () { - // ... + if (view ()->is_editable ()) { + // Show editor options panel + show_editor_options_page (view ()); + } + + // make all editor option pages visible + activate_service (view (), plugin_declaration (), true); } void diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 68747b3d4..18f70c3a5 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -502,7 +502,7 @@ commit_recent (lay::LayoutView *view) } class PartialPluginDeclaration - : public lay::PluginDeclaration + : public PluginDeclarationBase { public: PartialPluginDeclaration (const std::string &title, const std::string &mouse_mode) @@ -516,6 +516,11 @@ public: // .. nothing yet .. } + virtual void get_editor_options_pages (std::vector & /*pages*/, lay::LayoutView * /*view*/, lay::Dispatcher * /*root*/) const + { + // .. no specific ones .. + } + virtual lay::Plugin *create_plugin (db::Manager *manager, lay::Dispatcher *root, lay::LayoutView *view) const { return new edt::PartialService (manager, view, root); From 7bf2c7f06061b945ed310e64418eb5903b3ddb38 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Aug 2020 20:53:33 +0200 Subject: [PATCH 067/113] WIP: live ruler properties. --- src/ant/ant/antPropertiesPage.cc | 133 +++++++++++++++++++++++++------ src/ant/ant/antPropertiesPage.h | 1 + 2 files changed, 108 insertions(+), 26 deletions(-) diff --git a/src/ant/ant/antPropertiesPage.cc b/src/ant/ant/antPropertiesPage.cc index 0df2c3967..f94185e0c 100644 --- a/src/ant/ant/antPropertiesPage.cc +++ b/src/ant/ant/antPropertiesPage.cc @@ -28,6 +28,27 @@ namespace ant { +// ------------------------------------------------------------------------- + +void +indicate_error (QWidget *le, const tl::Exception *ex) +{ + // by the way, update the foreground color of the cell edit box as well (red, if not valid) + QPalette pl = le->palette (); + if (ex) { + pl.setColor (QPalette::Active, QPalette::Text, Qt::red); + pl.setColor (QPalette::Active, QPalette::Base, QColor (Qt::red).lighter (180)); + le->setToolTip (tl::to_qstring (ex->msg ())); + } else { + QWidget *pw = dynamic_cast (le->parent ()); + tl_assert (pw != 0); + pl.setColor (QPalette::Active, QPalette::Text, pw->palette ().color (QPalette::Text)); + pl.setColor (QPalette::Active, QPalette::Base, pw->palette ().color (QPalette::Base)); + le->setToolTip (QString ()); + } + le->setPalette (pl); +} + // ------------------------------------------------------------------------- // PropertiesPage implementation @@ -50,6 +71,38 @@ PropertiesPage::PropertiesPage (ant::Service *rulers, db::Manager *manager, QWid p2_to_layout->setEnabled (! readonly()); both_to_layout->setEnabled (! readonly()); + if (! readonly ()) { + + connect (fmt_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (fmt_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (fmt_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (x1, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (x2, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (y1, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (y2, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + + connect (style_cb, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (outline_cb, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (main_position, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (main_xalign, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (main_yalign, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (xlabel_xalign, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (xlabel_yalign, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (ylabel_xalign, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (ylabel_yalign, SIGNAL (activated (int)), this, SIGNAL (edited ())); + + } else { + + fmt_le->setReadOnly (true); + fmt_x_le->setReadOnly (true); + fmt_y_le->setReadOnly (true); + x1->setReadOnly (true); + x2->setReadOnly (true); + y1->setReadOnly (true); + y2->setReadOnly (true); + + } + lay::activate_help_links (help_label); mp_rulers->clear_highlights (); @@ -91,7 +144,53 @@ PropertiesPage::swap_points_clicked () y2->setText (ty2); db::Transaction t (manager (), tl::to_string (QObject::tr ("Swap ruler points"))); - apply (); + emit edited (); +} + +void +PropertiesPage::get_points (db::DPoint &p1, db::DPoint &p2) +{ + double dx1 = 0.0, dy1 = 0.0, dx2 = 0.0, dy2 = 0.0; + bool has_error = false; + + try { + tl::from_string (tl::to_string (x1->text ()), dx1); + indicate_error (x1, 0); + } catch (tl::Exception &ex) { + indicate_error (x1, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (x2->text ()), dx2); + indicate_error (x2, 0); + } catch (tl::Exception &ex) { + indicate_error (x2, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (y1->text ()), dy1); + indicate_error (y1, 0); + } catch (tl::Exception &ex) { + indicate_error (y1, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (y2->text ()), dy2); + indicate_error (y2, 0); + } catch (tl::Exception &ex) { + indicate_error (y2, &ex); + has_error = true; + } + + if (has_error) { + throw tl::Exception (tl::to_string (tr ("At least one value is invalid - see highlighted entry fields"))); + } + + p1 = db::DPoint (dx1, dy1); + p2 = db::DPoint (dx2, dy2); } void @@ -104,13 +203,8 @@ PropertiesPage::snap_to_layout_clicked () ant::Service *service = dynamic_cast (editable ()); tl_assert (service != 0); - double dx1 = 0.0, dy1 = 0.0, dx2 = 0.0, dy2 = 0.0; - tl::from_string (tl::to_string (x1->text ()), dx1); - tl::from_string (tl::to_string (x2->text ()), dx2); - tl::from_string (tl::to_string (y1->text ()), dy1); - tl::from_string (tl::to_string (y2->text ()), dy2); - - db::DPoint p1 (dx1, dy1), p2 (dx2, dy2); + db::DPoint p1, p2; + get_points (p1, p2); ant::Object r = current (); @@ -152,7 +246,7 @@ PropertiesPage::snap_to_layout_clicked () } db::Transaction t (manager (), tl::to_string (snap_p1 ? QObject::tr ("Snap first ruler point") : QObject::tr ("Snap second ruler point"))); - apply (); + emit edited (); break; @@ -177,7 +271,7 @@ PropertiesPage::snap_to_layout_clicked () y2->setText (tl::to_qstring (tl::micron_to_string (ee.second.p2 ().y ()))); db::Transaction t (manager (), tl::to_string (QObject::tr ("Snap both ruler points"))); - apply (); + emit edited (); } @@ -268,22 +362,9 @@ PropertiesPage::readonly () void PropertiesPage::apply () { - double dx1 = current ().p1 ().x (), dy1 = current ().p1 ().y (); - double dx2 = current ().p2 ().x (), dy2 = current ().p2 ().y (); - // only adjust the values if the text has changed - if (tl::to_qstring (tl::micron_to_string (dx1)) != x1->text ()) { - tl::from_string (tl::to_string (x1->text ()), dx1); - } - if (tl::to_qstring (tl::micron_to_string (dx2)) != x2->text ()) { - tl::from_string (tl::to_string (x2->text ()), dx2); - } - if (tl::to_qstring (tl::micron_to_string (dy1)) != y1->text ()) { - tl::from_string (tl::to_string (y1->text ()), dy1); - } - if (tl::to_qstring (tl::micron_to_string (dy2)) != y2->text ()) { - tl::from_string (tl::to_string (y2->text ()), dy2); - } + db::DPoint p1, p2; + get_points (p1, p2); std::string fmt = tl::to_string (fmt_le->text ()); std::string fmt_x = tl::to_string (fmt_x_le->text ()); @@ -291,7 +372,7 @@ PropertiesPage::apply () Object::style_type style = Object::style_type (style_cb->currentIndex ()); Object::outline_type outline = Object::outline_type (outline_cb->currentIndex ()); - ant::Object ruler (db::DPoint (dx1, dy1), db::DPoint (dx2, dy2), current ().id (), fmt_x, fmt_y, fmt, style, outline, current ().snap (), current ().angle_constraint ()); + ant::Object ruler (p1, p2, current ().id (), fmt_x, fmt_y, fmt, style, outline, current ().snap (), current ().angle_constraint ()); ruler.set_main_position (Object::position_type (main_position->currentIndex ())); ruler.set_main_xalign (Object::alignment_type (main_xalign->currentIndex ())); diff --git a/src/ant/ant/antPropertiesPage.h b/src/ant/ant/antPropertiesPage.h index 5cae57e5f..8a23b1801 100644 --- a/src/ant/ant/antPropertiesPage.h +++ b/src/ant/ant/antPropertiesPage.h @@ -64,6 +64,7 @@ private: bool m_enable_cb_callback; const ant::Object ¤t () const; + void get_points(db::DPoint &p1, db::DPoint &p2); }; } From 95364ea36e80e4453e37f2a65256aa87685cd7ad Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Aug 2020 21:44:26 +0200 Subject: [PATCH 068/113] WIP: image properties now are also 'live' - auto-apply button has been removed. --- src/img/img/ImagePropertiesPage.ui | 11 - src/img/img/imgPropertiesPage.cc | 321 +++++++++++++++++++---------- src/img/img/imgPropertiesPage.h | 8 +- 3 files changed, 212 insertions(+), 128 deletions(-) diff --git a/src/img/img/ImagePropertiesPage.ui b/src/img/img/ImagePropertiesPage.ui index d0453605b..2ea522bdd 100644 --- a/src/img/img/ImagePropertiesPage.ui +++ b/src/img/img/ImagePropertiesPage.ui @@ -1058,16 +1058,6 @@ p, li { white-space: pre-wrap; } 0 - - - - Preview (Auto apply) - - - true - - - @@ -1141,7 +1131,6 @@ p, li { white-space: pre-wrap; } g_sb b_slider b_sb - preview_cbx reset_pb contrast_sb gamma_sb diff --git a/src/img/img/imgPropertiesPage.cc b/src/img/img/imgPropertiesPage.cc index f53c0a100..ddd9298e7 100644 --- a/src/img/img/imgPropertiesPage.cc +++ b/src/img/img/imgPropertiesPage.cc @@ -35,6 +35,27 @@ namespace img const double min_gamma = 0.3; const double max_gamma = 3.0; +// ------------------------------------------------------------------------- + +void +indicate_error (QWidget *le, const tl::Exception *ex) +{ + // by the way, update the foreground color of the cell edit box as well (red, if not valid) + QPalette pl = le->palette (); + if (ex) { + pl.setColor (QPalette::Active, QPalette::Text, Qt::red); + pl.setColor (QPalette::Active, QPalette::Base, QColor (Qt::red).lighter (180)); + le->setToolTip (tl::to_qstring (ex->msg ())); + } else { + QWidget *pw = dynamic_cast (le->parent ()); + tl_assert (pw != 0); + pl.setColor (QPalette::Active, QPalette::Text, pw->palette ().color (QPalette::Text)); + pl.setColor (QPalette::Active, QPalette::Base, pw->palette ().color (QPalette::Base)); + le->setToolTip (QString ()); + } + le->setPalette (pl); +} + // ------------------------------------------------------------------------- // PropertiesPage implementation @@ -73,6 +94,7 @@ void PropertiesPage::init () { m_no_signals = false; + m_in_color_mapping_signal = false; setupUi (this); @@ -108,6 +130,7 @@ PropertiesPage::init () connect (browse_pb, SIGNAL (clicked ()), this, SLOT (browse ())); connect (colors, SIGNAL (color_changed (std::pair)), false_color_control, SLOT (set_current_color (std::pair))); connect (false_color_control, SIGNAL (selection_changed (std::pair)), colors, SLOT (set_color (std::pair))); + connect (false_color_control, SIGNAL (color_mapping_changed ()), this, SLOT (color_mapping_changed ())); connect (brightness_slider, SIGNAL (valueChanged (int)), this, SLOT (brightness_slider_changed (int))); connect (brightness_sb, SIGNAL (valueChanged (int)), this, SLOT (brightness_spinbox_changed (int))); @@ -122,15 +145,22 @@ PropertiesPage::init () connect (b_slider, SIGNAL (valueChanged (int)), this, SLOT (blue_slider_changed (int))); connect (b_sb, SIGNAL (valueChanged (double)), this, SLOT (blue_spinbox_changed (double))); - connect (false_color_control, SIGNAL (color_mapping_changed ()), this, SLOT (color_mapping_changed ())); - connect (false_color_control, SIGNAL (selection_changed ()), this, SLOT (color_mapping_changed ())); - connect (from_le, SIGNAL (returnPressed ()), this, SLOT (min_max_return_pressed ())); - connect (to_le, SIGNAL (returnPressed ()), this, SLOT (min_max_return_pressed ())); - connect (value_le, SIGNAL (returnPressed ()), this, SLOT (value_return_pressed ())); + connect (from_le, SIGNAL (editingFinished ()), this, SLOT (min_max_value_changed ())); + connect (to_le, SIGNAL (editingFinished ()), this, SLOT (min_max_value_changed ())); + connect (value_le, SIGNAL (editingFinished ()), this, SLOT (value_changed ())); + connect (width_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (height_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (x_offset_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (y_offset_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (angle_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (shear_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (persp_tx_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (persp_ty_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + + connect (mirror_cbx, SIGNAL (clicked ()), this, SIGNAL (edited ())); connect (reset_pb, SIGNAL (clicked ()), this, SLOT (reset_pressed ())); connect (save_pb, SIGNAL (clicked ()), this, SLOT (save_pressed ())); - connect (preview_cbx, SIGNAL (clicked ()), this, SLOT (preview_checked ())); connect (define_landmarks_pb, SIGNAL (clicked ()), this, SLOT (define_landmarks_pressed ())); } @@ -202,11 +232,42 @@ PropertiesPage::readonly () return false; } -void -PropertiesPage::min_max_return_pressed () +void +PropertiesPage::get_xmin_xmax (double &xmin, double &xmax, bool &has_error_out) { -BEGIN_PROTECTED + bool has_error = false; + try { + tl::from_string (tl::to_string (from_le->text ()), xmin); + indicate_error (from_le, 0); + } catch (tl::Exception &ex) { + indicate_error (from_le, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (to_le->text ()), xmax); + indicate_error (to_le, 0); + } catch (tl::Exception &ex) { + indicate_error (to_le, &ex); + has_error = true; + } + + if (! has_error && xmin >= xmax) { + tl::Exception ex (tl::to_string (QObject::tr ("Invalid data value range (min. value must be less than max. value)"))); + indicate_error (from_le, &ex); + indicate_error (to_le, &ex); + has_error = true; + } + + if (has_error) { + has_error_out = true; + } +} + +void +PropertiesPage::min_max_value_changed () +{ value_le->setText (QString ()); value_le->setEnabled (false); @@ -214,10 +275,11 @@ BEGIN_PROTECTED colors->set_single_mode (false); double xmin, xmax; - tl::from_string (tl::to_string (from_le->text ()), xmin); - tl::from_string (tl::to_string (to_le->text ()), xmax); - if (xmin >= xmax) { - throw tl::Exception (tl::to_string (QObject::tr ("Invalid data value range (min. value must be less than max. value)"))); + bool has_error = false; + get_xmin_xmax (xmin, xmax, has_error); + + if (has_error) { + return; } if (false_color_control->has_selection () && false_color_control->selected_node () > 0 && false_color_control->selected_node () < int (false_color_control->nodes ().size ()) - 1) { @@ -236,9 +298,7 @@ BEGIN_PROTECTED recompute_histogram (); - preview (); - -END_PROTECTED + emit edited (); } void @@ -246,22 +306,20 @@ PropertiesPage::color_mapping_changed () { if (! m_no_signals) { + bool has_error = false; + value_le->setText (QString ()); value_le->setEnabled (false); colors->setEnabled (false_color_control->has_selection ()); colors->set_single_mode (false); - try { + if (false_color_control->has_selection () && false_color_control->selected_node () > 0 && false_color_control->selected_node () < int (false_color_control->nodes ().size ()) - 1) { - if (false_color_control->has_selection () && false_color_control->selected_node () > 0 && false_color_control->selected_node () < int (false_color_control->nodes ().size ()) - 1) { + double xmin, xmax; + get_xmin_xmax (xmin, xmax, has_error); - double xmin, xmax; - tl::from_string (tl::to_string (from_le->text ()), xmin); - tl::from_string (tl::to_string (to_le->text ()), xmax); - if (xmin >= xmax) { - throw tl::Exception (""); - } + if (! has_error) { double x = false_color_control->nodes () [false_color_control->selected_node ()].first; double xx = x * (xmax - xmin) + xmin; @@ -269,46 +327,57 @@ PropertiesPage::color_mapping_changed () value_le->setText (tl::to_qstring (tl::sprintf ("%.4g", xx))); value_le->setEnabled (true); - } else if (false_color_control->has_selection ()) { - - colors->set_single_mode (true); - } - } catch (...) { } + } else if (false_color_control->has_selection ()) { - preview (); + colors->set_single_mode (true); + + } + + if (! has_error) { + m_in_color_mapping_signal = true; + emit edited (); + m_in_color_mapping_signal = false; + } } } void -PropertiesPage::value_return_pressed () +PropertiesPage::value_changed () { -BEGIN_PROTECTED + double xx = 0; + bool has_error = false; double xmin, xmax; - tl::from_string (tl::to_string (from_le->text ()), xmin); - tl::from_string (tl::to_string (to_le->text ()), xmax); - if (xmin >= xmax) { - throw tl::Exception (tl::to_string (QObject::tr ("Invalid data value range (min. value must be less than max. value)"))); - } + get_xmin_xmax (xmin, xmax, has_error); double x = 0.0; - tl::from_string (tl::to_string (value_le->text ()), x); - - double xx = (x - xmin) / (xmax - xmin); - if (xx < 0 || xx > 1.0) { - throw tl::Exception (tl::to_string (QObject::tr ("The position entered (%g) must be between the minimum (%g) and maximum (%g) value")), x, xmin, xmax); + try { + tl::from_string (tl::to_string (value_le->text ()), x); + indicate_error (value_le, 0); + } catch (tl::Exception &ex) { + indicate_error (value_le, &ex); + has_error = true; } - m_no_signals = true; - false_color_control->set_current_position (xx); - m_no_signals = false; + xx = (x - xmin) / (xmax - xmin); + if (! has_error && (xx < 0 || xx > 1.0)) { + tl::Exception ex (tl::to_string (QObject::tr ("The position entered (%g) must be between the minimum (%g) and maximum (%g) value")), x, xmin, xmax); + indicate_error (value_le, &ex); + has_error = true; + } - preview (); + if (! has_error) { -END_PROTECTED + m_no_signals = true; + false_color_control->set_current_position (xx); + m_no_signals = false; + + emit edited (); + + } } inline double @@ -320,6 +389,10 @@ round_to_zero (double x) void PropertiesPage::update () { + if (m_in_color_mapping_signal) { + return; + } + m_no_signals = true; if (mp_service) { @@ -498,7 +571,7 @@ PropertiesPage::brightness_slider_changed (int value) m_no_signals = true; brightness_sb->setValue (value); - preview (); + emit edited (); m_no_signals = false; } @@ -511,7 +584,7 @@ PropertiesPage::brightness_spinbox_changed (int value) m_no_signals = true; brightness_slider->setValue (value); - preview (); + emit edited (); m_no_signals = false; } @@ -524,7 +597,7 @@ PropertiesPage::contrast_slider_changed (int value) m_no_signals = true; contrast_sb->setValue (value); - preview (); + emit edited (); m_no_signals = false; } @@ -537,7 +610,7 @@ PropertiesPage::contrast_spinbox_changed (int value) m_no_signals = true; contrast_slider->setValue (value); - preview (); + emit edited (); m_no_signals = false; } @@ -556,7 +629,7 @@ PropertiesPage::gamma_spinbox_changed (double value) gamma_slider->setValue (50 + int (0.5 + (value - 1.0) / (max_gamma - 1.0) * 50.0)); } - preview (); + emit edited (); m_no_signals = false; } @@ -578,7 +651,7 @@ PropertiesPage::gamma_slider_changed (int value) } gamma_sb->setValue (gamma); - preview (); + emit edited (); m_no_signals = false; } @@ -595,7 +668,7 @@ PropertiesPage::red_slider_changed (int value) double gain = value * 0.02; r_sb->setValue (gain); - preview (); + emit edited (); m_no_signals = false; } @@ -610,7 +683,7 @@ PropertiesPage::red_spinbox_changed (double value) m_no_signals = true; r_slider->setValue (int (0.5 + value * 50.0)); - preview (); + emit edited (); m_no_signals = false; } @@ -627,7 +700,7 @@ PropertiesPage::green_slider_changed (int value) double gain = value * 0.02; g_sb->setValue (gain); - preview (); + emit edited (); m_no_signals = false; } @@ -642,7 +715,7 @@ PropertiesPage::green_spinbox_changed (double value) m_no_signals = true; g_slider->setValue (int (0.5 + value * 50.0)); - preview (); + emit edited (); m_no_signals = false; } @@ -659,7 +732,7 @@ PropertiesPage::blue_slider_changed (int value) double gain = value * 0.02; b_sb->setValue (gain); - preview (); + emit edited (); m_no_signals = false; } @@ -674,9 +747,9 @@ PropertiesPage::blue_spinbox_changed (double value) m_no_signals = true; b_slider->setValue (int (0.5 + value * 50.0)); - preview (); + emit edited (); - m_no_signals = false; + m_no_signals = false; } void @@ -686,6 +759,7 @@ PropertiesPage::black_to_white () nodes.push_back (std::make_pair (0.0, std::make_pair (QColor (0, 0, 0), QColor (0, 0, 0)))); nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (255, 255, 255), QColor (255, 255, 255)))); false_color_control->set_nodes (nodes); + emit edited (); } void @@ -695,6 +769,7 @@ PropertiesPage::white_to_black () nodes.push_back (std::make_pair (0.0, std::make_pair (QColor (255, 255, 255), QColor (255, 255, 255)))); nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (0, 0, 0), QColor (0, 0, 0)))); false_color_control->set_nodes (nodes); + emit edited (); } void @@ -704,7 +779,7 @@ PropertiesPage::red_to_blue () nodes.push_back (std::make_pair (0.0, std::make_pair (QColor (255, 0, 0), QColor (255, 0, 0)))); nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (0, 0, 255), QColor (0, 0, 255)))); false_color_control->set_nodes (nodes); - + emit edited (); } void @@ -714,6 +789,7 @@ PropertiesPage::blue_to_red () nodes.push_back (std::make_pair (0.0, std::make_pair (QColor (0, 0, 255), QColor (0, 0, 255)))); nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (255, 0, 0), QColor (255, 0, 0)))); false_color_control->set_nodes (nodes); + emit edited (); } void @@ -725,11 +801,14 @@ PropertiesPage::reverse_color_order () std::swap (nodes [i].second.first, nodes [nodes.size () - 1 - i].second.second); } false_color_control->set_nodes (nodes); + emit edited (); } void PropertiesPage::apply () { + bool has_error = false; + db::Matrix3d matrix = mp_direct_image->matrix (); // The observer distance for perspective distortion is the average of the images width and height. @@ -741,45 +820,89 @@ PropertiesPage::apply () double w = matrix.mag_x (), h = matrix.mag_y (), x = matrix.disp ().x (), y = matrix.disp ().y (), a = matrix.angle (), sa = matrix.shear_angle (), tx = matrix.perspective_tilt_x (z), ty = matrix.perspective_tilt_y (z); - bool mirror; - - if (width_le->text () != tl::to_qstring (tl::micron_to_string (matrix.mag_x ()))) { + try { tl::from_string (tl::to_string (width_le->text ()), w); + if (w <= 0.0 || h <= 0.0) { + throw tl::Exception (tl::to_string (QObject::tr ("Pixel width or height must be positive, non-null values"))); + } + indicate_error (width_le, 0); + } catch (tl::Exception &ex) { + indicate_error (width_le, &ex); + has_error = true; } - if (height_le->text () != tl::to_qstring (tl::micron_to_string (matrix.mag_y ()))) { + + try { tl::from_string (tl::to_string (height_le->text ()), h); + indicate_error (height_le, 0); + } catch (tl::Exception &ex) { + indicate_error (height_le, &ex); + has_error = true; } - if (x_offset_le->text () != tl::to_qstring (tl::micron_to_string (round_to_zero (matrix.disp ().x ())))) { + + try { tl::from_string (tl::to_string (x_offset_le->text ()), x); + indicate_error (x_offset_le, 0); + } catch (tl::Exception &ex) { + indicate_error (x_offset_le, &ex); + has_error = true; } - if (y_offset_le->text () != tl::to_qstring (tl::micron_to_string (round_to_zero (matrix.disp ().y ())))) { + + try { tl::from_string (tl::to_string (y_offset_le->text ()), y); + indicate_error (y_offset_le, 0); + } catch (tl::Exception &ex) { + indicate_error (y_offset_le, &ex); + has_error = true; } - if (angle_le->text () != tl::to_qstring (tl::to_string (round_to_zero (matrix.angle ())))) { + + try { tl::from_string (tl::to_string (angle_le->text ()), a); + indicate_error (angle_le, 0); + } catch (tl::Exception &ex) { + indicate_error (angle_le, &ex); + has_error = true; } - if (shear_le->text () != tl::to_qstring (tl::to_string (round_to_zero (matrix.shear_angle ())))) { + + try { tl::from_string (tl::to_string (shear_le->text ()), sa); + if (sa <= -45 || sa >= 45) { + throw tl::Exception (tl::to_string (QObject::tr ("The shear angle must be larger than -45 and less than 45 degree"))); + } + indicate_error (shear_le, 0); + } catch (tl::Exception &ex) { + indicate_error (shear_le, &ex); + has_error = true; } - if (persp_tx_le->text () != tl::to_qstring (tl::to_string (round_to_zero (matrix.perspective_tilt_x (z))))) { + + try { tl::from_string (tl::to_string (persp_tx_le->text ()), tx); + if (tx <= -90 || tx >= 90) { + throw tl::Exception (tl::to_string (QObject::tr ("The perspective tilt angles must be larger than -90 and less than 90 degree"))); + } + indicate_error (persp_tx_le, 0); + } catch (tl::Exception &ex) { + indicate_error (persp_tx_le, &ex); + has_error = true; } - if (persp_ty_le->text () != tl::to_qstring (tl::to_string (round_to_zero (matrix.perspective_tilt_y (z))))) { + + try { tl::from_string (tl::to_string (persp_ty_le->text ()), ty); + if (ty <= -90 || ty >= 90) { + throw tl::Exception (tl::to_string (QObject::tr ("The perspective tilt angles must be larger than -90 and less than 90 degree"))); + } + indicate_error (persp_ty_le, 0); + } catch (tl::Exception &ex) { + indicate_error (persp_ty_le, &ex); + has_error = true; } - mirror = mirror_cbx->isChecked (); + bool mirror = mirror_cbx->isChecked (); - if (w <= 0.0 || h <= 0.0) { - throw tl::Exception (tl::to_string (QObject::tr ("Pixel width or height must be positive, non-null values"))); - } + double xmin, xmax; + get_xmin_xmax (xmin, xmax, has_error); - if (sa <= -45 || sa >= 45) { - throw tl::Exception (tl::to_string (QObject::tr ("The shear angle must be larger than -45 and less than 45 degree"))); - } - - if (tx <= -90 || tx >= 90 || ty <= -90 || ty >= 90) { - throw tl::Exception (tl::to_string (QObject::tr ("The perspective tilt angles must be larger than -90 and less than 90 degree"))); + if (has_error) { + throw tl::Exception (tl::to_string (tr ("At least one value is invalid - see highlighted entry fields"))); } // Compute the new observer distance @@ -790,13 +913,6 @@ PropertiesPage::apply () matrix = db::Matrix3d::disp (db::DVector (x, y)) * db::Matrix3d::perspective (tx, ty, z) * db::Matrix3d::rotation (a) * db::Matrix3d::shear (sa) * db::Matrix3d::mag (w, h) * db::Matrix3d::mirror (mirror); mp_direct_image->set_matrix (matrix); - double xmin, xmax; - tl::from_string (tl::to_string (from_le->text ()), xmin); - tl::from_string (tl::to_string (to_le->text ()), xmax); - if (xmin >= xmax) { - throw tl::Exception (tl::to_string (QObject::tr ("Invalid data value range (min. value must be less than max. value)"))); - } - mp_direct_image->set_min_value (xmin); mp_direct_image->set_max_value (xmax); @@ -891,30 +1007,7 @@ PropertiesPage::reset_pressed () m_no_signals = false; - preview (); -} - -void -PropertiesPage::preview_checked () -{ - preview (); -} - -void -PropertiesPage::preview () -{ - if (preview_cbx->isChecked ()) { - - BEGIN_PROTECTED_CLEANUP - - apply (); // this is a HACK, because it changes the current object - - END_PROTECTED_CLEANUP - { - preview_cbx->setChecked (false); - } - - } + emit edited (); } void @@ -922,7 +1015,9 @@ PropertiesPage::define_landmarks_pressed () { if (mp_direct_image) { img::LandmarksDialog dialog (this, *mp_direct_image); - dialog.exec (); + if (dialog.exec ()) { + emit edited (); + } } } diff --git a/src/img/img/imgPropertiesPage.h b/src/img/img/imgPropertiesPage.h index 1de6a3f88..8a45bd186 100644 --- a/src/img/img/imgPropertiesPage.h +++ b/src/img/img/imgPropertiesPage.h @@ -64,7 +64,7 @@ public: private slots: void browse (); - void value_return_pressed (); + void value_changed (); void color_mapping_changed (); void brightness_slider_changed (int value); void brightness_spinbox_changed (int value); @@ -83,8 +83,7 @@ private slots: void red_to_blue (); void blue_to_red (); void reverse_color_order (); - void min_max_return_pressed (); - void preview_checked (); + void min_max_value_changed (); void reset_pressed (); void save_pressed (); void define_landmarks_pressed (); @@ -95,11 +94,12 @@ private: img::Service *mp_service; img::Object *mp_direct_image; bool m_no_signals; + bool m_in_color_mapping_signal; void recompute_histogram (); void invalidate (); void init (); - void preview (); + void get_xmin_xmax (double &xmin, double &xmax, bool &has_error_out); }; } From 5c6d3775cb265b2e43abc52952716f8beccb15cb Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 31 Aug 2020 00:45:49 +0200 Subject: [PATCH 069/113] Fixed a compilation issue for a shader program. --- src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index 8dbe74798..ff9fc8f2a 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -752,7 +752,7 @@ D25ViewWidget::initializeGL () "uniform highp float mist_factor;\n" "uniform highp float mist_add;\n" "\n" - "vec4 color_by_z(lowp vec4 c, highp float z) {\n" + "lowp vec4 color_by_z(lowp vec4 c, highp float z) {\n" " highp float mist_rgb = c.g * mist_factor + mist_add;\n" " lowp vec4 mist_color = vec4(mist_rgb, mist_rgb, mist_rgb, 1.0);\n" " highp float d = 0.12;\n" // d + dd/2 = 0.15 = 1/? From 9c7a9f4c6cba99c1a416df992c4275648b89f2f6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 2 Sep 2020 23:38:31 +0200 Subject: [PATCH 070/113] Some refactoring for reducing size of layMainWindow.cc a little. --- src/lay/lay/HelpDialog.ui | 63 +++ src/lay/lay/lay.pro | 15 +- src/lay/lay/layControlWidgetStack.cc | 141 +++++++ src/lay/lay/layControlWidgetStack.h | 77 ++++ src/lay/lay/layHelpAboutDialog.cc | 140 +++++++ src/lay/lay/layHelpAboutDialog.h | 55 +++ src/lay/lay/layHelpDialog.cc | 46 +-- src/lay/lay/layHelpDialog.h | 7 +- src/lay/lay/layMainWindow.cc | 508 ++----------------------- src/lay/lay/layMainWindow.h | 46 +-- src/lay/lay/layProgressDialog.cc | 75 ++++ src/lay/lay/layProgressDialog.h | 61 +++ src/lay/lay/layTextProgressDelegate.cc | 73 ++++ src/lay/lay/layTextProgressDelegate.h | 55 +++ src/lay/lay/layViewWidgetStack.cc | 103 +++++ src/lay/lay/layViewWidgetStack.h | 64 ++++ 16 files changed, 991 insertions(+), 538 deletions(-) create mode 100644 src/lay/lay/HelpDialog.ui create mode 100644 src/lay/lay/layControlWidgetStack.cc create mode 100644 src/lay/lay/layControlWidgetStack.h create mode 100644 src/lay/lay/layHelpAboutDialog.cc create mode 100644 src/lay/lay/layHelpAboutDialog.h create mode 100644 src/lay/lay/layProgressDialog.cc create mode 100644 src/lay/lay/layProgressDialog.h create mode 100644 src/lay/lay/layTextProgressDelegate.cc create mode 100644 src/lay/lay/layTextProgressDelegate.h create mode 100644 src/lay/lay/layViewWidgetStack.cc create mode 100644 src/lay/lay/layViewWidgetStack.h diff --git a/src/lay/lay/HelpDialog.ui b/src/lay/lay/HelpDialog.ui new file mode 100644 index 000000000..f176091db --- /dev/null +++ b/src/lay/lay/HelpDialog.ui @@ -0,0 +1,63 @@ + + + HelpDialog + + + + 0 + 0 + 900 + 500 + + + + Assistant + + + + + + + 0 + 1 + + + + + + + + QDialogButtonBox::Close + + + + + + + + lay::BrowserPanel + QWidget +
layBrowserPanel.h
+ 1 +
+
+ + + + button_frame + rejected() + HelpDialog + accept() + + + 436 + 487 + + + 309 + 498 + + + + +
diff --git a/src/lay/lay/lay.pro b/src/lay/lay/lay.pro index cfb7e9c75..9724ba932 100644 --- a/src/lay/lay/lay.pro +++ b/src/lay/lay/lay.pro @@ -9,9 +9,11 @@ DEFINES += MAKE_LAY_LIBRARY HEADERS = \ layApplication.h \ layClipDialog.h \ + layControlWidgetStack.h \ layCrashMessage.h \ layFillDialog.h \ layGSIHelpProvider.h \ + layHelpAboutDialog.h \ layHelpDialog.h \ layHelpProvider.h \ layHelpSource.h \ @@ -25,6 +27,7 @@ HEADERS = \ layMainWindow.h \ layNavigator.h \ layProgress.h \ + layProgressDialog.h \ layProgressWidget.h \ layResourceHelpProvider.h \ layRuntimeErrorForm.h \ @@ -35,6 +38,7 @@ HEADERS = \ laySettingsForm.h \ layTechSetupDialog.h \ layTextProgress.h \ + layTextProgressDelegate.h \ layVersion.h \ layCommon.h \ layConfig.h \ @@ -57,7 +61,8 @@ HEADERS = \ layMacroEditorSetupPage.h \ layPasswordDialog.h \ layForceLink.h \ - layInit.h + layInit.h \ + layViewWidgetStack.h FORMS = \ ClipDialog.ui \ @@ -66,6 +71,7 @@ FORMS = \ DeleteModeDialog.ui \ FillDialog.ui \ HelpAboutDialog.ui \ + HelpDialog.ui \ LogViewerDialog.ui \ MacroEditorDialog.ui \ MacroPropertiesDialog.ui \ @@ -112,9 +118,11 @@ SOURCES = \ gsiDeclLayMainWindow.cc \ layApplication.cc \ layClipDialog.cc \ + layControlWidgetStack.cc \ layCrashMessage.cc \ layFillDialog.cc \ layGSIHelpProvider.cc \ + layHelpAboutDialog.cc \ layHelpDialog.cc \ layHelpProvider.cc \ layHelpSource.cc \ @@ -128,6 +136,7 @@ SOURCES = \ layMainWindow.cc \ layNavigator.cc \ layProgress.cc \ + layProgressDialog.cc \ layProgressWidget.cc \ layResourceHelpProvider.cc \ layRuntimeErrorForm.cc \ @@ -139,6 +148,7 @@ SOURCES = \ laySettingsForm.cc \ layTechSetupDialog.cc \ layTextProgress.cc \ + layTextProgressDelegate.cc \ layVersion.cc \ layMacroController.cc \ layTechnologyController.cc \ @@ -159,7 +169,8 @@ SOURCES = \ layMacroEditorSetupPage.cc \ layPasswordDialog.cc \ layForceLink.cc \ - layInit.cc + layInit.cc \ + layViewWidgetStack.cc RESOURCES = layBuildInMacros.qrc \ layHelpResources.qrc \ diff --git a/src/lay/lay/layControlWidgetStack.cc b/src/lay/lay/layControlWidgetStack.cc new file mode 100644 index 000000000..a4806323e --- /dev/null +++ b/src/lay/lay/layControlWidgetStack.cc @@ -0,0 +1,141 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "layControlWidgetStack.h" + +#include + +namespace lay +{ + +ControlWidgetStack::ControlWidgetStack(QWidget *parent, const char *name) + : QFrame (parent), mp_current_widget (0) +{ + setObjectName (QString::fromUtf8 (name)); + + // Background ist a simple label without a text currently + mp_bglabel = new QLabel (this); + mp_bglabel->setAutoFillBackground (true); + mp_bglabel->setAlignment (Qt::AlignVCenter | Qt::AlignHCenter); + mp_bglabel->show (); +} + +void ControlWidgetStack::focusInEvent(QFocusEvent *) +{ + for (size_t i = 0; i < m_widgets.size (); ++i) { + if (m_widgets [i]->isVisible ()) { + m_widgets [i]->setFocus (); + break; + } + } +} + +void ControlWidgetStack::add_widget(QWidget *w) +{ + m_widgets.push_back (w); + w->setParent (this); + resize_children (); + raise_widget (m_widgets.size () - 1); + + int mw = 0; + for (size_t i = 0; i < m_widgets.size (); ++i) { + mw = std::max (m_widgets [i]->sizeHint ().width (), mw); + mw = std::max (m_widgets [i]->minimumWidth (), mw); + } + + if (mw > minimumWidth ()) { + setMinimumWidth (mw); + resize (minimumWidth (), height ()); + } +} + +QSize ControlWidgetStack::sizeHint() const +{ + int w = 0; + for (size_t i = 0; i < m_widgets.size (); ++i) { + w = std::max (m_widgets [i]->sizeHint ().width (), w); + } + return QSize (w, 0); +} + +void ControlWidgetStack::remove_widget(size_t index) +{ + if (index < m_widgets.size ()) { + if (mp_current_widget == m_widgets [index]) { + mp_current_widget = 0; + } + m_widgets.erase (m_widgets.begin () + index); + } + if (m_widgets.size () == 0) { + mp_bglabel->show (); + } +} + +void ControlWidgetStack::raise_widget(size_t index) +{ + mp_current_widget = 0; + bool any_visible = false; + for (size_t i = 0; i < m_widgets.size (); ++i) { + if (m_widgets [i]) { + if (i == index) { + m_widgets [i]->show (); + mp_current_widget = m_widgets [i]; + any_visible = true; + } else { + m_widgets [i]->hide (); + } + } + } + + if (! any_visible) { + mp_bglabel->show (); + } else { + mp_bglabel->hide (); + } +} + +QWidget *ControlWidgetStack::widget(size_t index) +{ + if (index < m_widgets.size ()) { + return m_widgets [index]; + } else { + return 0; + } +} + +QWidget *ControlWidgetStack::background_widget() +{ + return mp_bglabel; +} + +void ControlWidgetStack::resize_children() +{ + // set the geometry of all children + for (std::vector ::iterator child = m_widgets.begin (); child != m_widgets.end (); ++child) { + if (*child) { + (*child)->setGeometry (0, 0, width (), height ()); + } + } + mp_bglabel->setGeometry (0, 0, width (), height ()); +} + +} diff --git a/src/lay/lay/layControlWidgetStack.h b/src/lay/lay/layControlWidgetStack.h new file mode 100644 index 000000000..29b436a68 --- /dev/null +++ b/src/lay/lay/layControlWidgetStack.h @@ -0,0 +1,77 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_layControlWidgetStack +#define HDR_layControlWidgetStack + +#include "layCommon.h" + +#include + +class QLabel; + +namespace lay +{ + +class ControlWidgetStack + : public QFrame +{ +public: + ControlWidgetStack (QWidget *parent = 0, const char *name = 0); + + void focusInEvent (QFocusEvent *); + + QSize sizeHint () const; + + void add_widget (QWidget *w); + void remove_widget (size_t index); + void raise_widget (size_t index); + QWidget *widget (size_t index); + QWidget *background_widget (); + + QWidget *currentWidget () const + { + return mp_current_widget; + } + + size_t count () const + { + return m_widgets.size (); + } + +protected: + virtual void resizeEvent (QResizeEvent *) + { + resize_children (); + } + + void resize_children (); + + std::vector m_widgets; + QWidget *mp_current_widget; + QLabel *mp_bglabel; +}; + +} + +#endif diff --git a/src/lay/lay/layHelpAboutDialog.cc b/src/lay/lay/layHelpAboutDialog.cc new file mode 100644 index 000000000..6430d43a1 --- /dev/null +++ b/src/lay/lay/layHelpAboutDialog.cc @@ -0,0 +1,140 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "layHelpAboutDialog.h" +#include "layApplication.h" +#include "layVersion.h" +#include "layHelpSource.h" // because of escape_xml +#include "layInit.h" +#include "dbInit.h" +#include "gsiInterpreter.h" + +#include "ui_HelpAboutDialog.h" + +#include + +namespace lay +{ + +// ------------------------------------------------------------ +// Implementation of the "help about" dialog + +HelpAboutDialog::HelpAboutDialog (QWidget *parent) + : QDialog (parent) +{ + mp_ui = new Ui::HelpAboutDialog (); + mp_ui->setupUi (this); + + std::vector build_options; + if (lay::ApplicationBase::instance ()->ruby_interpreter ().available ()) { + build_options.push_back (tl::to_string (tr ("Ruby interpreter ")) + lay::ApplicationBase::instance ()->ruby_interpreter ().version ()); + } + if (lay::ApplicationBase::instance ()->python_interpreter ().available ()) { + build_options.push_back (tl::to_string (tr ("Python interpreter ")) + lay::ApplicationBase::instance ()->python_interpreter ().version ()); + } +#if defined(HAVE_QTBINDINGS) + build_options.push_back (tl::to_string (tr ("Qt bindings for scripts"))); +#endif +#if defined(HAVE_64BIT_COORD) + build_options.push_back (tl::to_string (tr ("Wide coordinates (64 bit)"))); +#endif + + std::string s; + + s = ""; + + s += "

"; + s += escape_xml (std::string (lay::Version::name ()) + " " + lay::Version::version ()); + s += "

"; + + std::vector about_paras = tl::split (lay::Version::about_text (), "\n\n"); + for (std::vector::const_iterator p = about_paras.begin (); p != about_paras.end (); ++p) { + s += std::string ("

") + escape_xml (*p) + "

"; + } + + if (! build_options.empty ()) { + s += "

"; + s += "

"; + s += escape_xml (tl::to_string (QObject::tr ("Build options:"))); + s += "

    "; + for (std::vector::const_iterator bo = build_options.begin (); bo != build_options.end (); ++bo) { + s += "
  • "; + s += escape_xml (*bo); + s += "
  • "; + } + s += "
"; + } + + if (! lay::plugins ().empty () || ! db::plugins ().empty ()) { + + s += "

"; + s += "

"; + s += escape_xml (tl::to_string (QObject::tr ("Binary extensions:"))); + s += "

    "; + + for (std::list::const_iterator pd = lay::plugins ().begin (); pd != lay::plugins ().end (); ++pd) { + s += "
  • "; + if (! pd->description.empty ()) { + s += escape_xml (pd->description); + } else { + s += escape_xml (pd->path); + } + if (! pd->version.empty ()) { + s += " (" + escape_xml (pd->version) + ")"; + } + s += "
  • "; + } + + for (std::list::const_iterator pd = db::plugins ().begin (); pd != db::plugins ().end (); ++pd) { + s += "
  • "; + if (! pd->description.empty ()) { + s += escape_xml (pd->description); + } else { + s += escape_xml (pd->path); + } + if (! pd->version.empty ()) { + s += " (" + escape_xml (pd->version) + ")"; + } + s += "
  • "; + } + + s += "
"; + + } + + s += ""; + + std::string t = tl::to_string (QObject::tr ("About ")) + lay::Version::name (); + + setWindowTitle (tl::to_qstring (t)); + + mp_ui->main->setWordWrap (true); + mp_ui->main->setText (tl::to_qstring (s)); +} + +HelpAboutDialog::~HelpAboutDialog () +{ + delete mp_ui; + mp_ui = 0; +} + +} diff --git a/src/lay/lay/layHelpAboutDialog.h b/src/lay/lay/layHelpAboutDialog.h new file mode 100644 index 000000000..fc98fe98e --- /dev/null +++ b/src/lay/lay/layHelpAboutDialog.h @@ -0,0 +1,55 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_layHelpAboutDialog +#define HDR_layHelpAboutDialog + +#include "layCommon.h" + +#include + +namespace Ui { + class HelpAboutDialog; +} + +namespace lay +{ + +/** + * @brief A dialog for showing the "help about" dialog + */ +class LAY_PUBLIC HelpAboutDialog + : public QDialog +{ +public: + HelpAboutDialog (QWidget *parent); + ~HelpAboutDialog (); + +private: + Ui::HelpAboutDialog *mp_ui; +}; + +} + +#endif + diff --git a/src/lay/lay/layHelpDialog.cc b/src/lay/lay/layHelpDialog.cc index 36fd04864..8e337ee02 100644 --- a/src/lay/lay/layHelpDialog.cc +++ b/src/lay/lay/layHelpDialog.cc @@ -25,6 +25,7 @@ #include "layHelpSource.h" #include "layBrowserPanel.h" #include "tlStaticObjects.h" +#include "ui_HelpDialog.h" #include "tlString.h" @@ -42,35 +43,16 @@ HelpDialog::HelpDialog (QWidget *parent, bool modal) : QDialog (modal ? parent : 0 /*show as separate window*/, modal ? Qt::WindowFlags (0) : Qt::Window /*enabled minimize button*/), m_initialized (false) { + mp_ui = new Ui::HelpDialog (); + mp_ui->setupUi (this); + setModal (modal); - QVBoxLayout *help_layout = new QVBoxLayout (this); - setLayout (help_layout); - setWindowTitle (QObject::tr ("Assistant")); - mp_browser_panel = new lay::BrowserPanel (this); - help_layout->addWidget (mp_browser_panel); - - QSizePolicy sp = mp_browser_panel->sizePolicy (); - sp.setVerticalStretch (1); - mp_browser_panel->setSizePolicy (sp); - - if (modal) { - QFrame *button_frame = new QFrame (this); - help_layout->addWidget (button_frame); - QHBoxLayout *button_layout = new QHBoxLayout (button_frame); - button_layout->setMargin (0); - QPushButton *close_button = new QPushButton (button_frame); - button_layout->addStretch (1); - button_layout->addWidget (close_button); - close_button->setText (QObject::tr ("Close")); - close_button->setDefault (false); - close_button->setAutoDefault (false); - connect (close_button, SIGNAL (clicked ()), this, SLOT (accept ())); - } + mp_ui->button_frame->setVisible (modal); m_def_title = windowTitle (); - connect (mp_browser_panel, SIGNAL (title_changed (const QString &)), this, SLOT (title_changed (const QString &))); - connect (mp_browser_panel, SIGNAL (url_changed (const QString &)), this, SLOT (title_changed (const QString &))); + connect (mp_ui->browser_panel, SIGNAL (title_changed (const QString &)), this, SLOT (title_changed (const QString &))); + connect (mp_ui->browser_panel, SIGNAL (url_changed (const QString &)), this, SLOT (title_changed (const QString &))); } HelpDialog::~HelpDialog () @@ -82,14 +64,14 @@ void HelpDialog::title_changed (const QString &) { QString wt; - QString title = tl::to_qstring (mp_browser_panel->title ()); + QString title = tl::to_qstring (mp_ui->browser_panel->title ()); if (title.isNull () || title.size () == 0) { wt = m_def_title; } else { wt = m_def_title + QString::fromUtf8 (" - ") + title; } - QString url = tl::to_qstring (mp_browser_panel->url ()); + QString url = tl::to_qstring (mp_ui->browser_panel->url ()); if (! url.isNull () && url.size () > 0) { wt += QString::fromUtf8 (" [") + url + QString::fromUtf8 ("]"); } @@ -100,13 +82,13 @@ void HelpDialog::title_changed (const QString &) void HelpDialog::load (const std::string &url) { initialize (); - mp_browser_panel->load (url); + mp_ui->browser_panel->load (url); } void HelpDialog::search (const std::string &topic) { initialize (); - mp_browser_panel->search (topic); + mp_ui->browser_panel->search (topic); } void HelpDialog::showEvent (QShowEvent *) @@ -121,13 +103,13 @@ void HelpDialog::initialize () { if (! m_initialized) { m_initialized = true; - mp_browser_panel->set_search_url ("int:/search.xml", "string"); + mp_ui->browser_panel->set_search_url ("int:/search.xml", "string"); if (! mp_help_source) { mp_help_source = new lay::HelpSource (); tl::StaticObjects::reg (&mp_help_source); } - mp_browser_panel->set_source (mp_help_source); - mp_browser_panel->set_home ("int:/index.xml"); + mp_ui->browser_panel->set_source (mp_help_source); + mp_ui->browser_panel->set_home ("int:/index.xml"); } } diff --git a/src/lay/lay/layHelpDialog.h b/src/lay/lay/layHelpDialog.h index e24c09d2e..361aed565 100644 --- a/src/lay/lay/layHelpDialog.h +++ b/src/lay/lay/layHelpDialog.h @@ -31,6 +31,11 @@ #include #include +namespace Ui +{ + class HelpDialog; +} + namespace lay { @@ -58,7 +63,7 @@ protected slots: void title_changed (const QString &t); private: - lay::BrowserPanel *mp_browser_panel; + Ui::HelpDialog *mp_ui; QRect m_geometry; static lay::HelpSource *mp_help_source; QString m_def_title; diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 70156fe2c..87215e52c 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -69,9 +69,9 @@ #include "layConfig.h" #include "layMainWindow.h" #include "layHelpDialog.h" -#include "layHelpSource.h" // because of escape_xml #include "layNavigator.h" #include "layProgress.h" +#include "layProgressDialog.h" #include "layProgressWidget.h" #include "layStream.h" #include "layLayerControlPanel.h" // because of LabelWithBackground @@ -89,10 +89,12 @@ #include "laySaltController.h" #include "layTipDialog.h" #include "layMacroController.h" +#include "layHelpAboutDialog.h" +#include "layControlWidgetStack.h" +#include "layViewWidgetStack.h" #include "layInit.h" #include "antObject.h" #include "antService.h" -#include "ui_HelpAboutDialog.h" #include "gsi.h" #include "gsiInterpreter.h" #include "gtf.h" @@ -104,334 +106,6 @@ const int max_dirty_files = 15; // ------------------------------------------------------------- -class ProgressDialog - : public QDialog, - public tl::Object -{ -public: - ProgressDialog (QWidget *parent, lay::ProgressReporter *pr) - : QDialog (parent), mp_pr (pr) - { - QVBoxLayout *vbl = new QVBoxLayout (this); - vbl->setMargin (0); - vbl->setSpacing (0); - - mp_progress_widget = new ProgressWidget (pr, this, true); - mp_progress_widget->setObjectName (QString::fromUtf8 ("progress")); - vbl->addWidget (mp_progress_widget); - - setWindowTitle (QObject::tr ("Progress")); - setWindowModality (Qt::WindowModal); - } - - void closeEvent (QCloseEvent * /*event*/) - { - if (mp_pr) { - // NOTE: We don't kill on close for now. This creates a too easy way to scrap results. - // mp_pr->signal_break (); - // TODO: there should be a warning saying some jobs are pending. - } - } - - void set_progress (tl::Progress *progress) - { - mp_progress_widget->set_progress (progress); - } - - void add_widget (QWidget *widget) - { - mp_progress_widget->add_widget (widget); - } - - void remove_widget () - { - mp_progress_widget->remove_widget (); - } - - QWidget *get_widget () const - { - return mp_progress_widget->get_widget (); - } - -private: - lay::ProgressWidget *mp_progress_widget; - lay::ProgressReporter *mp_pr; -}; - -// ------------------------------------------------------------- - -class ControlWidgetStack - : public QFrame -{ -public: - ControlWidgetStack (QWidget *parent = 0, const char *name = 0) - : QFrame (parent), mp_current_widget (0) - { - setObjectName (QString::fromUtf8 (name)); - - // Background ist a simple label without a text currently - mp_bglabel = new QLabel (this); - mp_bglabel->setAutoFillBackground (true); - mp_bglabel->setAlignment (Qt::AlignVCenter | Qt::AlignHCenter); - mp_bglabel->show (); - } - - void focusInEvent (QFocusEvent *) - { - for (size_t i = 0; i < m_widgets.size (); ++i) { - if (m_widgets [i]->isVisible ()) { - m_widgets [i]->setFocus (); - break; - } - } - } - - void addWidget (QWidget *w) - { - m_widgets.push_back (w); - w->setParent (this); - resize_children (); - raiseWidget (m_widgets.size () - 1); - - int mw = 0; - for (size_t i = 0; i < m_widgets.size (); ++i) { - mw = std::max (m_widgets [i]->sizeHint ().width (), mw); - mw = std::max (m_widgets [i]->minimumWidth (), mw); - } - - if (mw > minimumWidth ()) { - setMinimumWidth (mw); - resize (minimumWidth (), height ()); - } - } - - QSize sizeHint () const - { - int w = 0; - for (size_t i = 0; i < m_widgets.size (); ++i) { - w = std::max (m_widgets [i]->sizeHint ().width (), w); - } - return QSize (w, 0); - } - - void removeWidget (size_t index) - { - if (index < m_widgets.size ()) { - if (mp_current_widget == m_widgets [index]) { - mp_current_widget = 0; - } - m_widgets.erase (m_widgets.begin () + index); - } - if (m_widgets.size () == 0) { - mp_bglabel->show (); - } - } - - QWidget *currentWidget () const - { - return mp_current_widget; - } - - void raiseWidget (size_t index) - { - mp_current_widget = 0; - bool any_visible = false; - for (size_t i = 0; i < m_widgets.size (); ++i) { - if (m_widgets [i]) { - if (i == index) { - m_widgets [i]->show (); - mp_current_widget = m_widgets [i]; - any_visible = true; - } else { - m_widgets [i]->hide (); - } - } - } - - if (! any_visible) { - mp_bglabel->show (); - } else { - mp_bglabel->hide (); - } - } - - QWidget *widget (size_t index) - { - if (index < m_widgets.size ()) { - return m_widgets [index]; - } else { - return 0; - } - } - - QWidget *background_widget () - { - return mp_bglabel; - } - - size_t count () const - { - return m_widgets.size (); - } - -protected: - virtual void resizeEvent (QResizeEvent *) - { - resize_children (); - } - - void resize_children () - { - // set the geometry of all children - for (std::vector ::iterator child = m_widgets.begin (); child != m_widgets.end (); ++child) { - if (*child) { - (*child)->setGeometry (0, 0, width (), height ()); - } - } - mp_bglabel->setGeometry (0, 0, width (), height ()); - } - - std::vector m_widgets; - QWidget *mp_current_widget; - QLabel *mp_bglabel; -}; - -// ------------------------------------------------------------- - -class ViewWidgetStack - : public QWidget -{ -public: - ViewWidgetStack (QWidget *parent = 0, const char *name = 0) - : QWidget (parent) - { - setObjectName (QString::fromUtf8 (name)); - - mp_bglabel = new QLabel (this); - mp_bglabel->setAutoFillBackground (true); - mp_bglabel->setText (QObject::tr ("

Use File/Open to open a layout

")); - mp_bglabel->setAlignment (Qt::AlignVCenter | Qt::AlignHCenter); - mp_bglabel->show (); - } - - void addWidget (LayoutView *w) - { - m_widgets.push_back (w); - w->setParent (this); - resize_children (); - raiseWidget (m_widgets.size () - 1); - - updateGeometry (); - } - - void removeWidget (size_t index) - { - if (index < m_widgets.size ()) { - m_widgets.erase (m_widgets.begin () + index); - } - if (m_widgets.size () == 0) { - mp_bglabel->show (); - } - } - - void raiseWidget (size_t index) - { - if (index < m_widgets.size ()) { - mp_bglabel->hide (); - m_widgets [index]->show (); - } else { - mp_bglabel->show (); - } - - size_t i = 0; - for (std::vector ::iterator child = m_widgets.begin (); child != m_widgets.end (); ++child, ++i) { - if (i != index) { - (*child)->hide (); - } - } - } - - LayoutView *widget (size_t index) - { - if (index < m_widgets.size ()) { - return m_widgets [index]; - } else { - return 0; - } - } - - QWidget *background_widget () - { - return mp_bglabel; - } - -protected: - virtual void resizeEvent (QResizeEvent *) - { - resize_children (); - } - - void resize_children () - { - // set the geometry of all children - for (std::vector ::iterator child = m_widgets.begin (); child != m_widgets.end (); ++child) { - (*child)->setGeometry (0, 0, width (), height ()); - } - mp_bglabel->setGeometry (0, 0, width (), height ()); - } - - std::vector m_widgets; - QLabel *mp_bglabel; -}; - -// ------------------------------------------------------------- - -TextProgressDelegate::TextProgressDelegate (MainWindow *mw, int verbosity) - : lay::TextProgress (verbosity), mp_mw (mw) -{ - // .. nothing yet .. -} - -void TextProgressDelegate::update_progress (tl::Progress *progress) -{ - if (!mp_mw->update_progress (progress)) { - lay::TextProgress::update_progress (progress); - } -} - -void TextProgressDelegate::show_progress_bar (bool show) -{ - if (!mp_mw->show_progress_bar (show)) { - lay::TextProgress::show_progress_bar (show); - } -} - -bool TextProgressDelegate::progress_wants_widget () const -{ - return mp_mw != 0 && mp_mw->progress_wants_widget (); -} - -void TextProgressDelegate::progress_add_widget (QWidget *widget) -{ - if (mp_mw) { - mp_mw->progress_add_widget (widget); - } -} - -QWidget *TextProgressDelegate::progress_get_widget () const -{ - return mp_mw ? mp_mw->progress_get_widget () : 0; -} - -void TextProgressDelegate::progress_remove_widget () -{ - if (mp_mw) { - mp_mw->progress_remove_widget (); - } -} - -// ------------------------------------------------------------- - static MainWindow *mw_instance = 0; MainWindow * @@ -1004,12 +678,12 @@ MainWindow::close_all () lay::LayoutView *view = mp_views.back (); mp_views.pop_back (); - mp_lp_stack->removeWidget (mp_views.size ()); - mp_hp_stack->removeWidget (mp_views.size ()); - mp_libs_stack->removeWidget (mp_views.size ()); - mp_eo_stack->removeWidget (mp_views.size ()); - mp_bm_stack->removeWidget (mp_views.size ()); - mp_view_stack->removeWidget (mp_views.size ()); + mp_lp_stack->remove_widget (mp_views.size ()); + mp_hp_stack->remove_widget (mp_views.size ()); + mp_libs_stack->remove_widget (mp_views.size ()); + mp_eo_stack->remove_widget (mp_views.size ()); + mp_bm_stack->remove_widget (mp_views.size ()); + mp_view_stack->remove_widget (mp_views.size ()); delete view; @@ -2698,12 +2372,12 @@ MainWindow::select_view (int index) current_view ()->zoom_box (box); } - mp_view_stack->raiseWidget (index); - mp_hp_stack->raiseWidget (index); - mp_lp_stack->raiseWidget (index); - mp_libs_stack->raiseWidget (index); - mp_eo_stack->raiseWidget (index); - mp_bm_stack->raiseWidget (index); + mp_view_stack->raise_widget (index); + mp_hp_stack->raise_widget (index); + mp_lp_stack->raise_widget (index); + mp_libs_stack->raise_widget (index); + mp_eo_stack->raise_widget (index); + mp_bm_stack->raise_widget (index); mp_setup_form->setup (); } @@ -2890,12 +2564,12 @@ MainWindow::clone_current_view () mp_layer_toolbox->set_view (current_view ()); - mp_view_stack->addWidget (view); - mp_lp_stack->addWidget (view->layer_control_frame ()); - mp_hp_stack->addWidget (view->hierarchy_control_frame ()); - mp_libs_stack->addWidget (view->libraries_frame ()); - mp_eo_stack->addWidget (view->editor_options_frame ()); - mp_bm_stack->addWidget (view->bookmarks_frame ()); + mp_view_stack->add_widget (view); + mp_lp_stack->add_widget (view->layer_control_frame ()); + mp_hp_stack->add_widget (view->hierarchy_control_frame ()); + mp_libs_stack->add_widget (view->libraries_frame ()); + mp_eo_stack->add_widget (view->editor_options_frame ()); + mp_bm_stack->add_widget (view->bookmarks_frame ()); bool f = m_disable_tab_selected; m_disable_tab_selected = true; @@ -3141,12 +2815,12 @@ MainWindow::close_view (int index) } mp_tab_bar->removeTab (index); - mp_view_stack->removeWidget (index); - mp_lp_stack->removeWidget (index); - mp_hp_stack->removeWidget (index); - mp_libs_stack->removeWidget (index); - mp_eo_stack->removeWidget (index); - mp_bm_stack->removeWidget (index); + mp_view_stack->remove_widget (index); + mp_lp_stack->remove_widget (index); + mp_hp_stack->remove_widget (index); + mp_libs_stack->remove_widget (index); + mp_eo_stack->remove_widget (index); + mp_bm_stack->remove_widget (index); view_closed_event (int (index)); @@ -3684,12 +3358,12 @@ MainWindow::create_view () mp_layer_toolbox->set_view (current_view ()); - mp_view_stack->addWidget (mp_views.back ()); - mp_lp_stack->addWidget (mp_views.back ()->layer_control_frame ()); - mp_hp_stack->addWidget (mp_views.back ()->hierarchy_control_frame ()); - mp_libs_stack->addWidget (mp_views.back ()->libraries_frame ()); - mp_eo_stack->addWidget (mp_views.back ()->editor_options_frame ()); - mp_bm_stack->addWidget (mp_views.back ()->bookmarks_frame ()); + mp_view_stack->add_widget (mp_views.back ()); + mp_lp_stack->add_widget (mp_views.back ()->layer_control_frame ()); + mp_hp_stack->add_widget (mp_views.back ()->hierarchy_control_frame ()); + mp_libs_stack->add_widget (mp_views.back ()->libraries_frame ()); + mp_eo_stack->add_widget (mp_views.back ()->editor_options_frame ()); + mp_bm_stack->add_widget (mp_views.back ()->bookmarks_frame ()); bool f = m_disable_tab_selected; m_disable_tab_selected = true; @@ -3748,12 +3422,12 @@ MainWindow::create_or_load_layout (const std::string *filename, const db::LoadLa mp_layer_toolbox->set_view (current_view ()); - mp_view_stack->addWidget (mp_views.back ()); - mp_lp_stack->addWidget (mp_views.back ()->layer_control_frame ()); - mp_hp_stack->addWidget (mp_views.back ()->hierarchy_control_frame ()); - mp_libs_stack->addWidget (mp_views.back ()->libraries_frame ()); - mp_eo_stack->addWidget (mp_views.back ()->editor_options_frame ()); - mp_bm_stack->addWidget (mp_views.back ()->bookmarks_frame ()); + mp_view_stack->add_widget (mp_views.back ()); + mp_lp_stack->add_widget (mp_views.back ()->layer_control_frame ()); + mp_hp_stack->add_widget (mp_views.back ()->hierarchy_control_frame ()); + mp_libs_stack->add_widget (mp_views.back ()->libraries_frame ()); + mp_eo_stack->add_widget (mp_views.back ()->editor_options_frame ()); + mp_bm_stack->add_widget (mp_views.back ()->bookmarks_frame ()); bool f = m_disable_tab_selected; m_disable_tab_selected = true; @@ -4475,108 +4149,6 @@ MainWindow::plugin_removed (lay::PluginDeclaration *cls) } } -// ------------------------------------------------------------ -// Implementation of the "help about" dialog - -HelpAboutDialog::HelpAboutDialog (QWidget *parent) - : QDialog (parent) -{ - mp_ui = new Ui::HelpAboutDialog (); - mp_ui->setupUi (this); - - std::vector build_options; - if (lay::ApplicationBase::instance ()->ruby_interpreter ().available ()) { - build_options.push_back (tl::to_string (tr ("Ruby interpreter ")) + lay::ApplicationBase::instance ()->ruby_interpreter ().version ()); - } - if (lay::ApplicationBase::instance ()->python_interpreter ().available ()) { - build_options.push_back (tl::to_string (tr ("Python interpreter ")) + lay::ApplicationBase::instance ()->python_interpreter ().version ()); - } -#if defined(HAVE_QTBINDINGS) - build_options.push_back (tl::to_string (tr ("Qt bindings for scripts"))); -#endif -#if defined(HAVE_64BIT_COORD) - build_options.push_back (tl::to_string (tr ("Wide coordinates (64 bit)"))); -#endif - - std::string s; - - s = ""; - - s += "

"; - s += escape_xml (std::string (lay::Version::name ()) + " " + lay::Version::version ()); - s += "

"; - - std::vector about_paras = tl::split (lay::Version::about_text (), "\n\n"); - for (std::vector::const_iterator p = about_paras.begin (); p != about_paras.end (); ++p) { - s += std::string ("

") + escape_xml (*p) + "

"; - } - - if (! build_options.empty ()) { - s += "

"; - s += "

"; - s += escape_xml (tl::to_string (QObject::tr ("Build options:"))); - s += "

    "; - for (std::vector::const_iterator bo = build_options.begin (); bo != build_options.end (); ++bo) { - s += "
  • "; - s += escape_xml (*bo); - s += "
  • "; - } - s += "
"; - } - - if (! lay::plugins ().empty () || ! db::plugins ().empty ()) { - - s += "

"; - s += "

"; - s += escape_xml (tl::to_string (QObject::tr ("Binary extensions:"))); - s += "

    "; - - for (std::list::const_iterator pd = lay::plugins ().begin (); pd != lay::plugins ().end (); ++pd) { - s += "
  • "; - if (! pd->description.empty ()) { - s += escape_xml (pd->description); - } else { - s += escape_xml (pd->path); - } - if (! pd->version.empty ()) { - s += " (" + escape_xml (pd->version) + ")"; - } - s += "
  • "; - } - - for (std::list::const_iterator pd = db::plugins ().begin (); pd != db::plugins ().end (); ++pd) { - s += "
  • "; - if (! pd->description.empty ()) { - s += escape_xml (pd->description); - } else { - s += escape_xml (pd->path); - } - if (! pd->version.empty ()) { - s += " (" + escape_xml (pd->version) + ")"; - } - s += "
  • "; - } - - s += "
"; - - } - - s += ""; - - std::string t = tl::to_string (QObject::tr ("About ")) + lay::Version::name (); - - setWindowTitle (tl::to_qstring (t)); - - mp_ui->main->setWordWrap (true); - mp_ui->main->setText (tl::to_qstring (s)); -} - -HelpAboutDialog::~HelpAboutDialog () -{ - delete mp_ui; - mp_ui = 0; -} - // ------------------------------------------------------------ // Declaration of the "plugin" for the menu entries diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index 5ef8ccddf..b8c8b9b97 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -20,8 +20,6 @@ */ - - #ifndef HDR_layMainWindow #define HDR_layMainWindow @@ -46,6 +44,7 @@ #include "layProgress.h" #include "layTextProgress.h" #include "layTechnology.h" +#include "layTextProgressDelegate.h" #include "tlException.h" #include "tlDeferredExecution.h" #include "tlObjectCollection.h" @@ -61,10 +60,6 @@ class QStackedWidget; class QDockWidget; class QAction; -namespace Ui { - class HelpAboutDialog; -} - namespace lay { class SettingsForm; @@ -84,38 +79,19 @@ class Navigator; class LayerToolbox; class MainWindow; class HelpDialog; +class HelpAboutDialog; +class ControlWidgetStack; +class ViewWidgetStack; +class ProgressWidget; /** - * @brief A dialog for showing the "help about" dialog + * @brief A big main window class + * + * The main window is the core UI feature of the application. + * The main window is view container, basic controller, configuration root + * and holder of many resources. + * The main window is a singleton. */ -class LAY_PUBLIC HelpAboutDialog - : public QDialog -{ -public: - HelpAboutDialog (QWidget *parent); - ~HelpAboutDialog (); - -private: - Ui::HelpAboutDialog *mp_ui; -}; - -class TextProgressDelegate - : public lay::TextProgress -{ -public: - TextProgressDelegate (MainWindow *mw, int verbosity); - - virtual void update_progress (tl::Progress *progress); - virtual void show_progress_bar (bool show); - virtual bool progress_wants_widget () const; - virtual void progress_add_widget (QWidget *widget); - virtual QWidget *progress_get_widget () const; - virtual void progress_remove_widget (); - -private: - MainWindow *mp_mw; -}; - class LAY_PUBLIC MainWindow : public QMainWindow, public lay::Dispatcher diff --git a/src/lay/lay/layProgressDialog.cc b/src/lay/lay/layProgressDialog.cc new file mode 100644 index 000000000..5d74fed4f --- /dev/null +++ b/src/lay/lay/layProgressDialog.cc @@ -0,0 +1,75 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "layProgressDialog.h" +#include "layProgressWidget.h" + +#include + +namespace lay +{ + +ProgressDialog::ProgressDialog (QWidget *parent, ProgressReporter *pr) + : QDialog (parent), mp_pr (pr) +{ + QVBoxLayout *vbl = new QVBoxLayout (this); + vbl->setMargin (0); + vbl->setSpacing (0); + + mp_progress_widget = new ProgressWidget (pr, this, true); + mp_progress_widget->setObjectName (QString::fromUtf8 ("progress")); + vbl->addWidget (mp_progress_widget); + + setWindowTitle (QObject::tr ("Progress")); + setWindowModality (Qt::WindowModal); +} + +void ProgressDialog::closeEvent (QCloseEvent *) +{ + if (mp_pr) { + // NOTE: We don't kill on close for now. This creates a too easy way to scrap results. + // mp_pr->signal_break (); + // TODO: there should be a warning saying some jobs are pending. + } +} + +void ProgressDialog::set_progress (tl::Progress *progress) +{ + mp_progress_widget->set_progress (progress); +} + +void ProgressDialog::add_widget (QWidget *widget) +{ + mp_progress_widget->add_widget (widget); +} + +void ProgressDialog::remove_widget () +{ + mp_progress_widget->remove_widget (); +} + +QWidget *ProgressDialog::get_widget () const +{ + return mp_progress_widget->get_widget (); +} + +} diff --git a/src/lay/lay/layProgressDialog.h b/src/lay/lay/layProgressDialog.h new file mode 100644 index 000000000..f1036fd2d --- /dev/null +++ b/src/lay/lay/layProgressDialog.h @@ -0,0 +1,61 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_layProgressDialog +#define HDR_layProgressDialog + +#include "layCommon.h" + +#include "tlObject.h" +#include "tlProgress.h" + +#include + +namespace lay +{ + +class ProgressReporter; +class ProgressWidget; + +class ProgressDialog + : public QDialog, + public tl::Object +{ +public: + ProgressDialog (QWidget *parent, lay::ProgressReporter *pr); + + void closeEvent (QCloseEvent * /*event*/); + + void set_progress (tl::Progress *progress); + void add_widget (QWidget *widget); + void remove_widget (); + QWidget *get_widget () const; + +private: + lay::ProgressWidget *mp_progress_widget; + lay::ProgressReporter *mp_pr; +}; + +} + +#endif diff --git a/src/lay/lay/layTextProgressDelegate.cc b/src/lay/lay/layTextProgressDelegate.cc new file mode 100644 index 000000000..d0dd0db2f --- /dev/null +++ b/src/lay/lay/layTextProgressDelegate.cc @@ -0,0 +1,73 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "layTextProgressDelegate.h" +#include "layMainWindow.h" + +namespace lay +{ + +TextProgressDelegate::TextProgressDelegate (MainWindow *mw, int verbosity) + : lay::TextProgress (verbosity), mp_mw (mw) +{ + // .. nothing yet .. +} + +void TextProgressDelegate::update_progress (tl::Progress *progress) +{ + if (!mp_mw->update_progress (progress)) { + lay::TextProgress::update_progress (progress); + } +} + +void TextProgressDelegate::show_progress_bar (bool show) +{ + if (!mp_mw->show_progress_bar (show)) { + lay::TextProgress::show_progress_bar (show); + } +} + +bool TextProgressDelegate::progress_wants_widget () const +{ + return mp_mw != 0 && mp_mw->progress_wants_widget (); +} + +void TextProgressDelegate::progress_add_widget (QWidget *widget) +{ + if (mp_mw) { + mp_mw->progress_add_widget (widget); + } +} + +QWidget *TextProgressDelegate::progress_get_widget () const +{ + return mp_mw ? mp_mw->progress_get_widget () : 0; +} + +void TextProgressDelegate::progress_remove_widget () +{ + if (mp_mw) { + mp_mw->progress_remove_widget (); + } +} + +} diff --git a/src/lay/lay/layTextProgressDelegate.h b/src/lay/lay/layTextProgressDelegate.h new file mode 100644 index 000000000..d45128861 --- /dev/null +++ b/src/lay/lay/layTextProgressDelegate.h @@ -0,0 +1,55 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_layTextProgressDelegate +#define HDR_layTextProgressDelegate + +#include "layCommon.h" + +#include "layTextProgress.h" + +namespace lay +{ + +class MainWindow; + +class TextProgressDelegate + : public lay::TextProgress +{ +public: + TextProgressDelegate (MainWindow *mw, int verbosity); + + virtual void update_progress (tl::Progress *progress); + virtual void show_progress_bar (bool show); + virtual bool progress_wants_widget () const; + virtual void progress_add_widget (QWidget *widget); + virtual QWidget *progress_get_widget () const; + virtual void progress_remove_widget (); + +private: + MainWindow *mp_mw; +}; + +} + +#endif diff --git a/src/lay/lay/layViewWidgetStack.cc b/src/lay/lay/layViewWidgetStack.cc new file mode 100644 index 000000000..7d7b51f81 --- /dev/null +++ b/src/lay/lay/layViewWidgetStack.cc @@ -0,0 +1,103 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "layViewWidgetStack.h" +#include "layLayoutView.h" + +#include + +namespace lay +{ + +ViewWidgetStack::ViewWidgetStack (QWidget *parent, const char *name) + : QWidget (parent) +{ + setObjectName (QString::fromUtf8 (name)); + + mp_bglabel = new QLabel (this); + mp_bglabel->setAutoFillBackground (true); + mp_bglabel->setText (QObject::tr ("

Use File/Open to open a layout

")); + mp_bglabel->setAlignment (Qt::AlignVCenter | Qt::AlignHCenter); + mp_bglabel->show (); +} + +void ViewWidgetStack::add_widget (LayoutView *w) +{ + m_widgets.push_back (w); + w->setParent (this); + resize_children (); + raise_widget (m_widgets.size () - 1); + + updateGeometry (); +} + +void ViewWidgetStack::remove_widget (size_t index) +{ + if (index < m_widgets.size ()) { + m_widgets.erase (m_widgets.begin () + index); + } + if (m_widgets.size () == 0) { + mp_bglabel->show (); + } +} + +void ViewWidgetStack::raise_widget (size_t index) +{ + if (index < m_widgets.size ()) { + mp_bglabel->hide (); + m_widgets [index]->show (); + } else { + mp_bglabel->show (); + } + + size_t i = 0; + for (std::vector ::iterator child = m_widgets.begin (); child != m_widgets.end (); ++child, ++i) { + if (i != index) { + (*child)->hide (); + } + } +} + +LayoutView *ViewWidgetStack::widget (size_t index) +{ + if (index < m_widgets.size ()) { + return m_widgets [index]; + } else { + return 0; + } +} + +QWidget *ViewWidgetStack::background_widget () +{ + return mp_bglabel; +} + +void ViewWidgetStack::resize_children () +{ + // set the geometry of all children + for (std::vector ::iterator child = m_widgets.begin (); child != m_widgets.end (); ++child) { + (*child)->setGeometry (0, 0, width (), height ()); + } + mp_bglabel->setGeometry (0, 0, width (), height ()); +} + +} diff --git a/src/lay/lay/layViewWidgetStack.h b/src/lay/lay/layViewWidgetStack.h new file mode 100644 index 000000000..c4e7b1c1e --- /dev/null +++ b/src/lay/lay/layViewWidgetStack.h @@ -0,0 +1,64 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_layViewWidgetStack +#define HDR_layViewWidgetStack + +#include "layCommon.h" + +#include + +class QLabel; + +namespace lay +{ + +class LayoutView; + +class ViewWidgetStack + : public QWidget +{ +public: + ViewWidgetStack (QWidget *parent = 0, const char *name = 0); + + void add_widget (LayoutView *w); + void remove_widget (size_t index); + void raise_widget (size_t index); + LayoutView *widget (size_t index); + QWidget *background_widget (); + +protected: + virtual void resizeEvent (QResizeEvent *) + { + resize_children (); + } + + void resize_children (); + + std::vector m_widgets; + QLabel *mp_bglabel; +}; + +} + +#endif From 97df81f996a9d90d54cd8870de030d361ea13010 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 2 Sep 2020 23:58:33 +0200 Subject: [PATCH 071/113] WIP: UI preparations for help browser in-page search and bookmarks --- src/lay/lay/HelpDialog.ui | 12 + src/laybasic/laybasic/BrowserPanel.ui | 466 +++++++++++++++++--------- 2 files changed, 313 insertions(+), 165 deletions(-) diff --git a/src/lay/lay/HelpDialog.ui b/src/lay/lay/HelpDialog.ui index f176091db..19c3f1e66 100644 --- a/src/lay/lay/HelpDialog.ui +++ b/src/lay/lay/HelpDialog.ui @@ -14,6 +14,18 @@ Assistant
+ + 9 + + + 4 + + + 9 + + + 9 + diff --git a/src/laybasic/laybasic/BrowserPanel.ui b/src/laybasic/laybasic/BrowserPanel.ui index af94c7c61..bcd83e6fc 100644 --- a/src/laybasic/laybasic/BrowserPanel.ui +++ b/src/laybasic/laybasic/BrowserPanel.ui @@ -29,6 +29,36 @@ 2 + + + + Forward + + + ... + + + + :/forward.png:/forward.png + + + + 24 + 24 + + + + true + + + + + + + TextLabel + + + @@ -45,6 +75,68 @@ + + + + Previous Topic + + + ... + + + + :/prev_topic.png:/prev_topic.png + + + + 24 + 24 + + + + true + + + + + + + Qt::Horizontal + + + QSizePolicy::MinimumExpanding + + + + 20 + 20 + + + + + + + + Home + + + ... + + + + :/home.png:/home.png + + + + 24 + 24 + + + + true + + + @@ -68,79 +160,6 @@ - - - - - 0 - 50 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Horizontal - - - - - 0 - 0 - - - - - 0 - 0 - - - - false - - - true - - - false - - - - 1 - - - - - - - 4 - 0 - - - - - - - - @@ -174,98 +193,6 @@ - - - - Home - - - ... - - - - :/home.png:/home.png - - - - 24 - 24 - - - - true - - - - - - - Previous Topic - - - ... - - - - :/prev_topic.png:/prev_topic.png - - - - 24 - 24 - - - - true - - - - - - - Forward - - - ... - - - - :/forward.png:/forward.png - - - - 24 - 24 - - - - true - - - - - - - Qt::Horizontal - - - QSizePolicy::MinimumExpanding - - - - 20 - 20 - - - - - - - - TextLabel - - - @@ -282,6 +209,215 @@ + + + + + + + :/find.png + + + + + + + + 0 + 50 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + + 0 + 0 + + + + + 0 + 0 + + + + false + + + true + + + false + + + + 1 + + + + + + + + + + + + 1 + 0 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + :/find.png + + + + + + + Find on page + + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + From 523f8e41e46cca7ed5b8b5e9588d13e470135cd3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 3 Sep 2020 23:38:55 +0200 Subject: [PATCH 072/113] WIP: help browser: on-page search, search box completer --- src/lay/lay/layHelpSource.cc | 15 + src/lay/lay/layHelpSource.h | 3 +- src/laybasic/laybasic/BrowserPanel.ui | 377 +++++++++++++---------- src/laybasic/laybasic/layBrowserPanel.cc | 119 ++++++- src/laybasic/laybasic/layBrowserPanel.h | 20 ++ 5 files changed, 359 insertions(+), 175 deletions(-) diff --git a/src/lay/lay/layHelpSource.cc b/src/lay/lay/layHelpSource.cc index e33569d8b..0b5edd062 100644 --- a/src/lay/lay/layHelpSource.cc +++ b/src/lay/lay/layHelpSource.cc @@ -554,6 +554,21 @@ HelpSource::get_outline (const std::string &u) return ol; } +void +HelpSource::search_completers (const std::string &string, std::list &completers) +{ + size_t n = 0; + const size_t max_completers = 100; + + // first produce all hits with match + for (std::vector ::const_iterator i = m_index.begin (); i < m_index.end () && n < max_completers; ++i) { + if (i->normalized_key.find (string) != std::string::npos) { + completers.push_back (i->key); + ++n; + } + } +} + std::string HelpSource::next_topic (const std::string &url) { diff --git a/src/lay/lay/layHelpSource.h b/src/lay/lay/layHelpSource.h index dc3af75c2..d355cd2b5 100644 --- a/src/lay/lay/layHelpSource.h +++ b/src/lay/lay/layHelpSource.h @@ -82,8 +82,9 @@ public: virtual QImage get_image (const std::string &url); virtual std::string get_css (const std::string &url); - virtual std::string next_topic (const std::string &url); + virtual void search_completers(const std::string &search_string, std::list &completers); + virtual std::string next_topic (const std::string &url); virtual std::string prev_topic (const std::string &url); QDomDocument get_dom (const std::string &u); diff --git a/src/laybasic/laybasic/BrowserPanel.ui b/src/laybasic/laybasic/BrowserPanel.ui index bcd83e6fc..ac0144c39 100644 --- a/src/laybasic/laybasic/BrowserPanel.ui +++ b/src/laybasic/laybasic/BrowserPanel.ui @@ -52,52 +52,6 @@
- - - - TextLabel - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 6 - 20 - - - - - - - - Previous Topic - - - ... - - - - :/prev_topic.png:/prev_topic.png - - - - 24 - 24 - - - - true - - - @@ -114,111 +68,6 @@ - - - - Home - - - ... - - - - :/home.png:/home.png - - - - 24 - 24 - - - - true - - - - - - - Next Topic - - - ... - - - - :/next_topic.png:/next_topic.png - - - - 24 - 24 - - - - true - - - - - - - - 0 - 0 - - - - - - - - Back - - - ... - - - - :/back.png:/back.png - - - - 24 - 24 - - - - true - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 6 - 20 - - - - - - - - - - - :/find.png - - - @@ -360,6 +209,9 @@ QFrame::Raised + + 2 + 0 @@ -372,25 +224,47 @@ 0 - - - - - - - :/find.png - - - - Find on page + Find on page - + + + ... + + + + :/find.png:/find.png + + + true + + + + + + + true + + + + + + + ... + + + + :/clear_edit.png:/clear_edit.png + + + true + + @@ -402,8 +276,15 @@ - - + + + + TextLabel + + + + + Qt::Horizontal @@ -412,13 +293,156 @@ - 5 + 6 20 + + + + Back + + + ... + + + + :/back.png:/back.png + + + + 24 + 24 + + + + true + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 6 + 20 + + + + + + + + Next Topic + + + ... + + + + :/next_topic.png:/next_topic.png + + + + 24 + 24 + + + + true + + + + + + + Previous Topic + + + ... + + + + :/prev_topic.png:/prev_topic.png + + + + 24 + 24 + + + + true + + + + + + + Home + + + ... + + + + :/home.png:/home.png + + + + 24 + 24 + + + + true + + + + + + + + 0 + 0 + + + + true + + + + + + + ... + + + + :/find.png:/find.png + + + true + + + + + + Find + + + Ctrl+F + + @@ -428,7 +452,7 @@ - searchEdit + search_edit back_pb forward_pb home_pb @@ -436,5 +460,22 @@ - + + + toolButton + clicked() + search_frame + hide() + + + 816 + 571 + + + 371 + 577 + + + + diff --git a/src/laybasic/laybasic/layBrowserPanel.cc b/src/laybasic/laybasic/layBrowserPanel.cc index 1da5393a7..22d1fa529 100644 --- a/src/laybasic/laybasic/layBrowserPanel.cc +++ b/src/laybasic/laybasic/layBrowserPanel.cc @@ -34,6 +34,9 @@ #endif #include +#include +#include +#include namespace lay { @@ -73,18 +76,35 @@ BrowserPanel::init () mp_ui->browser->set_panel (this); mp_ui->browser->setWordWrapMode (QTextOption::WordWrap); + mp_ui->browser->addAction (mp_ui->action_find); + connect (mp_ui->back_pb, SIGNAL (clicked ()), this, SLOT (back ())); connect (mp_ui->forward_pb, SIGNAL (clicked ()), this, SLOT (forward ())); connect (mp_ui->next_topic_pb, SIGNAL (clicked ()), this, SLOT (next ())); connect (mp_ui->prev_topic_pb, SIGNAL (clicked ()), this, SLOT (prev ())); connect (mp_ui->home_pb, SIGNAL (clicked ()), this, SLOT (home ())); - connect (mp_ui->searchEdit, SIGNAL (returnPressed ()), this, SLOT (search_edited ())); + connect (mp_ui->search_edit, SIGNAL (textEdited (const QString &)), this, SLOT (search_text_changed (const QString &))); + connect (mp_ui->search_edit, SIGNAL (returnPressed ()), this, SLOT (search_edited ())); + connect (mp_ui->search_button, SIGNAL (clicked ()), this, SLOT (search_edited ())); connect (mp_ui->browser, SIGNAL (textChanged ()), this, SLOT (text_changed ())); connect (mp_ui->browser, SIGNAL (backwardAvailable (bool)), mp_ui->back_pb, SLOT (setEnabled (bool))); connect (mp_ui->browser, SIGNAL (forwardAvailable (bool)), mp_ui->forward_pb, SLOT (setEnabled (bool))); connect (mp_ui->outline_tree, SIGNAL (itemActivated (QTreeWidgetItem *, int)), this, SLOT (outline_item_clicked (QTreeWidgetItem *))); + connect (mp_ui->action_find, SIGNAL (triggered ()), this, SLOT (find ())); + connect (mp_ui->on_page_search_edit, SIGNAL (textChanged (const QString &)), this, SLOT (page_search_edited ())); + connect (mp_ui->on_page_search_edit, SIGNAL (returnPressed ()), this, SLOT (page_search_next ())); + connect (mp_ui->on_page_search_next, SIGNAL (clicked ()), this, SLOT (page_search_next ())); - mp_ui->searchEdit->hide (); + mp_completer = new QCompleter (this); + mp_completer->setFilterMode (Qt::MatchStartsWith); + mp_completer->setCaseSensitivity (Qt::CaseInsensitive); + mp_completer->setCompletionMode (QCompleter::UnfilteredPopupCompletion); + mp_completer_model = new QStringListModel (mp_completer); + mp_completer->setModel (mp_completer_model); + mp_ui->search_edit->setCompleter (mp_completer); + + mp_ui->search_frame->hide (); + mp_ui->search_edit->hide (); set_label (std::string ()); } @@ -110,6 +130,87 @@ BrowserPanel::url () const return m_cached_url; } +void +BrowserPanel::find () +{ + mp_ui->search_frame->show (); + mp_ui->on_page_search_edit->setFocus(); +} + +void +BrowserPanel::page_search_edited () +{ + m_search_selection.clear (); + m_search_index = -1; + + if (mp_ui->on_page_search_edit->text ().size () < 2) { + mp_ui->browser->setExtraSelections (m_search_selection); + return; + } + + QString search_text = mp_ui->on_page_search_edit->text (); + + QTextDocument *doc = mp_ui->browser->document (); + for (QTextBlock b = doc->firstBlock (); b.isValid (); b = b.next ()) { + + int from = 0; + int index; + + QString t = b.text (); + + while ((index = t.indexOf (search_text, from, Qt::CaseInsensitive)) >= 0) { + + QTextCursor highlight (b); + highlight.movePosition (QTextCursor::NextCharacter, QTextCursor::MoveAnchor, index); + highlight.movePosition (QTextCursor::NextCharacter, QTextCursor::KeepAnchor, search_text.size ()); + + QTextEdit::ExtraSelection extra_selection; + extra_selection.cursor = highlight; + extra_selection.format.setBackground (QColor (255, 255, 160)); + m_search_selection.push_back (extra_selection); + + from = index + search_text.size (); + + } + + } + + if (! m_search_selection.empty ()) { + m_search_index = 0; + mp_ui->browser->setExtraSelections (m_search_selection); + mp_ui->browser->setTextCursor (m_search_selection [m_search_index].cursor); + } +} + +void +BrowserPanel::page_search_next () +{ + if (m_search_index >= 0) { + + ++m_search_index; + if (m_search_index >= m_search_selection.size ()) { + m_search_index = 0; + } + + mp_ui->browser->setTextCursor (m_search_selection [m_search_index].cursor); + + } +} + +void +BrowserPanel::search_text_changed (const QString &text) +{ + QList strings; + if (! text.isEmpty () && mp_source.get ()) { + std::list cl; + mp_source->search_completers (tl::to_string (text.toLower ()), cl); + for (std::list::const_iterator i = cl.begin (); i != cl.end (); ++i) { + strings.push_back (tl::to_qstring (*i)); + } + } + mp_completer_model->setStringList (strings); +} + void BrowserPanel::text_changed () { @@ -259,15 +360,15 @@ BrowserPanel::search (const std::string &s) void BrowserPanel::search_edited () { - if (mp_ui->searchEdit->text ().size () > 0) { + if (mp_ui->search_edit->text ().size () > 0) { QUrl url (tl::to_qstring (m_search_url)); #if QT_VERSION >= 0x050000 QUrlQuery qi; - qi.addQueryItem (tl::to_qstring (m_search_query_item), mp_ui->searchEdit->text ()); + qi.addQueryItem (tl::to_qstring (m_search_query_item), mp_ui->search_edit->text ()); url.setQuery (qi); #else QList > qi; - qi.push_back (QPair (tl::to_qstring (m_search_query_item), mp_ui->searchEdit->text ())); + qi.push_back (QPair (tl::to_qstring (m_search_query_item), mp_ui->search_edit->text ())); url.setQueryItems (qi); #endif load (url.toEncoded ().constData ()); @@ -279,7 +380,7 @@ BrowserPanel::set_search_url (const std::string &url, const std::string &query_i { m_search_url = url; m_search_query_item = query_item; - mp_ui->searchEdit->setVisible (! url.empty ()); + mp_ui->search_edit->setVisible (! url.empty ()); } void @@ -474,6 +575,12 @@ BrowserSource::get_outline (const std::string & /*url*/) return BrowserOutline (); } +void +BrowserSource::search_completers (const std::string & /*search_string*/, std::list & /*completers*/) +{ + // .. nothing here .. +} + std::string BrowserSource::get (const std::string & /*url*/) { diff --git a/src/laybasic/laybasic/layBrowserPanel.h b/src/laybasic/laybasic/layBrowserPanel.h index b974de658..3c02123a4 100644 --- a/src/laybasic/laybasic/layBrowserPanel.h +++ b/src/laybasic/laybasic/layBrowserPanel.h @@ -30,12 +30,15 @@ #include "gsiObject.h" #include +#include #include #include #include class QTreeWidgetItem; +class QCompleter; +class QStringListModel; namespace Ui { @@ -204,6 +207,11 @@ public: */ virtual BrowserOutline get_outline (const std::string &url); + /** + * @brief Gets the search completer items for a given search string + */ + virtual void search_completers (const std::string &search_string, std::list &completers); + /** * @brief Get the image for a given "int" URL in an image */ @@ -385,7 +393,15 @@ public slots: */ void home (); + /** + * @brief "find" activated + */ + void find (); + protected slots: + void page_search_edited (); + void page_search_next(); + void search_text_changed(const QString &text); void search_edited (); void text_changed (); void outline_item_clicked (QTreeWidgetItem *item); @@ -408,6 +424,10 @@ private: tl::DeferredMethod m_back_dm; std::string m_search_url, m_search_query_item; QString m_current_title; + QList m_search_selection; + int m_search_index; + QCompleter *mp_completer; + QStringListModel *mp_completer_model; void init (); }; From fd6e126038cf8fb8b4e30d02bead1238eaa63115 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 3 Sep 2020 23:54:58 +0200 Subject: [PATCH 073/113] WIP: some refinement of help dialog search function. --- src/laybasic/laybasic/BrowserPanel.ui | 4 ++-- src/laybasic/laybasic/layBrowserPanel.cc | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/laybasic/laybasic/BrowserPanel.ui b/src/laybasic/laybasic/BrowserPanel.ui index ac0144c39..38a767f04 100644 --- a/src/laybasic/laybasic/BrowserPanel.ui +++ b/src/laybasic/laybasic/BrowserPanel.ui @@ -253,7 +253,7 @@ - + ... @@ -462,7 +462,7 @@
- toolButton + search_close_button clicked() search_frame hide() diff --git a/src/laybasic/laybasic/layBrowserPanel.cc b/src/laybasic/laybasic/layBrowserPanel.cc index 22d1fa529..243227a89 100644 --- a/src/laybasic/laybasic/layBrowserPanel.cc +++ b/src/laybasic/laybasic/layBrowserPanel.cc @@ -92,6 +92,7 @@ BrowserPanel::init () connect (mp_ui->outline_tree, SIGNAL (itemActivated (QTreeWidgetItem *, int)), this, SLOT (outline_item_clicked (QTreeWidgetItem *))); connect (mp_ui->action_find, SIGNAL (triggered ()), this, SLOT (find ())); connect (mp_ui->on_page_search_edit, SIGNAL (textChanged (const QString &)), this, SLOT (page_search_edited ())); + connect (mp_ui->search_close_button, SIGNAL (clicked ()), this, SLOT (page_search_edited ()), Qt::QueuedConnection); connect (mp_ui->on_page_search_edit, SIGNAL (returnPressed ()), this, SLOT (page_search_next ())); connect (mp_ui->on_page_search_next, SIGNAL (clicked ()), this, SLOT (page_search_next ())); @@ -143,7 +144,7 @@ BrowserPanel::page_search_edited () m_search_selection.clear (); m_search_index = -1; - if (mp_ui->on_page_search_edit->text ().size () < 2) { + if (! mp_ui->search_frame->isVisible () || mp_ui->on_page_search_edit->text ().size () < 2) { mp_ui->browser->setExtraSelections (m_search_selection); return; } @@ -219,6 +220,9 @@ BrowserPanel::text_changed () m_current_title = title; emit title_changed (title); } + + // refresh on-page search + page_search_edited (); } void From 842501c9e81c246a5f1b6c89b40c03afdb767e07 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 4 Sep 2020 17:43:56 +0200 Subject: [PATCH 074/113] WIP: some updates, specifically: width of help browser does not change when the window is resized -> good for bookmarks, history ... --- src/laybasic/laybasic/BrowserPanel.ui | 19 +++++- src/laybasic/laybasic/layBrowserPanel.cc | 86 +++++++++++++++++++++++- src/laybasic/laybasic/layBrowserPanel.h | 27 ++++++++ 3 files changed, 130 insertions(+), 2 deletions(-) diff --git a/src/laybasic/laybasic/BrowserPanel.ui b/src/laybasic/laybasic/BrowserPanel.ui index 38a767f04..dd3c85b39 100644 --- a/src/laybasic/laybasic/BrowserPanel.ui +++ b/src/laybasic/laybasic/BrowserPanel.ui @@ -159,7 +159,16 @@ - + + + false + + + + Bookmarks + + + @@ -443,6 +452,14 @@ Ctrl+F + + + Bookmark + + + Ctrl+B + + diff --git a/src/laybasic/laybasic/layBrowserPanel.cc b/src/laybasic/laybasic/layBrowserPanel.cc index 243227a89..fc7855867 100644 --- a/src/laybasic/laybasic/layBrowserPanel.cc +++ b/src/laybasic/laybasic/layBrowserPanel.cc @@ -37,6 +37,7 @@ #include #include #include +#include namespace lay { @@ -75,8 +76,13 @@ BrowserPanel::init () mp_ui->browser->setReadOnly (true); mp_ui->browser->set_panel (this); mp_ui->browser->setWordWrapMode (QTextOption::WordWrap); + mp_ui->browser->setLineWrapMode (QTextEdit::FixedPixelWidth); + QFontMetrics fm (font ()); + int text_width = fm.boundingRect ('m').width () * 80; + mp_ui->browser->setLineWrapColumnOrWidth (text_width); mp_ui->browser->addAction (mp_ui->action_find); + mp_ui->browser->addAction (mp_ui->action_bookmark); connect (mp_ui->back_pb, SIGNAL (clicked ()), this, SLOT (back ())); connect (mp_ui->forward_pb, SIGNAL (clicked ()), this, SLOT (forward ())); @@ -90,11 +96,13 @@ BrowserPanel::init () connect (mp_ui->browser, SIGNAL (backwardAvailable (bool)), mp_ui->back_pb, SLOT (setEnabled (bool))); connect (mp_ui->browser, SIGNAL (forwardAvailable (bool)), mp_ui->forward_pb, SLOT (setEnabled (bool))); connect (mp_ui->outline_tree, SIGNAL (itemActivated (QTreeWidgetItem *, int)), this, SLOT (outline_item_clicked (QTreeWidgetItem *))); - connect (mp_ui->action_find, SIGNAL (triggered ()), this, SLOT (find ())); connect (mp_ui->on_page_search_edit, SIGNAL (textChanged (const QString &)), this, SLOT (page_search_edited ())); connect (mp_ui->search_close_button, SIGNAL (clicked ()), this, SLOT (page_search_edited ()), Qt::QueuedConnection); connect (mp_ui->on_page_search_edit, SIGNAL (returnPressed ()), this, SLOT (page_search_next ())); connect (mp_ui->on_page_search_next, SIGNAL (clicked ()), this, SLOT (page_search_next ())); + connect (mp_ui->action_find, SIGNAL (triggered ()), this, SLOT (find ())); + connect (mp_ui->action_bookmark, SIGNAL (triggered ()), this, SLOT (bookmark ())); + connect (mp_ui->browser_bookmark_view, SIGNAL (itemDoubleClicked (QTreeWidgetItem *, int)), this, SLOT (bookmark_item_selected (QTreeWidgetItem *))); mp_completer = new QCompleter (this); mp_completer->setFilterMode (Qt::MatchStartsWith); @@ -108,6 +116,10 @@ BrowserPanel::init () mp_ui->search_edit->hide (); set_label (std::string ()); + + // TODO: load bookmarks ... + + refresh_bookmark_list (); } BrowserPanel::~BrowserPanel () @@ -131,6 +143,78 @@ BrowserPanel::url () const return m_cached_url; } +void +BrowserPanel::bookmark () +{ + BookmarkItem bm; + bm.url = tl::to_string (mp_ui->browser->historyUrl (0).toString ()); + QString title = mp_ui->browser->document ()->metaInformation (QTextDocument::DocumentTitle); + bm.title = tl::to_string (title); + bm.position = mp_ui->browser->verticalScrollBar ()->value (); + + add_bookmark (bm); + refresh_bookmark_list (); +} + +void +BrowserPanel::bookmark_item_selected (QTreeWidgetItem *item) +{ + int index = mp_ui->browser_bookmark_view->indexOfTopLevelItem (item); + if (index < 0 || index >= int (m_bookmarks.size ())) { + return; + } + + std::list::iterator i = m_bookmarks.begin (); + for ( ; i != m_bookmarks.end () && index > 0; --index, ++i) + ; + + if (i == m_bookmarks.end ()) { + return; + } + + BookmarkItem bm = *i; + m_bookmarks.erase (i); + m_bookmarks.push_front (bm); + + refresh_bookmark_list (); + load (bm.url); + + mp_ui->browser->verticalScrollBar ()->setValue (bm.position); + mp_ui->browser_bookmark_view->topLevelItem (0)->setSelected (true); +} + +void +BrowserPanel::clear_bookmarks () +{ + m_bookmarks.clear (); +} + +void +BrowserPanel::add_bookmark (const BookmarkItem &item) +{ + for (std::list::iterator i = m_bookmarks.begin (); i != m_bookmarks.end (); ) { + std::list::iterator ii = i; + ++ii; + if (*i == item) { + m_bookmarks.erase (i); + } + i = ii; + } + m_bookmarks.push_front (item); +} + +void +BrowserPanel::refresh_bookmark_list () +{ + mp_ui->browser_bookmark_view->setVisible (! m_bookmarks.empty ()); + + mp_ui->browser_bookmark_view->clear (); + for (std::list::const_iterator i = m_bookmarks.begin (); i != m_bookmarks.end (); ++i) { + QTreeWidgetItem *item = new QTreeWidgetItem (mp_ui->browser_bookmark_view); + item->setData (0, Qt::DisplayRole, tl::to_qstring (i->title)); + } +} + void BrowserPanel::find () { diff --git a/src/laybasic/laybasic/layBrowserPanel.h b/src/laybasic/laybasic/layBrowserPanel.h index 3c02123a4..dd6bb194b 100644 --- a/src/laybasic/laybasic/layBrowserPanel.h +++ b/src/laybasic/laybasic/layBrowserPanel.h @@ -281,6 +281,23 @@ private: BrowserPanel *mp_panel; }; +/** + * @brief A structure describing a bookmark item + */ +struct LAYBASIC_PUBLIC BookmarkItem +{ + BookmarkItem () : position (0) { } + + bool operator== (const BookmarkItem &other) const + { + return url == other.url && position == other.position; + } + + std::string url; + std::string title; + int position; +}; + /** * @brief A specialisation of QWidget around a TextBrowser that allows loading a specific resource */ @@ -398,6 +415,11 @@ public slots: */ void find (); + /** + * @brief "bookmark" activated + */ + void bookmark(); + protected slots: void page_search_edited (); void page_search_next(); @@ -405,6 +427,7 @@ protected slots: void search_edited (); void text_changed (); void outline_item_clicked (QTreeWidgetItem *item); + void bookmark_item_selected (QTreeWidgetItem *item); protected: virtual QVariant loadResource (int type, const QUrl &url); @@ -428,8 +451,12 @@ private: int m_search_index; QCompleter *mp_completer; QStringListModel *mp_completer_model; + std::list m_bookmarks; void init (); + void clear_bookmarks (); + void add_bookmark (const BookmarkItem &item); + void refresh_bookmark_list (); }; } From 6bfed45b5d78c05721a57e76b307f635e95b5113 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 4 Sep 2020 18:23:26 +0200 Subject: [PATCH 075/113] WIP: nicer icons, added bookmark button. --- src/lay/lay/images/back.png | Bin 659 -> 722 bytes src/lay/lay/images/back_16.png | Bin 450 -> 578 bytes src/lay/lay/images/bookmark.png | Bin 0 -> 1046 bytes src/lay/lay/images/bookmark_16.png | Bin 0 -> 704 bytes src/lay/lay/images/forward.png | Bin 708 -> 711 bytes src/lay/lay/images/forward_16.png | Bin 445 -> 572 bytes src/lay/lay/images/home.png | Bin 703 -> 767 bytes src/lay/lay/images/next_topic.png | Bin 528 -> 776 bytes src/lay/lay/images/prev_topic.png | Bin 585 -> 797 bytes src/lay/lay/layResources.qrc | 2 + src/laybasic/laybasic/BrowserPanel.ui | 255 +++++++++++++---------- src/laybasic/laybasic/layBrowserPanel.cc | 2 + 12 files changed, 151 insertions(+), 108 deletions(-) create mode 100644 src/lay/lay/images/bookmark.png create mode 100644 src/lay/lay/images/bookmark_16.png diff --git a/src/lay/lay/images/back.png b/src/lay/lay/images/back.png index bfd1a9f3a8fa6c83058b4a68800250b8453fc06b..f106b46a52d1843ec2b7391540494122882632d4 100644 GIT binary patch delta 638 zcmV-^0)hRL1=0nOI|~FW000Cl0RlLZ_mM^@e@{t7K~zYI)s?|((@-48zfHPyO%Y8P z8^t0Mf|nrDBwfdbu#*4EaiFgaX5 ze{f3aOUH3$IF7p(HZPPwRaJp8_QEg>V|I479TpA%(Ow%AMTyC>yqd{m#)`#ayi_V3 zGR9Jxrs>^sYPA{(B~TP4mP{rq*=#mbC=}ux$00d9#9vBXoCBkCU=&~!00n3V0LA9F zJqbX>V??^aRaF%vNqTM=#)N5_37+Qxf42oVyTC#7=*ww10(%d+YW=cL5>9ZM9m{^?Lo)eD3}}?X-_Rf5HEN z`#K6L>uh*^D^SOI1Hjn_M%cp_|3Ovd7 z_Rj6~cOTdC`P@De&H*D=`-TH2OfqnMRg4T(#;**I6&CJ_+};-~m7jK=)OMPnG%#m# zmr6Y+*^5S_;Q+Y#hSJrAhmXdJXm`@_uRnfvKHKhN082f~@6~}9s;UY@=Mxn@#g7k* zj4!`;9}q%HVRA$L4=l?%j0obbjm`GPvkz|1E52_50932h11^@h5sOBaDWz}wk(}-L Y4fj}wqO_Uk!~g&Q07*qoM6N<$f)}MQV*mgE delta 575 zcmV-F0>J&!1(OAkI|~Eo000B%0kwNlYmr7Me-KGTK~zYIwU*Cn6Hye0f2T9kG%4ho zU1`A*U%(<*tkNWtKMRqH+H~V8E&_rJ1$S=TiavpFkTjExMN+%aG@%wZK7n-SO4QDj zOeZ9hVReZox6r_O_`Tq=AmF@iyZiSRaFDvf5u2Sy1I|$6A+OSkH>d1nanLs(_C;5I7sFR z*#nn_5IgB~dfgBA?lwRcRDrYecd1lryxD^cdRlv-BUDr37`|Wn|;Um-T9RC-5WQ&|iHkt>A2gUMpf73RP zU46b7e5Bw^q8pt%pFV#*+GZzda%0z?hc|IG>97xkm0;Nz;<|7|BZzj!tb&H=AmMs8p0IUR!bNT-g3?)&eSzKK( zA7>Z40G@@(3oURY5fPR5=ad2+jZi diff --git a/src/lay/lay/images/back_16.png b/src/lay/lay/images/back_16.png index b7d8e2918f673156793685ac02a81e77580908a8..b5c926f5006b73d4e516d6144a644a2b3e69d535 100644 GIT binary patch delta 493 zcmVI8hqmE|?HP ze{I`-K?s?T(lk{>tttV*M;yP0=$8P+Xev8 z>-8?L`hETF`;W(a-}YVP7N@%oZo}gY!X}!gExq~hiTc;i9u}V{((BLPN9UJ(SBOb& zFo%JBlR^9-&4`Dq%Zl>l;M6e(LF^z!8UX)CIVIB3pWv||i1|*Za}uSC{R`c0*We>h jpAmz1oO2^icklWOb{XM@FVOum00000NkvXXu0mjfx})c@ delta 364 zcmV-y0h9j11i}N5I|~EY000Bn0d7ZQHIYUre?Lh?K~y-6rIRsB!$25@-%DGm>0L1u zY9TlZx=F}Xg)C0~0{sKJ32rV5F5=|mq7FJabk#xkB>$q|B1Y3HlFNk-dJZNfg`~0d zg%7^ro`>)F?tt;B09Y+JlbaG_><)mO239Dg9m}%#Gy=jHyEaWTmh&U9VhbzN80G!2OQfQT#Vjr3&W6aYv$fmKyKX|-BAwr#7LHV24a zCOGWllw;fbTCr_~Sv*2L_)*fdT+Rh-1(5&ez)Q z@cp?PkMI9ZBK*-l1rG%P5Jk~}_xg6ZR=&9{K3<-(C4-=NjZCdx8Ow{DKORZ>uwS^w ztdM$b=EhuF2>I>SqBlM) zArQ(OcVTh9Y_o)n7ax2uW*PAXUwzX=T`VNd^`Wu1nk@#G@M0DcOlCJJ&H;u{$Ji`F zZ*OVuy*>=ut_8t_M1RT2Ip^m0`+m;-{m$jf;=X^fX@la3{k5*^;=TgX>GWGKG)nyGqbiQn+jW#X{IVC=Sc9U-7=GO^3Jmp?rfI@%@yu=xGLH|> z_H}x2a~$AhfRoYL?5D2ZMcvO7SvWn=KUZ*YCueQUxURe4IL%#=;j$5=NY&0Gu0nrZ#J`_10(k6ptlfwOV2g|D)_R@pD%=GdD zdv0!C1<-34MuGtVUDs91vIe{y-pm)#S?_W39S4ivwkHc*bDaZ{cOQvw{sDLejG&<5 zfFBJdsGo-DmlM~n+hb#6zvXhdA4(}I7A=heK6$&9!fUM!kTOY6If=p};gWb60#14C z`>ok|L?V%_Wm(4z!!S!lj>U{`INH5%CFz(rcob+K0w@9y_Hp6LQGzJ|ui(#Y5u>A{ zre#?JhGEQYosZsH+@6`8JQRFx{pydmwKtA5vSRBA;NDGuR{_DdIjiNlVr?@LKDcl( zo5^HGW3kv5l{KX+H#4c(8_x!t7O&n&9qXuHW0j|ZfSs>^_-Y}7ip$YhacXL6G8&Bz zR$rQ$K8VHRU*>H0)Vt%ivdLd&tMXn>NomiBkR*wrC~6UayDIAUq#bl zuxhygDVI+W1gNT-_j)UfNi z&6=h`k|bFWgbo0aJpm{J^^Z&*?|mFy|9$EHLo;U$B@&76DqJoBxYXUGD%ZfS~F5A8c!yZPh0(?TN0Xbd5>M~G86r-APCmK zG6;o24o90~GB$D8Fv!8l7^$gN2Bg{JK;OPglV^n!z0wldzne8KtW^7Y_};SD>$UPc zPy79TF_+7!K*J0ok%&c>eeq36ENpJoz857!S(GeB zlj$>nUJ3xl!>1l?snpTEPIc>uOSK2O6}pFW0xSpEU4WefSXUiS&CXRanapB59uFHL zkqA+1lHqtir(F;R+Q`a1z-$Msoeig>ju#$3Q!|;&dZkjC005H9<#gYG|IXT%&A^68 zyAKYXG?L0Ufb9lsUBB*a{C2&FYtu7jMNx*5$>i7i7Ut*YtG)w+XO>=lh+mm~AeKox zU}@-J8^3&=)ih0;OC%B>cdRiMi)r!usq=+mbt5k_JKHz0;CY^A80OHQa-dT*MHqWW zUHHCGMp-oi06ZQKWLfTS6c~QbfTqL3I6ARb0&d*AExudL%c`o{0)ap+7!2A7AqGJZ z+^JM5-_$_Wz}$McEWcR!RMmA{C4|h#vTRGI(_`6e_M#+7J^*e2`DP)+SakGBbaeFk m$jC^0Gk$n@cvn;5zt&G7bJT(e;-Ve^0000mLG?`M$BuTQ)?00}( z8|W!OuMIn^h_5!`L2O1o9m9$a56bvNC zJu?%r(dU#>ud_h!SbpukX!zN&li>J_C2GWU#}?ims$y?U&4UGF5KGJIyDh!ie`qR~ zWm)dq3;qNjoS(V^79K~jP*T@7jcTK*%nz>#v%oF<%ndMa2nDgIuGMv^(QKLE);(|o zpPB>HH55xr>RMfwh%aaf2P{@HpNQ^&835bG&+G4TZ905(;ryh>2v6YN<5hKib9bZB zY&mWf2YwM_EM4FFW|zCr*ya|EU2WMTKKCrGpLw=f29GI?v7<0l3l{HJ6Dv(zyJfm!v@chOe<>W5W-=)YeygTt1*k(Pbtk=ud&FO$+`l1?NQy1P?in>wve>`{R zY|FsKd#W+Ftj+6&eV&=^<^rEOH;;8VmVu%Bsxh-voYxKeoEPq2qQG$NKm1Ga{TjT| zzC-C}V;Gwri}Z~!3h2Zx9_)z_ne9e}HD2fJgnimRu0Y#txzjyRTq;R)3& zsKo`%v|C;kjXOB-b>(}rwPPzkT8lRwOTC+^xYRM>lHlg%7XA72XFd-P&j~|ALs4An7#SEC7|hMh`TqR*^uC{C3S6J^PY@n`+} zfBB`%nym+p8yg#2q8q{jV+aWe{hTyuk|12>_jd+{Z?6~_zP@6}4Y6Wl`u0YC_5MpI zjE#*gkPX2e21uG2zP@H)`1+b5+e4W(#7t6-hn?w!nVFd|ZUg?HYkmdCIkvp4fjaDR z987QQ22s z`u+Ipum4yR5+k|*449djDgOKS_bw+Z01ib^0I|~EY000Bn0d7ZQHIYUre>zD-K~y-6rIWEr!%!54|JVv`n%szS zad4MTlBF~hYahS|aH+eCxD^K#pTR|(bm`!#px~0cNFg{F;?N}bT-v3_p`@j)O{M;D zxc_kf@4&t10Qouqu({$+uGq4yR{({z0nKKUOw)V@u(1|EDaG}AorDnGJP0o&<4FN6)> z_jT8G+d&ZYiRf|`prWPW%x_sXJkP7tYPItyiUv~3+bLks&kaxfh@oj3vIm_40ALvB zgc)(1;^??dVHn;K(ajY8{aNizF)Dkzb8$-^svxy zg;;e@*sbo8sFZRCX(^yp-6m5nu0P76TzJ7hmTBcW{{Z=_x>3UVV8Z|a002ovPDHLk FV1jY|t^5E0 diff --git a/src/lay/lay/images/home.png b/src/lay/lay/images/home.png index eb50c31ea73ddf6cae59eec691a12a6e221cb9b1..7cee0be19f47be137800d26f13fa019a939a1ffa 100644 GIT binary patch delta 684 zcmV;d0#p6J1^)$*I|~FS000Ch0ohL-fRRQhe|$+qK~zYI)s@R@6Hye0fA=wuWD**h z$JmXg#RtWW&}`l4MyPcmxRPQ}>Pit8i4;Kxw20Qe1a}G*7Yd@FOaBBRDu^vsJ679; zX*=!2rj|hNjEj~wHZ$qOm7e8=^L^)c&m9K%muj$A*9Loat(hLu41CH;ngFmrzR;0- ze_UJ*(no{BKUoPgtdO{-koc69G=nDDF@cdytF!ak*N{Y$Lp?2was~NUi^5)Wmi9z` zbQsST%6}~|vT1d0UNcorQzI9;8FwmxJ%cI_BmZPV*uSbfN_?-#$fnh~qGpCla@qFtTZNwy2q5nWaW=8H{oTc=r&HBJ1D{_?ARLeqc=4 zD{Gz-;sKuyNmL)bafLAen0&E5c`dk3A|cm*RoJCfE8!G7Y9*u={=KM8hb5{HfA<|` zl9mAGKl}mmf^SM7A$RGVuuCgeLTpJ$Ej%PqefUI-$&%s=YAj*Mojop=OFymThJ*q@ z$qv15QJl8nV^tj3-{F#pLndm9PXYh%ex!2PQ3nRaz`xlL1Qs!%8d{tf>h7WEt3kn4r^ S1?R&60000_PmTH9;&87FZY<0$U`Cm?97pOIaEQBt~R9&_AG&x-lUH^1{H-6)6l%bfA&S5C??R zsI8FG!jPnHjv-A*Qzx})dCPh5ec%1wv!x>`CH`kAQC_yENwcR(i?WBpCa`l9|5)cW ze*m~Kv6G(rAe&+Kg-T%OC|<5}H=%JWp>eBR=dPWj_^}9ja8u)!Q@j>)9$$}{y0FT1 zHI!s*i;8&?6_?lc;EZtrs0Mtdp)mj6Z~SgK|QK=5VSr-mrfSoYm z%+D~>(5#B9){1!&kpz9XJ^&X+SJ0Dae@QqC&zO1Yq*ZanNXY>~F;8Np%3V|EmNPp9 z7skG4rk#fKvc$}UVXe5r4U+7eK@XnP`59++ujeD+gR4&*h5Pq>ud%g}&9pLuGwBNs zg%7X85^xmm&pPZ0N}~3_~O-b?26Lf z>~s`>+Vp_Ti$>U!WBWVgrx(8@*lPvAtA{D&^N*b`2Zr1$xj0dy^S7svNQZ0F&sE5G zcfZk0H67|d00`iEIJm-t5upS}RfLZg5CByRP7nbeC?KNeV-u*O;oZH5C$Ir!bb{E} zliOhg01?0f5{!ojqlQ0IB0AD7KuSqt1;<^HZWaGhe*h2j(zRTS4tW3o002ovPDHLk FV1kX*BiR4| diff --git a/src/lay/lay/images/next_topic.png b/src/lay/lay/images/next_topic.png index 63e9f391f328a3ab8fb195a61ef711dc39b29ea9..2ba1acba36fc2ee34d25dc61fcdbfba7b07a4a57 100644 GIT binary patch delta 694 zcmV;n0!jUl1c(NZI|~FS000Ch0ohL-fRRQge*%R`L_t(YiOrKgOcPNY$G>+Tg)YVr zj5f-~WEDJ1Qkd5l+th_kyxpl;ub)1kT!FO-M;CL z-irsot$6^~Ow%meTLrtgD2ge+-`^Sve}!BCpeRa3*Yz@E%$rCgR(w7m*B9OZBnNC( zljJ~4Swtsih++QJGHcm7NDUAS24BdsJP?n^>+Cl?Jd9*A2}&sdKz}*44Ip;N2BK5T z#Q0XyGV8x;klGFyV|{~zgALvu8XAHi2-}51de@Xns~Ec~y3c#Wz^UU$XaD?@f4U`e z&Ks7*Ifp3P+6pT>G7S_~fzuZO;Yh>09iS?TQrRg!Jv|M^*xonT%mbJ1z{;m_Yqm(1 zGM2WwR=A!@r7Cp*N@-Zv_44TGXhl_3sH%#Qk&%jF7zNIG=i=hx);r~G;s9`E4A$3o zh&=g7jPF#-tmk)MGO*>OmX?+Ye*pc}(p$^2!kVU~0l??;aXow-paf_;23&p!>$8H$ za@V^?Rt{A?-m4AhiMgcVQV%5qbQ2Pw@u8f#d8ASRW=3dH&_E;!OkS(lbP7 zO8bg81)vOw&K*p=DL`bFkWWA8!bY(?V47xSzkbJn<&5<{TP)Xney9LJAP_EOizIbW c@qZG(0h4claX)!`ng9R*07*qoM6N<$f_=q9&Hw-a delta 444 zcmV;t0Ymgt`by92^wCNp~0h9zr{H=%Axi1lKMi;viDc!J&;Mt?-J+q0zSaXI`Ke z?sW6ck323Z5UXV%i&Pjx?8MR(Kk4Qnqd}!o6UY2*iYsB7Z>g6@Uex4&XiLIshw} z#R6&J05J1l1wp4NSEUsmzM^cU5uO{!8V(@Gb6r2CyS3^|)0oe5trMOL&Z4^ zf3ofZ2k!Iv&ej6olR%Or!Rz%-P)d8AJbG}bt^!Gt1VTuhQYx#DhlyBRt(A|4UEy6_ zvN|$k!}V-^1Y7V9S-pRYh{fMPp-zHLcxRie-o0VN^=wr^Fc>^3ilS;+R;XNhUn-R% zb8~aws|q&Z9j&r@XK)LyXMsW%&{ucBe|ery`~Ch#MNvcmh{a;PiA3UJFc_qcq@e=d zeo9t{t`X7Lwkje_ zoNpcg(&@C3*Y)0yU%pNcK75%kF&;S5R@r@NV+Sl2iy3Zsxi;z4JkCodld+nckG)=9 z+c=xhQYS85kOjV}9r*SRDC7Xx-9Dc+plKS{($exGlgV67rBWOr#IvxlfYH%WBbiL< zjIsWurKKDIaPB2ak|dBuK7RUmf1?~7ju6km!@%?iV3`1b!1y0%n#KY4NfHEM48Xba z^hwh+BQrBIiw>_suqhD{r$;^V_`L}6kS1Vi1OVG1*h{MZi!@Cu<_+|vm(8j0(9?Wr zaTcJL_sZN|;Feq;o5&Xz696yNPvDjezWSUuipIZ&*Di1*D>=PCy=+e3HGHeD*IfYE x_hcof_kH?de%Kd*Bl)$a_i`L}rdG+m<}c>jQvQN6ZI%E4002ovPDHLkV1fp~SX2N2 delta 501 zcmVmsfLm?g9nm`Jj+95S0OC%!| z#j;-q%ZkpqDw44u+;n%J5AVJEK@q_^W24Gql8R(=Edr9tvg*eGzOP&`hqtz$wX47Z ze*hvD+CJNIob9*LzTOC$hQQ5($85G6PLQe=~fc2W4Ud07PW$2Z8N<`RY2mucIWoHp6v+ zTfoicvL1*?;s+z!I~};rtAi-{dwmzKmuMe=Z-D#hoG!>DqtN#H=dSbSLzG0m4$ozf zo{}bjE`VV=x&X2h1OPPs2PBpC=;3ke>1MmP*Q%)`ZTchzcU_1G3(BK~@g|E%U*dimages/empty_12.png images/unlocked_16.png images/locked_16.png + images/bookmark.png + images/bookmark_16.png syntax/ruby.xml diff --git a/src/laybasic/laybasic/BrowserPanel.ui b/src/laybasic/laybasic/BrowserPanel.ui index dd3c85b39..8e04d599a 100644 --- a/src/laybasic/laybasic/BrowserPanel.ui +++ b/src/laybasic/laybasic/BrowserPanel.ui @@ -29,46 +29,23 @@ 2 - - - - Forward - - - ... - - - - :/forward.png:/forward.png - - - - 24 - 24 - - - - true - - - - + Qt::Horizontal - QSizePolicy::MinimumExpanding + QSizePolicy::Fixed - 20 + 6 20 - + @@ -285,28 +262,28 @@ - - + + + + Forward + - TextLabel + ... - - - - - - Qt::Horizontal + + + :/forward.png:/forward.png - - QSizePolicy::Fixed - - + - 6 - 20 + 24 + 24 - + + true + + @@ -331,68 +308,6 @@ - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 6 - 20 - - - - - - - - Next Topic - - - ... - - - - :/next_topic.png:/next_topic.png - - - - 24 - 24 - - - - true - - - - - - - Previous Topic - - - ... - - - - :/prev_topic.png:/prev_topic.png - - - - 24 - 24 - - - - true - - - @@ -416,7 +331,99 @@ - + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 6 + 20 + + + + + + + + Previous Topic + + + ... + + + + :/prev_topic.png:/prev_topic.png + + + + 24 + 24 + + + + true + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 20 + + + + + + + + ... + + + + :/find.png:/find.png + + + true + + + + + + + Next Topic + + + ... + + + + :/next_topic.png:/next_topic.png + + + + 24 + 24 + + + + true + + + + @@ -429,14 +436,46 @@ + + + + Qt::Horizontal + + + QSizePolicy::MinimumExpanding + + + + 20 + 20 + + + + + + + + TextLabel + + + - + + + Bookmark this location + ... - :/find.png:/find.png + :/bookmark.png:/bookmark.png + + + + 24 + 24 + true diff --git a/src/laybasic/laybasic/layBrowserPanel.cc b/src/laybasic/laybasic/layBrowserPanel.cc index fc7855867..f9a3dd172 100644 --- a/src/laybasic/laybasic/layBrowserPanel.cc +++ b/src/laybasic/laybasic/layBrowserPanel.cc @@ -88,6 +88,7 @@ BrowserPanel::init () connect (mp_ui->forward_pb, SIGNAL (clicked ()), this, SLOT (forward ())); connect (mp_ui->next_topic_pb, SIGNAL (clicked ()), this, SLOT (next ())); connect (mp_ui->prev_topic_pb, SIGNAL (clicked ()), this, SLOT (prev ())); + connect (mp_ui->bookmark_pb, SIGNAL (clicked ()), this, SLOT (bookmark ())); connect (mp_ui->home_pb, SIGNAL (clicked ()), this, SLOT (home ())); connect (mp_ui->search_edit, SIGNAL (textEdited (const QString &)), this, SLOT (search_text_changed (const QString &))); connect (mp_ui->search_edit, SIGNAL (returnPressed ()), this, SLOT (search_edited ())); @@ -212,6 +213,7 @@ BrowserPanel::refresh_bookmark_list () for (std::list::const_iterator i = m_bookmarks.begin (); i != m_bookmarks.end (); ++i) { QTreeWidgetItem *item = new QTreeWidgetItem (mp_ui->browser_bookmark_view); item->setData (0, Qt::DisplayRole, tl::to_qstring (i->title)); + item->setData (0, Qt::DecorationRole, QIcon (":/bookmark_16.png")); } } From 70981ab03bd0096bb44a63030c67d09041f2a256 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 5 Sep 2020 00:16:21 +0200 Subject: [PATCH 076/113] Help dialog: bookmarks are persisted now. --- src/lay/lay/layConfig.h | 1 + src/lay/lay/layHelpDialog.cc | 3 + src/lay/lay/layMainConfigPages.cc | 1 + src/lay/lay/layMainWindow.cc | 22 ++--- src/laybasic/laybasic/BrowserPanel.ui | 9 +- src/laybasic/laybasic/layBrowserPanel.cc | 114 ++++++++++++++++++++++- src/laybasic/laybasic/layBrowserPanel.h | 22 ++++- 7 files changed, 151 insertions(+), 21 deletions(-) diff --git a/src/lay/lay/layConfig.h b/src/lay/lay/layConfig.h index a915366cf..541d9f4ef 100644 --- a/src/lay/lay/layConfig.h +++ b/src/lay/lay/layConfig.h @@ -60,6 +60,7 @@ static const std::string cfg_layout_file_watcher_enabled ("layout-file-watcher-e static const std::string cfg_window_geometry ("window-geometry"); static const std::string cfg_micron_digits ("digits-micron"); static const std::string cfg_dbu_digits ("digits-dbu"); +static const std::string cfg_assistant_bookmarks ("assistant-bookmarks"); } diff --git a/src/lay/lay/layHelpDialog.cc b/src/lay/lay/layHelpDialog.cc index 8e337ee02..47f603666 100644 --- a/src/lay/lay/layHelpDialog.cc +++ b/src/lay/lay/layHelpDialog.cc @@ -24,6 +24,8 @@ #include "layHelpDialog.h" #include "layHelpSource.h" #include "layBrowserPanel.h" +#include "layDispatcher.h" +#include "layConfig.h" #include "tlStaticObjects.h" #include "ui_HelpDialog.h" @@ -49,6 +51,7 @@ HelpDialog::HelpDialog (QWidget *parent, bool modal) setModal (modal); mp_ui->button_frame->setVisible (modal); + mp_ui->browser_panel->set_dispatcher (lay::Dispatcher::instance (), cfg_assistant_bookmarks); m_def_title = windowTitle (); connect (mp_ui->browser_panel, SIGNAL (title_changed (const QString &)), this, SLOT (title_changed (const QString &))); diff --git a/src/lay/lay/layMainConfigPages.cc b/src/lay/lay/layMainConfigPages.cc index d5a219cee..7b982788b 100644 --- a/src/lay/lay/layMainConfigPages.cc +++ b/src/lay/lay/layMainConfigPages.cc @@ -83,6 +83,7 @@ public: options.push_back (std::pair (cfg_micron_digits, "5")); options.push_back (std::pair (cfg_dbu_digits, "2")); options.push_back (std::pair (cfg_reader_options_show_always, "false")); + options.push_back (std::pair (cfg_assistant_bookmarks, "")); } virtual std::vector > config_pages (QWidget *parent) const diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 87215e52c..29383c2d4 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -192,7 +192,7 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha init_menu (); - mp_assistant = new lay::HelpDialog (this); + mp_assistant = 0; mp_pr = new lay::ProgressReporter (); mp_pr->set_progress_bar (&m_text_progress); @@ -3719,6 +3719,10 @@ MainWindow::cm_macro_editor () void MainWindow::cm_show_assistant () { + if (! mp_assistant) { + mp_assistant = new lay::HelpDialog (this); + } + if (mp_assistant->isMinimized ()) { mp_assistant->showNormal (); } else { @@ -3754,13 +3758,7 @@ MainWindow::show_assistant_url (const std::string &url, bool modal) } else { - if (mp_assistant->isMinimized ()) { - mp_assistant->showNormal (); - } else { - mp_assistant->show (); - } - mp_assistant->activateWindow (); - mp_assistant->raise (); + cm_show_assistant (); mp_assistant->load (url); } @@ -3777,13 +3775,7 @@ MainWindow::show_assistant_topic (const std::string &s, bool modal) } else { - if (mp_assistant->isMinimized ()) { - mp_assistant->showNormal (); - } else { - mp_assistant->show (); - } - mp_assistant->activateWindow (); - mp_assistant->raise (); + cm_show_assistant (); mp_assistant->search (s); } diff --git a/src/laybasic/laybasic/BrowserPanel.ui b/src/laybasic/laybasic/BrowserPanel.ui index 8e04d599a..d77c6c45a 100644 --- a/src/laybasic/laybasic/BrowserPanel.ui +++ b/src/laybasic/laybasic/BrowserPanel.ui @@ -142,7 +142,7 @@ - Bookmarks + Favorites @@ -462,7 +462,7 @@ - Bookmark this location + Add this location to the favorites list ... @@ -499,6 +499,11 @@ Ctrl+B + + + Delete Entry + + diff --git a/src/laybasic/laybasic/layBrowserPanel.cc b/src/laybasic/laybasic/layBrowserPanel.cc index f9a3dd172..3dd2393e2 100644 --- a/src/laybasic/laybasic/layBrowserPanel.cc +++ b/src/laybasic/laybasic/layBrowserPanel.cc @@ -22,9 +22,11 @@ #include "layBrowserPanel.h" +#include "layDispatcher.h" #include "tlExceptions.h" #include "tlInternational.h" #include "tlException.h" +#include "tlString.h" #include "ui_BrowserPanel.h" @@ -56,9 +58,44 @@ BrowserTextWidget::loadResource (int type, const QUrl &url) // ------------------------------------------------------------- +void +BookmarkItem::read (tl::Extractor &ex) +{ + while (! ex.at_end () && ! ex.test (";")) { + + std::string k, v; + ex.read_word (k); + ex.test (":"); + ex.read_word_or_quoted (v, "+-."); + ex.test (","); + + if (k == "url") { + url = v; + } else if (k == "title") { + title = v; + } else if (k == "position") { + tl::from_string (v, position); + } + + } +} + +std::string +BookmarkItem::to_string () const +{ + std::string r; + r = "url:" + tl::to_quoted_string (url) + ","; + r += "title:" + tl::to_quoted_string (title) + ","; + r += "position:" + tl::to_string (position) + ";"; + return r; +} + +// ------------------------------------------------------------- + BrowserPanel::BrowserPanel (QWidget *parent) : QWidget (parent), - m_back_dm (this, &BrowserPanel::back) + m_back_dm (this, &BrowserPanel::back), + mp_dispatcher (0) { init (); } @@ -84,6 +121,9 @@ BrowserPanel::init () mp_ui->browser->addAction (mp_ui->action_find); mp_ui->browser->addAction (mp_ui->action_bookmark); + mp_ui->browser_bookmark_view->addAction (mp_ui->action_delete_bookmark); + mp_ui->browser_bookmark_view->setContextMenuPolicy (Qt::ActionsContextMenu); + connect (mp_ui->back_pb, SIGNAL (clicked ()), this, SLOT (back ())); connect (mp_ui->forward_pb, SIGNAL (clicked ()), this, SLOT (forward ())); connect (mp_ui->next_topic_pb, SIGNAL (clicked ()), this, SLOT (next ())); @@ -103,6 +143,7 @@ BrowserPanel::init () connect (mp_ui->on_page_search_next, SIGNAL (clicked ()), this, SLOT (page_search_next ())); connect (mp_ui->action_find, SIGNAL (triggered ()), this, SLOT (find ())); connect (mp_ui->action_bookmark, SIGNAL (triggered ()), this, SLOT (bookmark ())); + connect (mp_ui->action_delete_bookmark, SIGNAL (triggered ()), this, SLOT (delete_bookmark ())); connect (mp_ui->browser_bookmark_view, SIGNAL (itemDoubleClicked (QTreeWidgetItem *, int)), this, SLOT (bookmark_item_selected (QTreeWidgetItem *))); mp_completer = new QCompleter (this); @@ -118,8 +159,6 @@ BrowserPanel::init () set_label (std::string ()); - // TODO: load bookmarks ... - refresh_bookmark_list (); } @@ -132,6 +171,37 @@ BrowserPanel::~BrowserPanel () mp_ui = 0; } +void +BrowserPanel::set_dispatcher (lay::Dispatcher *dispatcher, const std::string &cfg_bookmarks) +{ + mp_dispatcher = dispatcher; + m_cfg_bookmarks = cfg_bookmarks; + + m_bookmarks.clear (); + + // load the bookmarks + try { + + if (mp_dispatcher) { + + std::string v; + mp_dispatcher->config_get (m_cfg_bookmarks, v); + + tl::Extractor ex (v.c_str ()); + while (! ex.at_end ()) { + m_bookmarks.push_back (BookmarkItem ()); + m_bookmarks.back ().read (ex); + } + + } + + } catch (...) { + // exceptions ignored here + } + + refresh_bookmark_list (); +} + std::string BrowserPanel::title () const { @@ -155,6 +225,22 @@ BrowserPanel::bookmark () add_bookmark (bm); refresh_bookmark_list (); + store_bookmarks (); +} + +void +BrowserPanel::store_bookmarks () +{ + if (mp_dispatcher) { + + std::string s; + for (std::list::const_iterator i = m_bookmarks.begin (); i != m_bookmarks.end (); ++i) { + s += i->to_string (); + } + + mp_dispatcher->config_set (m_cfg_bookmarks, s); + + } } void @@ -178,6 +264,7 @@ BrowserPanel::bookmark_item_selected (QTreeWidgetItem *item) m_bookmarks.push_front (bm); refresh_bookmark_list (); + store_bookmarks (); load (bm.url); mp_ui->browser->verticalScrollBar ()->setValue (bm.position); @@ -204,6 +291,26 @@ BrowserPanel::add_bookmark (const BookmarkItem &item) m_bookmarks.push_front (item); } +void +BrowserPanel::delete_bookmark () +{ + QTreeWidgetItem *item = mp_ui->browser_bookmark_view->currentItem (); + if (! item) { + return; + } + + int index = mp_ui->browser_bookmark_view->indexOfTopLevelItem (item); + std::list::iterator i = m_bookmarks.begin (); + for ( ; i != m_bookmarks.end () && index > 0; --index, ++i) + ; + + if (i != m_bookmarks.end ()) { + m_bookmarks.erase (i); + refresh_bookmark_list (); + store_bookmarks (); + } +} + void BrowserPanel::refresh_bookmark_list () { @@ -213,6 +320,7 @@ BrowserPanel::refresh_bookmark_list () for (std::list::const_iterator i = m_bookmarks.begin (); i != m_bookmarks.end (); ++i) { QTreeWidgetItem *item = new QTreeWidgetItem (mp_ui->browser_bookmark_view); item->setData (0, Qt::DisplayRole, tl::to_qstring (i->title)); + item->setData (0, Qt::ToolTipRole, tl::to_qstring (i->title)); item->setData (0, Qt::DecorationRole, QIcon (":/bookmark_16.png")); } } diff --git a/src/laybasic/laybasic/layBrowserPanel.h b/src/laybasic/laybasic/layBrowserPanel.h index dd6bb194b..d32366a18 100644 --- a/src/laybasic/laybasic/layBrowserPanel.h +++ b/src/laybasic/laybasic/layBrowserPanel.h @@ -49,6 +49,7 @@ namespace lay { class BrowserPanel; +class Dispatcher; /** * @brief Specifies the outline of the document @@ -293,6 +294,9 @@ struct LAYBASIC_PUBLIC BookmarkItem return url == other.url && position == other.position; } + void read (tl::Extractor &ex); + std::string to_string () const; + std::string url; std::string title; int position; @@ -311,14 +315,26 @@ Q_OBJECT public: /** * @brief Constructor + * + * @param p The parent widget */ - BrowserPanel (QWidget *p); + BrowserPanel (QWidget *p); /** * @brief Dtor */ ~BrowserPanel (); + /** + * @brief Connects the panel to a configuration dispatcher + * + * Doing so allows storing bookmarks and retrieving them. + * + * @param dispatcher If given, this interface will be used to retrieve and store the bookmark list + * @param cfg_bookmarks If dispatcher is given, this will be the configuration key to store the bookmarks + */ + void set_dispatcher (lay::Dispatcher *dispatcher, const std::string &cfg_bookmarks); + /** * @brief Connect to a source object * If " @@ -428,6 +444,7 @@ protected slots: void text_changed (); void outline_item_clicked (QTreeWidgetItem *item); void bookmark_item_selected (QTreeWidgetItem *item); + void delete_bookmark (); protected: virtual QVariant loadResource (int type, const QUrl &url); @@ -452,11 +469,14 @@ private: QCompleter *mp_completer; QStringListModel *mp_completer_model; std::list m_bookmarks; + lay::Dispatcher *mp_dispatcher; + std::string m_cfg_bookmarks; void init (); void clear_bookmarks (); void add_bookmark (const BookmarkItem &item); void refresh_bookmark_list (); + void store_bookmarks (); }; } From fb901441764da456dc85c0dc0f5cb7a5d4f2ccff Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 5 Sep 2020 23:57:40 +0200 Subject: [PATCH 077/113] WIP: mouse cursor and snap highlighting --- src/ant/ant/antPropertiesPage.cc | 20 ++-- src/ant/ant/antService.cc | 18 ++-- src/edt/edt/edtPartialService.cc | 2 +- src/edt/edt/edtService.cc | 84 ++++++++++++++++- src/edt/edt/edtService.h | 18 ++++ src/edt/edt/edtServiceImpl.cc | 12 ++- src/edt/edt/edtServiceImpl.h | 1 + src/laybasic/laybasic/laySnap.cc | 92 +++++++++++++----- src/laybasic/laybasic/laySnap.h | 85 +++++++++++++++-- .../{laySnap.cc => laySnapTests.cc} | 94 ++++++++++--------- src/laybasic/unit_tests/unit_tests.pro | 4 +- 11 files changed, 327 insertions(+), 103 deletions(-) rename src/laybasic/unit_tests/{laySnap.cc => laySnapTests.cc} (55%) diff --git a/src/ant/ant/antPropertiesPage.cc b/src/ant/ant/antPropertiesPage.cc index f94185e0c..f48dec09f 100644 --- a/src/ant/ant/antPropertiesPage.cc +++ b/src/ant/ant/antPropertiesPage.cc @@ -231,11 +231,11 @@ PropertiesPage::snap_to_layout_clicked () while (snap_range < max_range) { - std::pair pp = lay::obj_snap (service->view (), snap_p1 ? p2 : p1, snap_p1 ? p1 : p2, g, ac, snap_range); - if (pp.first) { + lay::PointSnapToObjectResult pp = lay::obj_snap (service->view (), snap_p1 ? p2 : p1, snap_p1 ? p1 : p2, g, ac, snap_range); + if (pp.object_snap != lay::PointSnapToObjectResult::NoObject) { - QString xs = tl::to_qstring (tl::micron_to_string (pp.second.x ())); - QString ys = tl::to_qstring (tl::micron_to_string (pp.second.y ())); + QString xs = tl::to_qstring (tl::micron_to_string (pp.snapped_point.x ())); + QString ys = tl::to_qstring (tl::micron_to_string (pp.snapped_point.y ())); if (sender () == p1_to_layout) { x1->setText (xs); @@ -262,13 +262,13 @@ PropertiesPage::snap_to_layout_clicked () double snap_range = service->widget ()->mouse_event_trans ().inverted ().ctrans (service->snap_range ()); snap_range *= 0.5; - std::pair ee = lay::obj_snap2 (service->view (), p1, p2, g, ac, snap_range, snap_range * 1000.0); - if (ee.first) { + lay::TwoPointSnapToObjectResult ee = lay::obj_snap2 (service->view (), p1, p2, g, ac, snap_range, snap_range * 1000.0); + if (ee.any) { - x1->setText (tl::to_qstring (tl::micron_to_string (ee.second.p1 ().x ()))); - y1->setText (tl::to_qstring (tl::micron_to_string (ee.second.p1 ().y ()))); - x2->setText (tl::to_qstring (tl::micron_to_string (ee.second.p2 ().x ()))); - y2->setText (tl::to_qstring (tl::micron_to_string (ee.second.p2 ().y ()))); + x1->setText (tl::to_qstring (tl::micron_to_string (ee.first.x ()))); + y1->setText (tl::to_qstring (tl::micron_to_string (ee.first.y ()))); + x2->setText (tl::to_qstring (tl::micron_to_string (ee.second.x ()))); + y2->setText (tl::to_qstring (tl::micron_to_string (ee.second.y ()))); db::Transaction t (manager (), tl::to_string (QObject::tr ("Snap both ruler points"))); emit edited (); diff --git a/src/ant/ant/antService.cc b/src/ant/ant/antService.cc index fd0a50dc0..282386c6a 100644 --- a/src/ant/ant/antService.cc +++ b/src/ant/ant/antService.cc @@ -1434,14 +1434,14 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio double snap_range = widget ()->mouse_event_trans ().inverted ().ctrans (m_snap_range); snap_range *= 0.5; - std::pair ee = lay::obj_snap2 (mp_view, p, g, ac, snap_range, snap_range * 1000.0); - if (ee.first) { + lay::TwoPointSnapToObjectResult ee = lay::obj_snap2 (mp_view, p, g, ac, snap_range, snap_range * 1000.0); + if (ee.any) { // begin the transaction tl_assert (! manager ()->transacting ()); manager ()->transaction (tl::to_string (QObject::tr ("Create ruler"))); - m_current = ant::Object (ee.second.p1 (), ee.second.p2 (), 0, tpl); + m_current = ant::Object (ee.first, ee.second, 0, tpl); show_message (); insert_ruler (m_current, true); @@ -1505,9 +1505,9 @@ Service::create_measure_ruler (const db::DPoint &pt, lay::angle_constraint_type ant::Template tpl; - std::pair ee = lay::obj_snap2 (mp_view, pt, db::DVector (), ac, snap_range, snap_range * 1000.0); - if (ee.first) { - return ant::Object (ee.second.p1 (), ee.second.p2 (), 0, tpl); + lay::TwoPointSnapToObjectResult ee = lay::obj_snap2 (mp_view, pt, db::DVector (), ac, snap_range, snap_range * 1000.0); + if (ee.any) { + return ant::Object (ee.first, ee.second, 0, tpl); } else { return ant::Object (pt, pt, 0, tpl); } @@ -1545,7 +1545,8 @@ Service::snap1 (const db::DPoint &p, bool obj_snap) } double snap_range = widget ()->mouse_event_trans ().inverted ().ctrans (m_snap_range); - return lay::obj_snap (obj_snap ? mp_view : 0, p, g, snap_range); + lay::PointSnapToObjectResult res = lay::obj_snap (obj_snap ? mp_view : 0, p, g, snap_range); + return std::make_pair (res.object_snap != lay::PointSnapToObjectResult::NoObject, res.snapped_point); } @@ -1560,7 +1561,8 @@ Service::snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *o double snap_range = widget ()->mouse_event_trans ().inverted ().ctrans (m_snap_range); lay::angle_constraint_type snap_mode = ac == lay::AC_Global ? (obj->angle_constraint () == lay::AC_Global ? m_snap_mode : obj->angle_constraint ()) : ac; - return lay::obj_snap (m_obj_snap && obj->snap () ? mp_view : 0, p1, p2, g, snap_mode, snap_range); + lay::PointSnapToObjectResult res = lay::obj_snap (m_obj_snap && obj->snap () ? mp_view : 0, p1, p2, g, snap_mode, snap_range); + return std::make_pair (res.object_snap != lay::PointSnapToObjectResult::NoObject, res.snapped_point); } diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index 96dc5c437..31254ae4d 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -1309,7 +1309,7 @@ db::DPoint PartialService::snap2 (const db::DPoint &p) const { double snap_range = widget ()->mouse_event_trans ().inverted ().ctrans (sr_pixels); - return lay::obj_snap (m_snap_to_objects ? view () : 0, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, snap_range).second; + return lay::obj_snap (m_snap_to_objects ? view () : 0, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, snap_range).snapped_point; } void diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index aa5cf2b53..56452b024 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -116,6 +116,7 @@ Service::~Service () } m_edit_markers.clear (); + reset_mouse_cursor (); clear_transient_selection (); } @@ -178,18 +179,24 @@ Service::snap (const db::DPoint &p, const db::DPoint &plast, bool connect) const const int sr_pixels = 8; // TODO: make variable -db::DPoint -Service::snap2 (const db::DPoint &p) const +lay::PointSnapToObjectResult +Service::snap2_details (const db::DPoint &p) const { double snap_range = widget ()->mouse_event_trans ().inverted ().ctrans (sr_pixels); - return lay::obj_snap (m_snap_to_objects ? view () : 0, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, snap_range).second; + return lay::obj_snap (m_snap_to_objects ? view () : 0, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, snap_range); +} + +db::DPoint +Service::snap2 (const db::DPoint &p) const +{ + return snap2_details (p).snapped_point; } db::DPoint Service::snap2 (const db::DPoint &p, const db::DPoint &plast, bool connect) const { double snap_range = widget ()->mouse_event_trans ().inverted ().ctrans (sr_pixels); - return lay::obj_snap (m_snap_to_objects ? view () : 0, plast, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, connect ? connect_ac () : move_ac (), snap_range).second; + return lay::obj_snap (m_snap_to_objects ? view () : 0, plast, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, connect ? connect_ac () : move_ac (), snap_range).snapped_point; } void @@ -473,6 +480,71 @@ Service::selection_bbox () return box; } +namespace +{ + +class MouseCursorViewObject + : public lay::ViewObject +{ +public: + MouseCursorViewObject (lay::ViewObjectWidget *widget, const db::DPoint &pt) + : lay::ViewObject (widget, false), m_pt (pt) + { } + + virtual void render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas) + { + int dither_pattern = 0; // solid + int lw = int (0.5 + 1.0 / canvas.resolution ()); + + std::vector ops; + ops.resize (1); + ops[0] = lay::ViewOp (canvas.foreground_color ().rgb (), lay::ViewOp::Copy, 0, (unsigned int) dither_pattern, 0, lay::ViewOp::Rect, lw, 0); + lay::CanvasPlane *plane = canvas.plane (ops); + + lay::Renderer &r = canvas.renderer (); + + double rad = 4.0 / canvas.resolution () / vp.trans ().mag (); + + const size_t num_pts = 16; + db::DPoint pts [num_pts]; + for (size_t i = 0; i < num_pts; ++i) { + double x = rad * cos (M_PI * 2.0 * double (i) / double (num_pts)); + double y = rad * sin (M_PI * 2.0 * double (i) / double (num_pts)); + pts [i] = m_pt + db::DVector (x, y); + } + db::DPolygon circle; + circle.assign_hull (pts, pts + num_pts); + + r.draw (circle, vp.trans (), 0, plane, 0, 0); + + r.draw (db::DEdge (m_pt + db::DVector (0, rad * 0.5), m_pt + db::DVector (0, rad * 4)), vp.trans (), 0, plane, 0, 0); + r.draw (db::DEdge (m_pt + db::DVector (rad * 0.5, 0), m_pt + db::DVector (rad * 4, 0)), vp.trans (), 0, plane, 0, 0); + r.draw (db::DEdge (m_pt + db::DVector (0, -rad * 0.5), m_pt + db::DVector (0, -rad * 4)), vp.trans (), 0, plane, 0, 0); + r.draw (db::DEdge (m_pt + db::DVector (-rad * 0.5, 0), m_pt + db::DVector (-rad * 4, 0)), vp.trans (), 0, plane, 0, 0); + } + +private: + db::DPoint m_pt; +}; + +} + +void +Service::set_mouse_cursor (const db::DPoint &pt) +{ + reset_mouse_cursor (); + m_mouse_cursor_markers.push_back (new MouseCursorViewObject (widget (), pt)); +} + +void +Service::reset_mouse_cursor () +{ + for (std::vector::iterator r = m_mouse_cursor_markers.begin (); r != m_mouse_cursor_markers.end (); ++r) { + delete *r; + } + m_mouse_cursor_markers.clear (); +} + void Service::set_edit_marker (lay::ViewObject *edit_marker) { @@ -715,6 +787,8 @@ Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) } if (m_editing) { do_mouse_move (p); + } else { + do_mouse_move_inactive (p); } m_alt_ac = lay::AC_Global; @@ -813,6 +887,8 @@ Service::activated () void Service::deactivated () { + reset_mouse_cursor (); + // make all editor option pages visible activate_service (view (), plugin_declaration (), false); diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h index 997d7e102..4d96adfa7 100644 --- a/src/edt/edt/edtService.h +++ b/src/edt/edt/edtService.h @@ -460,6 +460,16 @@ protected: */ virtual void service_configuration_changed (); + /** + * @brief Sets the mouse cursor to the given point + */ + void set_mouse_cursor (const db::DPoint &pt); + + /** + * @brief Resets the mouse cursor + */ + void reset_mouse_cursor (); + /** * @brief Install a marker for representing the edited object * @@ -552,6 +562,11 @@ protected: return m_editing; } + /** + * @brief Point snapping with detailed return value + */ + lay::PointSnapToObjectResult snap2_details (const db::DPoint &p) const; + private: // The layout view that the editor service is attached to lay::LayoutView *mp_view; @@ -565,6 +580,9 @@ private: // The marker representing the object to be edited std::vector m_edit_markers; + // The marker representing the mouse cursor + std::vector m_mouse_cursor_markers; + // True, if editing is in progress. bool m_editing; diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index 59fbdd3de..51b28a90b 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -413,9 +413,19 @@ PolygonService::set_last_point (const db::DPoint &p) } } -void +void +PolygonService::do_mouse_move_inactive (const db::DPoint &p) +{ + lay::PointSnapToObjectResult snap_details = snap2_details (p); + set_mouse_cursor (snap_details.snapped_point); +} + +void PolygonService::do_mouse_move (const db::DPoint &p) { + lay::PointSnapToObjectResult snap_details = snap2_details (p); + set_mouse_cursor (snap_details.snapped_point); + set_cursor (lay::Cursor::cross); if (m_points.size () >= 2) { set_last_point (p); diff --git a/src/edt/edt/edtServiceImpl.h b/src/edt/edt/edtServiceImpl.h index 37c8cf035..15865e2d6 100644 --- a/src/edt/edt/edtServiceImpl.h +++ b/src/edt/edt/edtServiceImpl.h @@ -91,6 +91,7 @@ public: virtual lay::PropertiesPage *properties_page (db::Manager *manager, QWidget *parent); virtual void do_begin_edit (const db::DPoint &p); virtual void do_mouse_move (const db::DPoint &p); + virtual void do_mouse_move_inactive (const db::DPoint &p); virtual bool do_mouse_click (const db::DPoint &p); virtual void do_finish_edit (); virtual void do_cancel_edit (); diff --git a/src/laybasic/laybasic/laySnap.cc b/src/laybasic/laybasic/laySnap.cc index 04de10a99..841468c63 100644 --- a/src/laybasic/laybasic/laySnap.cc +++ b/src/laybasic/laybasic/laySnap.cc @@ -726,7 +726,7 @@ private: bool m_directed; }; -static std::pair +static PointSnapToObjectResult do_obj_snap (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, double snap_range, const std::vector &cutlines) { db::DPoint dp (pt); @@ -753,25 +753,45 @@ do_obj_snap (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &gri } } - if (finder.any () && anyp) { - // if both the projection and the finder are sucessful, decide by a heuristic criterion which to take - // (the projection gets a penalty (of the snap range) to make it count less than the finder's choice) - // This avoids extreme distortions of the ruler due to projection on long edges. - if ((dp.distance (closest) + snap_range) * 5.0 < dp.distance (finder.get_found ())) { - return std::make_pair (false, closest); - } else { - return std::make_pair (true, finder.get_found ()); - } + // if both the projection and the finder are sucessful, decide by a heuristic criterion which to take + // (the projection gets a penalty (of the snap range) to make it count less than the finder's choice) + // This avoids extreme distortions of the ruler due to projection on long edges. + if (finder.any () && anyp && (dp.distance (closest) + snap_range) * 5.0 < dp.distance (finder.get_found ())) { + + PointSnapToObjectResult res; + res.snapped_point = closest; + return res; + } else if (finder.any ()) { - return std::make_pair (true, finder.get_found ()); + + PointSnapToObjectResult res; + res.snapped_point = finder.get_found (); + res.object_ref = finder.get_found_edge (); + if (finder.is_vertex ()) { + res.object_snap = PointSnapToObjectResult::ObjectVertex; + } else if (finder.has_found_edge ()) { + res.object_snap = PointSnapToObjectResult::ObjectEdge; + } else { + res.object_snap = PointSnapToObjectResult::ObjectUnspecific; + } + return res; + } else if (anyp) { - return std::make_pair (false, closest); + + PointSnapToObjectResult res; + res.snapped_point = closest; + return res; + } else { - return std::make_pair (false, dp); + + PointSnapToObjectResult res; + res.snapped_point = dp; + return res; + } } -std::pair +static TwoPointSnapToObjectResult do_obj_snap2 (lay::LayoutView *view, const db::DPoint &pt1, const db::DPoint &pt2, const db::DVector &grid, double min_search_range, double max_search_range, const std::vector &cutlines) { db::DPoint dp1 (pt1); @@ -828,15 +848,41 @@ do_obj_snap2 (lay::LayoutView *view, const db::DPoint &pt1, const db::DPoint &pt finder2.find (view, sr2); if (finder2.any_exact ()) { + db::DPoint p2 = finder2.get_found (); - return std::make_pair (true, db::DEdge (p1, p2)); + + TwoPointSnapToObjectResult res; + res.any = true; + res.first = p1; + res.second = p2; + + res.object_ref_first = finder.get_found_edge (); + if (finder.is_vertex ()) { + res.object_snap_first = TwoPointSnapToObjectResult::ObjectVertex; + } else if (finder.has_found_edge ()) { + res.object_snap_first = TwoPointSnapToObjectResult::ObjectEdge; + } else { + res.object_snap_first = TwoPointSnapToObjectResult::ObjectUnspecific; + } + + res.object_ref_second = finder2.get_found_edge (); + if (finder2.is_vertex ()) { + res.object_snap_second = TwoPointSnapToObjectResult::ObjectVertex; + } else if (finder2.has_found_edge ()) { + res.object_snap_second = TwoPointSnapToObjectResult::ObjectEdge; + } else { + res.object_snap_second = TwoPointSnapToObjectResult::ObjectUnspecific; + } + + return res; + } sr2 *= 2.0; } - return std::make_pair (false, db::DEdge ()); + return TwoPointSnapToObjectResult (); } @@ -844,7 +890,7 @@ do_obj_snap2 (lay::LayoutView *view, const db::DPoint &pt1, const db::DPoint &pt } - return std::make_pair (false, db::DEdge ()); + return TwoPointSnapToObjectResult (); } static void @@ -868,13 +914,13 @@ make_cutlines (lay::angle_constraint_type snap_mode, const db::DPoint &p1, std:: } } -std::pair +PointSnapToObjectResult obj_snap (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, double snap_range) { return do_obj_snap (view, pt, grid, snap_range, std::vector ()); } -std::pair +PointSnapToObjectResult obj_snap (lay::LayoutView *view, const db::DPoint &p1, const db::DPoint &p2, const db::DVector &grid, lay::angle_constraint_type snap_mode, double snap_range) { std::vector cutlines; @@ -882,19 +928,19 @@ obj_snap (lay::LayoutView *view, const db::DPoint &p1, const db::DPoint &p2, con return do_obj_snap (view, p2, grid, snap_range, cutlines); } -std::pair +TwoPointSnapToObjectResult obj_snap2 (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, double min_search_range, double max_search_range) { return obj_snap2 (view, pt, pt, grid, min_search_range, max_search_range); } -std::pair +TwoPointSnapToObjectResult obj_snap2 (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, lay::angle_constraint_type ac, double min_search_range, double max_search_range) { return obj_snap2 (view, pt, pt, grid, ac, min_search_range, max_search_range); } -std::pair +TwoPointSnapToObjectResult obj_snap2 (lay::LayoutView *view, const db::DPoint &pt1, const db::DPoint &pt2, const db::DVector &grid, double min_search_range, double max_search_range) { db::DPoint dp1 = lay::snap_xy (pt1, grid); @@ -903,7 +949,7 @@ obj_snap2 (lay::LayoutView *view, const db::DPoint &pt1, const db::DPoint &pt2, return do_obj_snap2 (view, dp1, dp2, db::DVector (), min_search_range, max_search_range, std::vector ()); } -std::pair +TwoPointSnapToObjectResult obj_snap2 (lay::LayoutView *view, const db::DPoint &pt1, const db::DPoint &pt2, const db::DVector &grid, lay::angle_constraint_type snap_mode, double min_search_range, double max_search_range) { db::DPoint dp1 = lay::snap_xy (pt1, grid); diff --git a/src/laybasic/laybasic/laySnap.h b/src/laybasic/laybasic/laySnap.h index 8ac286b24..b2a1b3645 100644 --- a/src/laybasic/laybasic/laySnap.h +++ b/src/laybasic/laybasic/laySnap.h @@ -108,6 +108,36 @@ namespace lay */ LAYBASIC_PUBLIC std::pair snap (const db::DPoint &p1, const db::DPoint &p2, db::DCoord grid); + /** + * @brief A structure describing the snap result for a point-wise snap + */ + struct LAYBASIC_PUBLIC PointSnapToObjectResult + { + enum ObjectSnap { + NoObject = 0, + ObjectVertex, + ObjectEdge, + ObjectUnspecific + }; + + PointSnapToObjectResult () : object_snap (NoObject) { } + + /** + * @brief The result of the snap + */ + db::DPoint snapped_point; + + /** + * @brief Indicates whether and how the point was snapped to an object + */ + ObjectSnap object_snap; + + /** + * @brief Indicates the edge the point was snapped against unless in NoObject mode + */ + db::DEdge object_ref; + }; + /** * @brief combined grid-, projection- and object snapping provided to implementing "magnetic features" * @@ -115,17 +145,14 @@ namespace lay * to an object edge or point. It will use a heuristics to determine * what criterion is applied if a conflict is detected. * - * The function will return a pair of the "stiffness" flag and the resulting point. The - * "stiffness" is a measure if the result point can be moved if another snap will be applied. - * It is true if the point is not intended to be moved further, i.e. because it snapped to - * a grid point or a object vertex. + * The function will return a PointSnapToObjectResult object. * * @param view The layout view used for object snapping. Can be 0 to disable object snapping * @param pt The point to snap * @param grid Either (0,0) to disable grid snapping or a (gx,gy) value for the (potentially anisotropic grid) * @param snap_range The search range for objects */ - LAYBASIC_PUBLIC std::pair obj_snap (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, double snap_range); + LAYBASIC_PUBLIC PointSnapToObjectResult obj_snap (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, double snap_range); /** * @brief combined grid-, projection- and object snapping provided to implementing "magnetic features" @@ -133,7 +160,45 @@ namespace lay * This is a convenience method that creates the projection axes from a reference point and an angle mode. * "pr" is the reference point, "pt" is the point to snap. */ - LAYBASIC_PUBLIC std::pair obj_snap (lay::LayoutView *view, const db::DPoint &pr, const db::DPoint &pt, const db::DVector &grid, lay::angle_constraint_type ac, double snap_range); + LAYBASIC_PUBLIC PointSnapToObjectResult obj_snap (lay::LayoutView *view, const db::DPoint &pr, const db::DPoint &pt, const db::DVector &grid, lay::angle_constraint_type ac, double snap_range); + + /** + * @brief A structure describing the snap result for a two-sided object snap (distance measurement) + */ + struct LAYBASIC_PUBLIC TwoPointSnapToObjectResult + { + enum ObjectSnap { + NoObject = 0, + ObjectVertex, + ObjectEdge, + ObjectUnspecific + }; + + TwoPointSnapToObjectResult () : any (false), object_snap_first (NoObject), object_snap_second (NoObject) { } + + /** + * @brief Indicates whether the two-sided snap was successful + */ + bool any; + + /** + * @brief The result of the snap + * Two values are provided for the first and second snap. + */ + db::DPoint first, second; + + /** + * @brief Indicates whether and how the point was snapped to an object + * Two values are provided for the first and second snap. + */ + ObjectSnap object_snap_first, object_snap_second; + + /** + * @brief Indicates the edge the point was snapped against unless in NoObject mode + * Two values are provided for the first and second snap. + */ + db::DEdge object_ref_first, object_ref_second; + }; /** * @brief Same than obj_snap, but delivers two points on two opposite sides of the initial point @@ -141,7 +206,7 @@ namespace lay * This method basically implements "auto measure". The first value of the returned pair * is true if such an edge could be found. Otherwise it's false. */ - LAYBASIC_PUBLIC std::pair obj_snap2 (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, double min_search_range, double max_search_range); + LAYBASIC_PUBLIC TwoPointSnapToObjectResult obj_snap2 (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, double min_search_range, double max_search_range); /** * @brief Same than obj_snap, but delivers two points on two opposite sides of the initial points @@ -151,14 +216,14 @@ namespace lay * * This version accepts two points defining different search regions for first and second edge. */ - LAYBASIC_PUBLIC std::pair obj_snap2 (lay::LayoutView *view, const db::DPoint &pt1, const db::DPoint &pt2, const db::DVector &grid, double min_search_range, double max_search_range); + LAYBASIC_PUBLIC TwoPointSnapToObjectResult obj_snap2 (lay::LayoutView *view, const db::DPoint &pt1, const db::DPoint &pt2, const db::DVector &grid, double min_search_range, double max_search_range); /** * @brief Same than the previous obj_snap2, but allows specification of an angle constraint * * Measurements will be confined to the direction specified. */ - LAYBASIC_PUBLIC std::pair obj_snap2 (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, lay::angle_constraint_type ac, double min_search_range, double max_search_range); + LAYBASIC_PUBLIC TwoPointSnapToObjectResult obj_snap2 (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, lay::angle_constraint_type ac, double min_search_range, double max_search_range); /** * @brief Same than the previous obj_snap2, but allows specification of an angle constraint @@ -167,7 +232,7 @@ namespace lay * * This version accepts two points defining different search regions for first and second edge. */ - LAYBASIC_PUBLIC std::pair obj_snap2 (lay::LayoutView *view, const db::DPoint &pt1, const db::DPoint &pt2, const db::DVector &grid, lay::angle_constraint_type ac, double min_search_range, double max_search_range); + LAYBASIC_PUBLIC TwoPointSnapToObjectResult obj_snap2 (lay::LayoutView *view, const db::DPoint &pt1, const db::DPoint &pt2, const db::DVector &grid, lay::angle_constraint_type ac, double min_search_range, double max_search_range); /** * @brief Reduce a given vector according to the angle constraint diff --git a/src/laybasic/unit_tests/laySnap.cc b/src/laybasic/unit_tests/laySnapTests.cc similarity index 55% rename from src/laybasic/unit_tests/laySnap.cc rename to src/laybasic/unit_tests/laySnapTests.cc index 39a177a1f..9b2c37d03 100644 --- a/src/laybasic/unit_tests/laySnap.cc +++ b/src/laybasic/unit_tests/laySnapTests.cc @@ -51,95 +51,101 @@ TEST(1) view.set_max_hier_levels (1); - std::pair res; + lay::PointSnapToObjectResult res; // not hit res = lay::obj_snap (&view, db::DPoint (1.505, 1.505), db::DVector (), 0.1); - EXPECT_EQ (res.first, false); - EXPECT_EQ (res.second.to_string (), "1.505,1.505"); + EXPECT_EQ (res.object_snap, lay::PointSnapToObjectResult::NoObject); + EXPECT_EQ (res.snapped_point.to_string (), "1.505,1.505"); res = lay::obj_snap (&view, db::DPoint (0.505, 0.505), db::DVector (), 0.1); - EXPECT_EQ (res.first, true); - EXPECT_EQ (res.second.to_string (), "0.5,0.5"); + EXPECT_EQ (res.object_snap, lay::PointSnapToObjectResult::ObjectEdge); + EXPECT_EQ (res.snapped_point.to_string (), "0.5,0.5"); res = lay::obj_snap (&view, db::DPoint (0.485, 0.505), db::DVector (0.01, 0.01), 0.1); - EXPECT_EQ (res.first, true); - EXPECT_EQ (res.second.to_string (), "0.49,0.51"); + EXPECT_EQ (res.object_snap, lay::PointSnapToObjectResult::ObjectEdge); + EXPECT_EQ (res.snapped_point.to_string (), "0.49,0.51"); + EXPECT_EQ (res.object_ref.to_string (), "(0,1;1,0)"); res = lay::obj_snap (&view, db::DPoint (0.205, 0.215), db::DVector (0.01, 0.025), 0.1); - EXPECT_EQ (res.first, false); - EXPECT_EQ (res.second.to_string (), "0.21,0.225"); + EXPECT_EQ (res.object_snap, lay::PointSnapToObjectResult::NoObject); + EXPECT_EQ (res.snapped_point.to_string (), "0.21,0.225"); res = lay::obj_snap (&view, db::DPoint (0.505, 1.005), db::DVector (), 0.1); - EXPECT_EQ (res.first, false); - EXPECT_EQ (res.second.to_string (), "0.505,1.005"); + EXPECT_EQ (res.object_snap, lay::PointSnapToObjectResult::NoObject); + EXPECT_EQ (res.snapped_point.to_string (), "0.505,1.005"); res = lay::obj_snap (&view, db::DPoint (0.005, 1.005), db::DVector (), 0.1); - EXPECT_EQ (res.first, true); - EXPECT_EQ (res.second.to_string (), "0,1"); + EXPECT_EQ (res.object_snap, lay::PointSnapToObjectResult::ObjectVertex); + EXPECT_EQ (res.snapped_point.to_string (), "0,1"); + + res = lay::obj_snap (&view, db::DPoint (0.0, 1.005), db::DVector (), 0.1); + EXPECT_EQ (res.object_snap, lay::PointSnapToObjectResult::ObjectVertex); + EXPECT_EQ (res.snapped_point.to_string (), "0,1"); res = lay::obj_snap (&view, db::DPoint (1.000, 0.505), db::DPoint (0.505, 0.500), db::DVector (), lay::AC_Horizontal, 0.1); - EXPECT_EQ (res.first, true); - EXPECT_EQ (res.second.to_string (), "0.495,0.505"); + EXPECT_EQ (res.object_snap, lay::PointSnapToObjectResult::ObjectEdge); + EXPECT_EQ (res.snapped_point.to_string (), "0.495,0.505"); // projected snapping res = lay::obj_snap (&view, db::DPoint (1.000, 0.505), db::DPoint (0.005, 1.005), db::DVector (), lay::AC_Horizontal, 0.1); - EXPECT_EQ (res.first, true); - EXPECT_EQ (res.second.to_string (), "0,0.505"); + EXPECT_EQ (res.object_snap, lay::PointSnapToObjectResult::ObjectUnspecific); + EXPECT_EQ (res.snapped_point.to_string (), "0,0.505"); + EXPECT_EQ (res.object_ref.to_string (), "(0,1;0,1)"); - std::pair res2; + lay::TwoPointSnapToObjectResult res2; res2 = lay::obj_snap2 (&view, db::DPoint (1.5, 1.5), db::DVector (), 0.005, 1.0); - EXPECT_EQ (res2.first, false); - EXPECT_EQ (res2.second.to_string (), "(0,0;0,0)"); + EXPECT_EQ (res2.any, false); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0,0;0,0)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.205, 0.5), db::DVector (), 0.005, 1.0); - EXPECT_EQ (res2.first, true); - EXPECT_EQ (res2.second.to_string (), "(0.3525,0.6475;0,0.295)"); + EXPECT_EQ (res2.any, true); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0.3525,0.6475;0,0.295)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.205, 0.5), db::DVector (), lay::AC_Horizontal, 0.005, 1.0); - EXPECT_EQ (res2.first, true); - EXPECT_EQ (res2.second.to_string (), "(0,0.5;0.5,0.5)"); + EXPECT_EQ (res2.any, true); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0,0.5;0.5,0.5)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.205, 0.5), db::DVector (0.03, 0.03), lay::AC_Horizontal, 0.005, 1.0); - EXPECT_EQ (res2.first, true); - EXPECT_EQ (res2.second.to_string (), "(0,0.51;0.49,0.51)"); + EXPECT_EQ (res2.any, true); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0,0.51;0.49,0.51)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.205, 0.5), db::DVector (), lay::AC_Vertical, 0.005, 1.0); - EXPECT_EQ (res2.first, true); - EXPECT_EQ (res2.second.to_string (), "(0.205,0.795;0.205,0)"); + EXPECT_EQ (res2.any, true); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0.205,0.795;0.205,0)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.205, 0.5), db::DVector (), lay::AC_Diagonal, 0.005, 1.0); - EXPECT_EQ (res2.first, true); - EXPECT_EQ (res2.second.to_string (), "(0.3525,0.6475;0,0.295)"); + EXPECT_EQ (res2.any, true); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0.3525,0.6475;0,0.295)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.205, 0.505), db::DVector (), lay::AC_Ortho, 0.005, 1.0); - EXPECT_EQ (res2.first, true); - EXPECT_EQ (res2.second.to_string (), "(0,0.505;0.495,0.505)"); + EXPECT_EQ (res2.any, true); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0,0.505;0.495,0.505)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.205, 0.5), db::DVector (), lay::AC_Any, 0.005, 1.0); - EXPECT_EQ (res2.first, true); - EXPECT_EQ (res2.second.to_string (), "(0.3525,0.6475;0,0.295)"); + EXPECT_EQ (res2.any, true); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0.3525,0.6475;0,0.295)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.205, 0.495), db::DVector (0.01, 0.01), 0.005, 1.0); - EXPECT_EQ (res2.first, true); - EXPECT_EQ (res2.second.to_string (), "(0.355,0.645;0,0.29)"); + EXPECT_EQ (res2.any, true); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0.355,0.645;0,0.29)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.5, 0.5), db::DVector (), 0.005, 1.0); - EXPECT_EQ (res2.first, false); - EXPECT_EQ (res2.second.to_string (), "(0,0;0,0)"); + EXPECT_EQ (res2.any, false); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0,0;0,0)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.005, 0.5), db::DVector (), 0.005, 1.0); - EXPECT_EQ (res2.first, true); - EXPECT_EQ (res2.second.to_string (), "(0,0.5;0.5,0.5)"); + EXPECT_EQ (res2.any, true); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0,0.5;0.5,0.5)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.0, 0.5), db::DVector (), 0.005, 1.0); - EXPECT_EQ (res2.first, false); - EXPECT_EQ (res2.second.to_string (), "(0,0;0,0)"); + EXPECT_EQ (res2.any, false); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0,0;0,0)"); res2 = lay::obj_snap2 (&view, db::DPoint (-0.2, 0.5), db::DVector (), 0.005, 1.0); - EXPECT_EQ (res2.first, false); - EXPECT_EQ (res2.second.to_string (), "(0,0;0,0)"); + EXPECT_EQ (res2.any, false); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0,0;0,0)"); } diff --git a/src/laybasic/unit_tests/unit_tests.pro b/src/laybasic/unit_tests/unit_tests.pro index 0aeab4b3d..d948a1d87 100644 --- a/src/laybasic/unit_tests/unit_tests.pro +++ b/src/laybasic/unit_tests/unit_tests.pro @@ -13,10 +13,10 @@ SOURCES = \ layLayerProperties.cc \ layParsedLayerSource.cc \ layRenderer.cc \ - laySnap.cc \ layNetlistBrowserModelTests.cc \ layNetlistBrowserTreeModelTests.cc \ - layAbstractMenuTests.cc + layAbstractMenuTests.cc \ + laySnapTests.cc INCLUDEPATH += $$TL_INC $$LAYBASIC_INC $$DB_INC $$GSI_INC $$OUT_PWD/../laybasic DEPENDPATH += $$TL_INC $$LAYBASIC_INC $$DB_INC $$GSI_INC $$OUT_PWD/../laybasic From 46b5b87eaf55ca8c4050b03487aaba851c3d2880 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Sep 2020 18:45:58 +0200 Subject: [PATCH 078/113] WIP: refactoring editor options - moved some classes from edt to lay namespace --- src/ant/ant/antPropertiesPage.cc | 37 +--- src/edt/edt/edt.pro | 6 +- src/edt/edt/edtEditorOptionsPages.cc | 203 +++-------------- src/edt/edt/edtEditorOptionsPages.h | 46 +--- src/edt/edt/edtInstPropertiesPage.cc | 49 ++--- src/edt/edt/edtPCellParametersPage.cc | 9 +- src/edt/edt/edtPlugin.cc | 41 ++-- src/edt/edt/edtPlugin.h | 5 +- src/edt/edt/edtPropertiesPageUtils.cc | 19 -- src/edt/edt/edtPropertiesPageUtils.h | 5 - src/edt/edt/edtPropertiesPages.cc | 65 +++--- src/edt/edt/edtRecentConfigurationPage.h | 5 +- src/edt/edt/edtService.h | 2 - src/img/img/imgPropertiesPage.cc | 72 +++---- .../laybasic/layEditorOptionsPage.cc} | 6 +- .../laybasic/layEditorOptionsPage.h} | 14 +- .../laybasic/layEditorOptionsPages.cc | 204 ++++++++++++++++++ src/laybasic/laybasic/layEditorOptionsPages.h | 82 +++++++ src/laybasic/laybasic/layQtTools.cc | 20 ++ src/laybasic/laybasic/layQtTools.h | 13 ++ src/laybasic/laybasic/laybasic.pro | 4 + 21 files changed, 491 insertions(+), 416 deletions(-) rename src/{edt/edt/edtEditorOptionsPage.cc => laybasic/laybasic/layEditorOptionsPage.cc} (94%) rename src/{edt/edt/edtEditorOptionsPage.h => laybasic/laybasic/layEditorOptionsPage.h} (92%) create mode 100644 src/laybasic/laybasic/layEditorOptionsPages.cc create mode 100644 src/laybasic/laybasic/layEditorOptionsPages.h diff --git a/src/ant/ant/antPropertiesPage.cc b/src/ant/ant/antPropertiesPage.cc index f48dec09f..d98d00122 100644 --- a/src/ant/ant/antPropertiesPage.cc +++ b/src/ant/ant/antPropertiesPage.cc @@ -28,27 +28,6 @@ namespace ant { -// ------------------------------------------------------------------------- - -void -indicate_error (QWidget *le, const tl::Exception *ex) -{ - // by the way, update the foreground color of the cell edit box as well (red, if not valid) - QPalette pl = le->palette (); - if (ex) { - pl.setColor (QPalette::Active, QPalette::Text, Qt::red); - pl.setColor (QPalette::Active, QPalette::Base, QColor (Qt::red).lighter (180)); - le->setToolTip (tl::to_qstring (ex->msg ())); - } else { - QWidget *pw = dynamic_cast (le->parent ()); - tl_assert (pw != 0); - pl.setColor (QPalette::Active, QPalette::Text, pw->palette ().color (QPalette::Text)); - pl.setColor (QPalette::Active, QPalette::Base, pw->palette ().color (QPalette::Base)); - le->setToolTip (QString ()); - } - le->setPalette (pl); -} - // ------------------------------------------------------------------------- // PropertiesPage implementation @@ -155,33 +134,33 @@ PropertiesPage::get_points (db::DPoint &p1, db::DPoint &p2) try { tl::from_string (tl::to_string (x1->text ()), dx1); - indicate_error (x1, 0); + lay::indicate_error (x1, 0); } catch (tl::Exception &ex) { - indicate_error (x1, &ex); + lay::indicate_error (x1, &ex); has_error = true; } try { tl::from_string (tl::to_string (x2->text ()), dx2); - indicate_error (x2, 0); + lay::indicate_error (x2, 0); } catch (tl::Exception &ex) { - indicate_error (x2, &ex); + lay::indicate_error (x2, &ex); has_error = true; } try { tl::from_string (tl::to_string (y1->text ()), dy1); - indicate_error (y1, 0); + lay::indicate_error (y1, 0); } catch (tl::Exception &ex) { - indicate_error (y1, &ex); + lay::indicate_error (y1, &ex); has_error = true; } try { tl::from_string (tl::to_string (y2->text ()), dy2); - indicate_error (y2, 0); + lay::indicate_error (y2, 0); } catch (tl::Exception &ex) { - indicate_error (y2, &ex); + lay::indicate_error (y2, &ex); has_error = true; } diff --git a/src/edt/edt/edt.pro b/src/edt/edt/edt.pro index 1c0e3a30f..5515e57c8 100644 --- a/src/edt/edt/edt.pro +++ b/src/edt/edt/edt.pro @@ -22,8 +22,7 @@ HEADERS = \ edtUtils.h \ edtCommon.h \ edtDistribute.h \ - edtRecentConfigurationPage.h \ - edtEditorOptionsPage.h + edtRecentConfigurationPage.h FORMS = \ AlignOptionsDialog.ui \ @@ -62,8 +61,7 @@ SOURCES = \ edtUtils.cc \ gsiDeclEdt.cc \ edtDistribute.cc \ - edtRecentConfigurationPage.cc \ - edtEditorOptionsPage.cc + edtRecentConfigurationPage.cc INCLUDEPATH += $$TL_INC $$GSI_INC $$LAYBASIC_INC $$DB_INC DEPENDPATH += $$TL_INC $$GSI_INC $$LAYBASIC_INC $$DB_INC diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index 1856c7d91..1182bcc42 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -34,6 +34,7 @@ #include "layPlugin.h" #include "layLayoutView.h" #include "layCellSelectionForm.h" +#include "layQtTools.h" #include "ui_EditorOptionsDialog.h" #include "ui_EditorOptionsGeneric.h" #include "ui_EditorOptionsPath.h" @@ -51,153 +52,7 @@ namespace edt { // ------------------------------------------------------------------ -// EditorOptionsPages implementation - -struct EOPCompareOp -{ - bool operator() (edt::EditorOptionsPage *a, edt::EditorOptionsPage *b) const - { - return a->order () < b->order (); - } -}; - -EditorOptionsPages::EditorOptionsPages (QWidget *parent, const std::vector &pages, lay::Dispatcher *dispatcher) - : QFrame (parent), mp_dispatcher (dispatcher) -{ - QVBoxLayout *ly1 = new QVBoxLayout (this); - ly1->setMargin (0); - - mp_pages = new QTabWidget (this); - ly1->addWidget (mp_pages); - - m_pages = pages; - for (std::vector ::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) { - (*p)->set_owner (this); - } - - update (0); - setup (); -} - -EditorOptionsPages::~EditorOptionsPages () -{ - while (m_pages.size () > 0) { - delete m_pages.front (); - } -} - -void -EditorOptionsPages::focusInEvent (QFocusEvent * /*event*/) -{ - // Sends the focus to the current page's last focus owner - if (mp_pages->currentWidget () && mp_pages->currentWidget ()->focusWidget ()) { - mp_pages->currentWidget ()->focusWidget ()->setFocus (); - } -} - -void -EditorOptionsPages::unregister_page (edt::EditorOptionsPage *page) -{ - std::vector pages; - for (std::vector ::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) { - if (*p != page) { - pages.push_back (*p); - } - } - m_pages = pages; - update (0); -} - -void -EditorOptionsPages::activate_page (edt::EditorOptionsPage *page) -{ - try { - if (page->active ()) { - page->setup (mp_dispatcher); - } - } catch (...) { - // catch any errors related to configuration file errors etc. - } - - update (page); -} - -void -EditorOptionsPages::update (edt::EditorOptionsPage *page) -{ - std::vector sorted_pages = m_pages; - std::sort (sorted_pages.begin (), sorted_pages.end (), EOPCompareOp ()); - - if (! page && m_pages.size () > 0) { - page = m_pages.back (); - } - - while (mp_pages->count () > 0) { - mp_pages->removeTab (0); - } - int index = -1; - for (std::vector ::iterator p = sorted_pages.begin (); p != sorted_pages.end (); ++p) { - if ((*p)->active ()) { - if ((*p) == page) { - index = mp_pages->count (); - } - mp_pages->addTab (*p, tl::to_qstring ((*p)->title ())); - } else { - (*p)->setParent (0); - } - } - if (index < 0) { - index = mp_pages->currentIndex (); - } - if (index >= int (mp_pages->count ())) { - index = mp_pages->count () - 1; - } - mp_pages->setCurrentIndex (index); - - setVisible (mp_pages->count () > 0); -} - -void -EditorOptionsPages::setup () -{ - try { - - for (std::vector ::iterator p = m_pages.begin (); p != m_pages.end (); ++p) { - if ((*p)->active ()) { - (*p)->setup (mp_dispatcher); - } - } - - // make the display consistent with the status (this is important for - // PCell parameters where the PCell may be asked to modify the parameters) - do_apply (); - - } catch (...) { - // catch any errors related to configuration file errors etc. - } -} - -void -EditorOptionsPages::do_apply () -{ - for (std::vector ::iterator p = m_pages.begin (); p != m_pages.end (); ++p) { - if ((*p)->active ()) { - // NOTE: we apply to the root dispatcher, so other dispatchers (views) get informed too. - (*p)->apply (mp_dispatcher->dispatcher ()); - } - } -} - -void -EditorOptionsPages::apply () -{ -BEGIN_PROTECTED - do_apply (); -END_PROTECTED_W (this) -} - -// ------------------------------------------------------------------ -// Indicates an error on a line edit +// Configures a value from a line edit template static void configure_from_line_edit (lay::Dispatcher *dispatcher, QLineEdit *le, const std::string &cfg_name) @@ -206,9 +61,9 @@ static void configure_from_line_edit (lay::Dispatcher *dispatcher, QLineEdit *le Value value = Value (0); tl::from_string (tl::to_string (le->text ()), value); dispatcher->config_set (cfg_name, tl::to_string (value)); - indicate_error (le, 0); + lay::indicate_error (le, 0); } catch (tl::Exception &ex) { - indicate_error (le, &ex); + lay::indicate_error (le, &ex); } } @@ -240,17 +95,17 @@ EditorOptionsGeneric::~EditorOptionsGeneric () mp_ui = 0; } -std::string +std::string EditorOptionsGeneric::title () const { return tl::to_string (QObject::tr ("Basic Editing")); } -void +void EditorOptionsGeneric::apply (lay::Dispatcher *root) { // Edit grid - + EditGridConverter egc; if (mp_ui->grid_cb->currentIndex () == 0) { root->config_set (cfg_edit_grid, egc.to_string (db::DVector (-1.0, -1.0))); @@ -260,10 +115,10 @@ EditorOptionsGeneric::apply (lay::Dispatcher *root) try { db::DVector eg; egc.from_string_picky (tl::to_string (mp_ui->edit_grid_le->text ()), eg); - indicate_error (mp_ui->edit_grid_le, 0); + lay::indicate_error (mp_ui->edit_grid_le, 0); root->config_set (cfg_edit_grid, egc.to_string (eg)); } catch (tl::Exception &ex) { - indicate_error (mp_ui->edit_grid_le, &ex); + lay::indicate_error (mp_ui->edit_grid_le, &ex); } } @@ -294,7 +149,7 @@ EditorOptionsGeneric::show_shapes_changed () mp_ui->max_shapes_le->setEnabled (mp_ui->show_shapes_cbx->isChecked ()); } -void +void EditorOptionsGeneric::setup (lay::Dispatcher *root) { // Edit grid @@ -312,13 +167,13 @@ EditorOptionsGeneric::setup (lay::Dispatcher *root) mp_ui->edit_grid_le->setText (tl::to_qstring (egc.to_string (eg))); } grid_changed (mp_ui->grid_cb->currentIndex ()); - indicate_error (mp_ui->edit_grid_le, 0); + lay::indicate_error (mp_ui->edit_grid_le, 0); // edit & move angle ACConverter acc; lay::angle_constraint_type ac; - + ac = lay::AC_Any; root->config_get (cfg_edit_move_angle_mode, ac, acc); mp_ui->move_angle_cb->setCurrentIndex (int (ac)); @@ -342,7 +197,7 @@ EditorOptionsGeneric::setup (lay::Dispatcher *root) unsigned int max_shapes = 1000; root->config_get (cfg_edit_max_shapes_of_instances, max_shapes); mp_ui->max_shapes_le->setText (tl::to_qstring (tl::to_string (max_shapes))); - indicate_error (mp_ui->max_shapes_le, 0); + lay::indicate_error (mp_ui->max_shapes_le, 0); bool show_shapes = true; root->config_get (cfg_edit_show_shapes_of_instances, show_shapes); @@ -353,7 +208,7 @@ EditorOptionsGeneric::setup (lay::Dispatcher *root) // EditorOptionsText implementation EditorOptionsText::EditorOptionsText (lay::Dispatcher *dispatcher) - : EditorOptionsPage (dispatcher) + : lay::EditorOptionsPage (dispatcher) { mp_ui = new Ui::EditorOptionsText (); mp_ui->setupUi (this); @@ -431,7 +286,7 @@ EditorOptionsText::setup (lay::Dispatcher *root) // EditorOptionsPath implementation EditorOptionsPath::EditorOptionsPath (lay::Dispatcher *dispatcher) - : EditorOptionsPage (dispatcher) + : lay::EditorOptionsPage (dispatcher) { mp_ui = new Ui::EditorOptionsPath (); mp_ui->setupUi (this); @@ -502,7 +357,7 @@ EditorOptionsPath::setup (lay::Dispatcher *root) double w = 0.0; root->config_get (cfg_edit_path_width, w); mp_ui->width_le->setText (tl::to_qstring (tl::to_string (w))); - indicate_error (mp_ui->width_le, 0); + lay::indicate_error (mp_ui->width_le, 0); // path type and extensions @@ -523,16 +378,16 @@ EditorOptionsPath::setup (lay::Dispatcher *root) root->config_get (cfg_edit_path_ext_var_begin, bgnext); root->config_get (cfg_edit_path_ext_var_end, endext); mp_ui->start_ext_le->setText (tl::to_qstring (tl::to_string (bgnext))); - indicate_error (mp_ui->start_ext_le, 0); + lay::indicate_error (mp_ui->start_ext_le, 0); mp_ui->end_ext_le->setText (tl::to_qstring (tl::to_string (endext))); - indicate_error (mp_ui->end_ext_le, 0); + lay::indicate_error (mp_ui->end_ext_le, 0); } // ------------------------------------------------------------------ // EditorOptionsInst implementation EditorOptionsInst::EditorOptionsInst (lay::Dispatcher *dispatcher) - : EditorOptionsPage (dispatcher) + : lay::EditorOptionsPage (dispatcher) { mp_ui = new Ui::EditorOptionsInst (); mp_ui->setupUi (this); @@ -623,7 +478,7 @@ EditorOptionsInst::update_cell_edits () // by the way, update the foreground color of the cell edit box as well (red, if not valid) tl::Exception ex ("No cell or PCell with this name"); - indicate_error (mp_ui->cell_le, (! pc.first && ! cc.first) ? &ex : 0); + lay::indicate_error (mp_ui->cell_le, (! pc.first && ! cc.first) ? &ex : 0); } void @@ -766,7 +621,7 @@ EditorOptionsInst::setup (lay::Dispatcher *root) double angle = 0.0; root->config_get (cfg_edit_inst_angle, angle); mp_ui->angle_le->setText (tl::to_qstring (tl::to_string (angle))); - indicate_error (mp_ui->angle_le, 0); + lay::indicate_error (mp_ui->angle_le, 0); bool mirror = false; root->config_get (cfg_edit_inst_mirror, mirror); @@ -775,7 +630,7 @@ EditorOptionsInst::setup (lay::Dispatcher *root) double scale = 1.0; root->config_get (cfg_edit_inst_scale, scale); mp_ui->scale_le->setText (tl::to_qstring (tl::to_string (scale))); - indicate_error (mp_ui->scale_le, 0); + lay::indicate_error (mp_ui->scale_le, 0); // array bool array = false; @@ -792,17 +647,17 @@ EditorOptionsInst::setup (lay::Dispatcher *root) root->config_get (cfg_edit_inst_column_y, column_y); mp_ui->rows_le->setText (tl::to_qstring (tl::to_string (rows))); - indicate_error (mp_ui->rows_le, 0); + lay::indicate_error (mp_ui->rows_le, 0); mp_ui->row_x_le->setText (tl::to_qstring (tl::to_string (row_x))); - indicate_error (mp_ui->row_x_le, 0); + lay::indicate_error (mp_ui->row_x_le, 0); mp_ui->row_y_le->setText (tl::to_qstring (tl::to_string (row_y))); - indicate_error (mp_ui->row_y_le, 0); + lay::indicate_error (mp_ui->row_y_le, 0); mp_ui->columns_le->setText (tl::to_qstring (tl::to_string (columns))); - indicate_error (mp_ui->columns_le, 0); + lay::indicate_error (mp_ui->columns_le, 0); mp_ui->column_x_le->setText (tl::to_qstring (tl::to_string (column_x))); - indicate_error (mp_ui->column_x_le, 0); + lay::indicate_error (mp_ui->column_x_le, 0); mp_ui->column_y_le->setText (tl::to_qstring (tl::to_string (column_y))); - indicate_error (mp_ui->column_y_le, 0); + lay::indicate_error (mp_ui->column_y_le, 0); // place origin of cell flag bool place_origin = false; @@ -814,7 +669,7 @@ EditorOptionsInst::setup (lay::Dispatcher *root) // EditorOptionsInstPCellParam implementation EditorOptionsInstPCellParam::EditorOptionsInstPCellParam (lay::Dispatcher *dispatcher) - : EditorOptionsPage (dispatcher), mp_pcell_parameters (0), mp_placeholder_label (0) + : lay::EditorOptionsPage (dispatcher), mp_pcell_parameters (0), mp_placeholder_label (0) { mp_ui = new Ui::EditorOptionsInstPCellParam (); mp_ui->setupUi (this); diff --git a/src/edt/edt/edtEditorOptionsPages.h b/src/edt/edt/edtEditorOptionsPages.h index 652be507e..3b639c5d8 100644 --- a/src/edt/edt/edtEditorOptionsPages.h +++ b/src/edt/edt/edtEditorOptionsPages.h @@ -24,7 +24,7 @@ #ifndef HDR_edtEditorOptionsPages #define HDR_edtEditorOptionsPages -#include "edtEditorOptionsPage.h" +#include "layEditorOptionsPage.h" #include @@ -59,45 +59,11 @@ namespace edt class PCellParametersPage; -/** - * @brief The object properties dialog - */ -class EditorOptionsPages - : public QFrame -{ -Q_OBJECT - -public: - EditorOptionsPages (QWidget *parent, const std::vector &pages, lay::Dispatcher *root); - ~EditorOptionsPages (); - - void unregister_page (edt::EditorOptionsPage *page); - void activate_page (edt::EditorOptionsPage *page); - void focusInEvent (QFocusEvent *event); - - const std::vector &pages () const - { - return m_pages; - } - -public slots: - void apply (); - void setup (); - -private: - std::vector m_pages; - lay::Dispatcher *mp_dispatcher; - QTabWidget *mp_pages; - - void update (edt::EditorOptionsPage *page); - void do_apply (); -}; - /** * @brief The generic properties page */ class EditorOptionsGeneric - : public EditorOptionsPage + : public lay::EditorOptionsPage { Q_OBJECT @@ -122,7 +88,7 @@ private: * @brief The text properties page */ class EditorOptionsText - : public EditorOptionsPage + : public lay::EditorOptionsPage { public: EditorOptionsText (lay::Dispatcher *dispatcher); @@ -141,7 +107,7 @@ private: * @brief The path properties page */ class EditorOptionsPath - : public EditorOptionsPage + : public lay::EditorOptionsPage { Q_OBJECT @@ -165,7 +131,7 @@ private: * @brief The instance properties page */ class EditorOptionsInst - : public EditorOptionsPage + : public lay::EditorOptionsPage { Q_OBJECT @@ -194,7 +160,7 @@ private: * @brief The instance properties page (PCell parameters) */ class EditorOptionsInstPCellParam - : public EditorOptionsPage + : public lay::EditorOptionsPage { Q_OBJECT diff --git a/src/edt/edt/edtInstPropertiesPage.cc b/src/edt/edt/edtInstPropertiesPage.cc index b3f8cc1c7..5266ff485 100644 --- a/src/edt/edt/edtInstPropertiesPage.cc +++ b/src/edt/edt/edtInstPropertiesPage.cc @@ -31,6 +31,7 @@ #include "layObjectInstPath.h" #include "layLayoutView.h" #include "layCellSelectionForm.h" +#include "layQtTools.h" #include "tlExceptions.h" #include "tlString.h" @@ -404,10 +405,10 @@ InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance & throw tl::Exception (tl::to_string (QObject::tr ("Not a valid cell or PCell name: %s")).c_str (), tl::to_string (cell_name_le->text ()).c_str ()); } - indicate_error (cell_name_le, 0); + lay::indicate_error (cell_name_le, 0); } catch (tl::Exception &ex) { - indicate_error (cell_name_le, &ex); + lay::indicate_error (cell_name_le, &ex); has_error = true; } @@ -446,17 +447,17 @@ InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance & try { tl::from_string (tl::to_string (pos_x_le->text ()), x); - indicate_error (pos_x_le, 0); + lay::indicate_error (pos_x_le, 0); } catch (tl::Exception &ex) { - indicate_error (pos_x_le, &ex); + lay::indicate_error (pos_x_le, &ex); has_error = true; } try { tl::from_string (tl::to_string (pos_y_le->text ()), y); - indicate_error (pos_y_le, 0); + lay::indicate_error (pos_y_le, 0); } catch (tl::Exception &ex) { - indicate_error (pos_y_le, &ex); + lay::indicate_error (pos_y_le, &ex); has_error = true; } @@ -471,18 +472,18 @@ InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance & double angle = 0.0; try { tl::from_string (tl::to_string (angle_le->text ()), angle); - indicate_error (angle_le, 0); + lay::indicate_error (angle_le, 0); } catch (tl::Exception &ex) { - indicate_error (angle_le, &ex); + lay::indicate_error (angle_le, &ex); has_error = true; } double mag = 0.0; try { tl::from_string (tl::to_string (mag_le->text ()), mag); - indicate_error (mag_le, 0); + lay::indicate_error (mag_le, 0); } catch (tl::Exception &ex) { - indicate_error (mag_le, &ex); + lay::indicate_error (mag_le, &ex); has_error = true; } @@ -509,49 +510,49 @@ InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance & try { tl::from_string (tl::to_string (column_x_le->text ()), cx); - indicate_error (column_x_le, 0); + lay::indicate_error (column_x_le, 0); } catch (tl::Exception &ex) { - indicate_error (column_x_le, &ex); + lay::indicate_error (column_x_le, &ex); has_error = true; } try { tl::from_string (tl::to_string (column_y_le->text ()), cy); - indicate_error (column_y_le, 0); + lay::indicate_error (column_y_le, 0); } catch (tl::Exception &ex) { - indicate_error (column_y_le, &ex); + lay::indicate_error (column_y_le, &ex); has_error = true; } try { tl::from_string (tl::to_string (row_x_le->text ()), rx); - indicate_error (row_x_le, 0); + lay::indicate_error (row_x_le, 0); } catch (tl::Exception &ex) { - indicate_error (row_x_le, &ex); + lay::indicate_error (row_x_le, &ex); has_error = true; } try { tl::from_string (tl::to_string (row_y_le->text ()), ry); - indicate_error (row_y_le, 0); + lay::indicate_error (row_y_le, 0); } catch (tl::Exception &ex) { - indicate_error (row_y_le, &ex); + lay::indicate_error (row_y_le, &ex); has_error = true; } try { tl::from_string (tl::to_string (rows_le->text ()), rows); - indicate_error (rows_le, 0); + lay::indicate_error (rows_le, 0); } catch (tl::Exception &ex) { - indicate_error (rows_le, &ex); + lay::indicate_error (rows_le, &ex); has_error = true; } try { tl::from_string (tl::to_string (columns_le->text ()), cols); - indicate_error (columns_le, 0); + lay::indicate_error (columns_le, 0); } catch (tl::Exception &ex) { - indicate_error (columns_le, &ex); + lay::indicate_error (columns_le, &ex); has_error = true; } @@ -763,9 +764,9 @@ InstPropertiesPage::update_pcell_parameters () // indicate an invalid cell name if (! pc.first && ! cc.first) { tl::Exception ex (tl::to_string (QObject::tr ("Not a valid cell or PCell name: %s")).c_str (), tl::to_string (cell_name_le->text ()).c_str ()); - indicate_error (cell_name_le, &ex); + lay::indicate_error (cell_name_le, &ex); } else { - indicate_error (cell_name_le, 0); + lay::indicate_error (cell_name_le, 0); } if (pc.first && layout->pcell_declaration (pc.second)) { diff --git a/src/edt/edt/edtPCellParametersPage.cc b/src/edt/edt/edtPCellParametersPage.cc index f55ce5c28..01b667002 100644 --- a/src/edt/edt/edtPCellParametersPage.cc +++ b/src/edt/edt/edtPCellParametersPage.cc @@ -24,6 +24,7 @@ #include "edtPCellParametersPage.h" #include "edtPropertiesPageUtils.h" #include "layWidgets.h" +#include "layQtTools.h" #include "tlScriptError.h" #include @@ -489,11 +490,11 @@ PCellParametersPage::get_parameters (bool *ok) tl::from_string (tl::to_string (le->text ()), v); parameters.back () = tl::Variant (v); - indicate_error (le, 0); + lay::indicate_error (le, 0); } catch (tl::Exception &ex) { - indicate_error (le, &ex); + lay::indicate_error (le, &ex); edit_error = false; } @@ -513,11 +514,11 @@ PCellParametersPage::get_parameters (bool *ok) tl::from_string (tl::to_string (le->text ()), v); parameters.back () = tl::Variant (v); - indicate_error (le, 0); + lay::indicate_error (le, 0); } catch (tl::Exception &ex) { - indicate_error (le, &ex); + lay::indicate_error (le, &ex); edit_error = false; } diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 18f70c3a5..789bcf601 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -22,6 +22,7 @@ #include "layTipDialog.h" +#include "layEditorOptionsPages.h" #include "edtPlugin.h" #include "edtConfig.h" #include "edtService.h" @@ -43,7 +44,7 @@ edt::RecentConfigurationPage::ConfigurationDescriptor shape_cfg_descriptors[] = }; static -void get_shape_editor_options_pages (std::vector &ret, lay::LayoutView *view, lay::Dispatcher *dispatcher) +void get_shape_editor_options_pages (std::vector &ret, lay::LayoutView *view, lay::Dispatcher *dispatcher) { ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-shape-param", &shape_cfg_descriptors[0], &shape_cfg_descriptors[sizeof (shape_cfg_descriptors) / sizeof (shape_cfg_descriptors[0])])); @@ -68,7 +69,7 @@ edt::RecentConfigurationPage::ConfigurationDescriptor text_cfg_descriptors[] = }; static -void get_text_editor_options_pages (std::vector &ret, lay::LayoutView *view, lay::Dispatcher *dispatcher) +void get_text_editor_options_pages (std::vector &ret, lay::LayoutView *view, lay::Dispatcher *dispatcher) { ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-text-param", &text_cfg_descriptors[0], &text_cfg_descriptors[sizeof (text_cfg_descriptors) / sizeof (text_cfg_descriptors[0])])); @@ -94,7 +95,7 @@ edt::RecentConfigurationPage::ConfigurationDescriptor path_cfg_descriptors[] = }; static -void get_path_editor_options_pages (std::vector &ret, lay::LayoutView *view, lay::Dispatcher *dispatcher) +void get_path_editor_options_pages (std::vector &ret, lay::LayoutView *view, lay::Dispatcher *dispatcher) { ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-path-param", &path_cfg_descriptors[0], &path_cfg_descriptors[sizeof (path_cfg_descriptors) / sizeof (path_cfg_descriptors[0])])); @@ -138,7 +139,7 @@ edt::RecentConfigurationPage::ConfigurationDescriptor inst_cfg_descriptors[] = }; static -void get_inst_editor_options_pages (std::vector &ret, lay::LayoutView *view, lay::Dispatcher *dispatcher) +void get_inst_editor_options_pages (std::vector &ret, lay::LayoutView *view, lay::Dispatcher *dispatcher) { ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-inst-param", &inst_cfg_descriptors[0], &inst_cfg_descriptors[sizeof (inst_cfg_descriptors) / sizeof (inst_cfg_descriptors[0])])); @@ -153,7 +154,7 @@ class PluginDeclaration public: PluginDeclaration (const std::string &title, const std::string &mouse_mode, void (*option_get_f) (std::vector < std::pair > &) = 0, - void (*pages_f) (std::vector &, lay::LayoutView *, lay::Dispatcher *) = 0) + void (*pages_f) (std::vector &, lay::LayoutView *, lay::Dispatcher *) = 0) : m_title (title), m_mouse_mode (mouse_mode), mp_option_get_f (option_get_f), mp_pages_f (pages_f) { // .. nothing yet .. @@ -176,7 +177,7 @@ public: // .. nothing yet .. } - virtual void get_editor_options_pages (std::vector &pages, lay::LayoutView *view, lay::Dispatcher *root) const + virtual void get_editor_options_pages (std::vector &pages, lay::LayoutView *view, lay::Dispatcher *root) const { if (mp_pages_f != 0) { size_t nstart = pages.size (); @@ -211,7 +212,7 @@ private: std::string m_mouse_mode; void (*mp_option_get_f) (std::vector < std::pair > &options); - void (*mp_pages_f) (std::vector &, lay::LayoutView *, lay::Dispatcher *); + void (*mp_pages_f) (std::vector &, lay::LayoutView *, lay::Dispatcher *); }; static tl::RegisteredClass config_decl1 ( @@ -409,7 +410,7 @@ show_editor_options_page (lay::LayoutView *view) return; } - std::vector prop_dialog_pages; + std::vector prop_dialog_pages; EditorOptionsGeneric *generic_opt = new EditorOptionsGeneric (view->dispatcher ()); prop_dialog_pages.push_back (generic_opt); @@ -420,13 +421,13 @@ show_editor_options_page (lay::LayoutView *view) } } - for (std::vector::const_iterator op = prop_dialog_pages.begin (); op != prop_dialog_pages.end (); ++op) { + for (std::vector::const_iterator op = prop_dialog_pages.begin (); op != prop_dialog_pages.end (); ++op) { (*op)->activate (false); } remove_editor_options_page (view); - edt::EditorOptionsPages *pages = new edt::EditorOptionsPages (view->editor_options_frame (), prop_dialog_pages, view); + lay::EditorOptionsPages *pages = new lay::EditorOptionsPages (view->editor_options_frame (), prop_dialog_pages, view); view->editor_options_frame ()->layout ()->addWidget (pages); view->editor_options_frame ()->setFocusProxy (pages); } @@ -447,13 +448,13 @@ remove_editor_options_page (lay::LayoutView *view) } static -edt::EditorOptionsPages *get_pages_widget (lay::LayoutView *view) +lay::EditorOptionsPages *get_pages_widget (lay::LayoutView *view) { // TODO: is there a better way to find the editor options pages? - edt::EditorOptionsPages *eo_pages = 0; + lay::EditorOptionsPages *eo_pages = 0; QObjectList children = view->editor_options_frame ()->children (); for (QObjectList::iterator c = children.begin (); c != children.end () && !eo_pages; ++c) { - eo_pages = dynamic_cast (*c); + eo_pages = dynamic_cast (*c); } return eo_pages; @@ -462,13 +463,13 @@ edt::EditorOptionsPages *get_pages_widget (lay::LayoutView *view) void activate_service (lay::LayoutView *view, const lay::PluginDeclaration *pd, bool active) { - edt::EditorOptionsPages *eo_pages = get_pages_widget (view); + lay::EditorOptionsPages *eo_pages = get_pages_widget (view); if (!eo_pages) { return; } // TODO: this is very inefficient as each "activate" will regenerate the tabs - for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { + for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { (*op)->activate (((*op)->plugin_declaration () == pd || ! (*op)->plugin_declaration ()) && active); } } @@ -476,12 +477,12 @@ activate_service (lay::LayoutView *view, const lay::PluginDeclaration *pd, bool void setup_pages (lay::LayoutView *view) { - edt::EditorOptionsPages *eo_pages = get_pages_widget (view); + lay::EditorOptionsPages *eo_pages = get_pages_widget (view); if (!eo_pages) { return; } - for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { + for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { (*op)->setup (view); } } @@ -489,12 +490,12 @@ setup_pages (lay::LayoutView *view) void commit_recent (lay::LayoutView *view) { - edt::EditorOptionsPages *eo_pages = get_pages_widget (view); + lay::EditorOptionsPages *eo_pages = get_pages_widget (view); if (!eo_pages) { return; } - for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { + for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { if ((*op)->active ()) { (*op)->commit_recent (view); } @@ -516,7 +517,7 @@ public: // .. nothing yet .. } - virtual void get_editor_options_pages (std::vector & /*pages*/, lay::LayoutView * /*view*/, lay::Dispatcher * /*root*/) const + virtual void get_editor_options_pages (std::vector & /*pages*/, lay::LayoutView * /*view*/, lay::Dispatcher * /*root*/) const { // .. no specific ones .. } diff --git a/src/edt/edt/edtPlugin.h b/src/edt/edt/edtPlugin.h index 745f4c8a7..2c1c097bf 100644 --- a/src/edt/edt/edtPlugin.h +++ b/src/edt/edt/edtPlugin.h @@ -31,12 +31,11 @@ namespace lay { class Dispatcher; + class EditorOptionsPage; } namespace edt { - class EditorOptionsPage; - /** * @brief A helper class for plugin declarations for editor services */ @@ -44,7 +43,7 @@ namespace edt : public lay::PluginDeclaration { public: - virtual void get_editor_options_pages (std::vector &, lay::LayoutView *, lay::Dispatcher *) const = 0; + virtual void get_editor_options_pages (std::vector &, lay::LayoutView *, lay::Dispatcher *) const = 0; }; /** diff --git a/src/edt/edt/edtPropertiesPageUtils.cc b/src/edt/edt/edtPropertiesPageUtils.cc index 801d0c57e..62fe988dc 100644 --- a/src/edt/edt/edtPropertiesPageUtils.cc +++ b/src/edt/edt/edtPropertiesPageUtils.cc @@ -786,24 +786,5 @@ coords_to_string (const db::DPoint &dp, double dbu, bool du, const char *sep) return coord_to_string (dp.x (), dbu, du) + sep + coord_to_string (dp.y (), dbu, du); } -void -indicate_error (QWidget *le, const tl::Exception *ex) -{ - // by the way, update the foreground color of the cell edit box as well (red, if not valid) - QPalette pl = le->palette (); - if (ex) { - pl.setColor (QPalette::Active, QPalette::Text, Qt::red); - pl.setColor (QPalette::Active, QPalette::Base, QColor (Qt::red).lighter (180)); - le->setToolTip (tl::to_qstring (ex->msg ())); - } else { - QWidget *pw = dynamic_cast (le->parent ()); - tl_assert (pw != 0); - pl.setColor (QPalette::Active, QPalette::Text, pw->palette ().color (QPalette::Text)); - pl.setColor (QPalette::Active, QPalette::Base, pw->palette ().color (QPalette::Base)); - le->setToolTip (QString ()); - } - le->setPalette (pl); -} - } diff --git a/src/edt/edt/edtPropertiesPageUtils.h b/src/edt/edt/edtPropertiesPageUtils.h index 74a33b672..1e71ff5cd 100644 --- a/src/edt/edt/edtPropertiesPageUtils.h +++ b/src/edt/edt/edtPropertiesPageUtils.h @@ -480,11 +480,6 @@ db::Point point_from_dpoint (const db::DPoint &dp, double dbu, bool du, const db */ db::Coord coord_from_string (const char *txt, double dbu, bool du, const db::VCplxTrans &t); -/** - * @brief Configures a QLineEdit to indicate a format error - */ -void indicate_error (QWidget *le, const tl::Exception *ex); - } #endif diff --git a/src/edt/edt/edtPropertiesPages.cc b/src/edt/edt/edtPropertiesPages.cc index 2cf797f2e..ca645bcec 100644 --- a/src/edt/edt/edtPropertiesPages.cc +++ b/src/edt/edt/edtPropertiesPages.cc @@ -28,6 +28,7 @@ #include "layDialogs.h" #include "layObjectInstPath.h" #include "layLayoutView.h" +#include "layQtTools.h" #include "tlExceptions.h" #include "tlString.h" @@ -508,10 +509,10 @@ PolygonPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Sha } - indicate_error (pointListEdit, 0); + lay::indicate_error (pointListEdit, 0); } catch (tl::Exception &ex) { - indicate_error (pointListEdit, &ex); + lay::indicate_error (pointListEdit, &ex); throw; } @@ -608,33 +609,33 @@ BoxPropertiesPage::get_box (int mode) const try { tl::from_string (tl::to_string (x1_le_1->text ()), x1); - indicate_error (x1_le_1, 0); + lay::indicate_error (x1_le_1, 0); } catch (tl::Exception &ex) { - indicate_error (x1_le_1, &ex); + lay::indicate_error (x1_le_1, &ex); has_error = true; } try { tl::from_string (tl::to_string (y1_le_1->text ()), y1); - indicate_error (y1_le_1, 0); + lay::indicate_error (y1_le_1, 0); } catch (tl::Exception &ex) { - indicate_error (y1_le_1, &ex); + lay::indicate_error (y1_le_1, &ex); has_error = true; } try { tl::from_string (tl::to_string (x2_le_1->text ()), x2); - indicate_error (x2_le_1, 0); + lay::indicate_error (x2_le_1, 0); } catch (tl::Exception &ex) { - indicate_error (x2_le_1, &ex); + lay::indicate_error (x2_le_1, &ex); has_error = true; } try { tl::from_string (tl::to_string (y2_le_1->text ()), y2); - indicate_error (y2_le_1, 0); + lay::indicate_error (y2_le_1, 0); } catch (tl::Exception &ex) { - indicate_error (y2_le_1, &ex); + lay::indicate_error (y2_le_1, &ex); has_error = true; } @@ -668,33 +669,33 @@ BoxPropertiesPage::get_box (int mode) const try { tl::from_string (tl::to_string (cx_le_2->text ()), cx); - indicate_error (cx_le_2, 0); + lay::indicate_error (cx_le_2, 0); } catch (tl::Exception &ex) { - indicate_error (cx_le_2, &ex); + lay::indicate_error (cx_le_2, &ex); has_error = true; } try { tl::from_string (tl::to_string (cy_le_2->text ()), cy); - indicate_error (cy_le_2, 0); + lay::indicate_error (cy_le_2, 0); } catch (tl::Exception &ex) { - indicate_error (cy_le_2, &ex); + lay::indicate_error (cy_le_2, &ex); has_error = true; } try { tl::from_string (tl::to_string (w_le_2->text ()), w); - indicate_error (w_le_2, 0); + lay::indicate_error (w_le_2, 0); } catch (tl::Exception &ex) { - indicate_error (w_le_2, &ex); + lay::indicate_error (w_le_2, &ex); has_error = true; } try { tl::from_string (tl::to_string (h_le_2->text ()), h); - indicate_error (h_le_2, 0); + lay::indicate_error (h_le_2, 0); } catch (tl::Exception &ex) { - indicate_error (h_le_2, &ex); + lay::indicate_error (h_le_2, &ex); has_error = true; } @@ -833,17 +834,17 @@ TextPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape try { tl::from_string (tl::to_string (x_le->text ()), x); - indicate_error (x_le, 0); + lay::indicate_error (x_le, 0); } catch (tl::Exception &ex) { - indicate_error (x_le, &ex); + lay::indicate_error (x_le, &ex); has_error = true; } try { tl::from_string (tl::to_string (y_le->text ()), y); - indicate_error (y_le, 0); + lay::indicate_error (y_le, 0); } catch (tl::Exception &ex) { - indicate_error (y_le, &ex); + lay::indicate_error (y_le, &ex); has_error = true; } @@ -874,9 +875,9 @@ TextPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape if (! size_le->text ().isEmpty ()) { try { size = coord_from_string (tl::to_string (size_le->text ()).c_str (), dbu, du, t); - indicate_error (size_le, 0); + lay::indicate_error (size_le, 0); } catch (tl::Exception &ex) { - indicate_error (size_le, &ex); + lay::indicate_error (size_le, &ex); has_error = true; } } @@ -1083,19 +1084,19 @@ EditablePathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db throw tl::Exception (tl::to_string (QObject::tr ("The path must have at least one point"))); } - indicate_error (ptlist_le, 0); + lay::indicate_error (ptlist_le, 0); } catch (tl::Exception &ex) { - indicate_error (ptlist_le, &ex); + lay::indicate_error (ptlist_le, &ex); has_error = true; } db::Coord w = 0; try { w = coord_from_string (tl::to_string (width_le->text ()).c_str (), dbu, du, t); - indicate_error (width_le, 0); + lay::indicate_error (width_le, 0); } catch (tl::Exception &ex) { - indicate_error (width_le, &ex); + lay::indicate_error (width_le, &ex); has_error = true; } @@ -1110,16 +1111,16 @@ EditablePathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db case 2: // variable try { se = coord_from_string (tl::to_string (start_ext_le->text ()).c_str (), dbu, du, t); - indicate_error (start_ext_le, 0); + lay::indicate_error (start_ext_le, 0); } catch (tl::Exception &ex) { - indicate_error (start_ext_le, &ex); + lay::indicate_error (start_ext_le, &ex); has_error = true; } try { ee = coord_from_string (tl::to_string (end_ext_le->text ()).c_str (), dbu, du, t); - indicate_error (end_ext_le, 0); + lay::indicate_error (end_ext_le, 0); } catch (tl::Exception &ex) { - indicate_error (end_ext_le, &ex); + lay::indicate_error (end_ext_le, &ex); has_error = true; } break; diff --git a/src/edt/edt/edtRecentConfigurationPage.h b/src/edt/edt/edtRecentConfigurationPage.h index 6f4adaea2..a177be7fb 100644 --- a/src/edt/edt/edtRecentConfigurationPage.h +++ b/src/edt/edt/edtRecentConfigurationPage.h @@ -24,7 +24,7 @@ #ifndef HDR_edtRecentConfigurationPage #define HDR_edtRecentConfigurationPage -#include "edtEditorOptionsPage.h" +#include "layEditorOptionsPage.h" #include "tlObject.h" #include @@ -46,7 +46,8 @@ class EditorOptionsPages; * @brief The base class for a object properties page */ class RecentConfigurationPage - : public EditorOptionsPage, public tl::Object + : public lay::EditorOptionsPage, + public tl::Object { Q_OBJECT diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h index 4d96adfa7..7f2320f02 100644 --- a/src/edt/edt/edtService.h +++ b/src/edt/edt/edtService.h @@ -52,8 +52,6 @@ namespace edt { class Service; class PluginDeclarationBase; -class EditorOptionsPages; -class EditorOptionsPage; // ------------------------------------------------------------- diff --git a/src/img/img/imgPropertiesPage.cc b/src/img/img/imgPropertiesPage.cc index ddd9298e7..e16d1291d 100644 --- a/src/img/img/imgPropertiesPage.cc +++ b/src/img/img/imgPropertiesPage.cc @@ -26,6 +26,7 @@ #include "imgStream.h" #include "layLayoutView.h" #include "layFileDialog.h" +#include "layQtTools.h" #include "tlExceptions.h" #include "tlFileUtils.h" @@ -35,27 +36,6 @@ namespace img const double min_gamma = 0.3; const double max_gamma = 3.0; -// ------------------------------------------------------------------------- - -void -indicate_error (QWidget *le, const tl::Exception *ex) -{ - // by the way, update the foreground color of the cell edit box as well (red, if not valid) - QPalette pl = le->palette (); - if (ex) { - pl.setColor (QPalette::Active, QPalette::Text, Qt::red); - pl.setColor (QPalette::Active, QPalette::Base, QColor (Qt::red).lighter (180)); - le->setToolTip (tl::to_qstring (ex->msg ())); - } else { - QWidget *pw = dynamic_cast (le->parent ()); - tl_assert (pw != 0); - pl.setColor (QPalette::Active, QPalette::Text, pw->palette ().color (QPalette::Text)); - pl.setColor (QPalette::Active, QPalette::Base, pw->palette ().color (QPalette::Base)); - le->setToolTip (QString ()); - } - le->setPalette (pl); -} - // ------------------------------------------------------------------------- // PropertiesPage implementation @@ -239,24 +219,24 @@ PropertiesPage::get_xmin_xmax (double &xmin, double &xmax, bool &has_error_out) try { tl::from_string (tl::to_string (from_le->text ()), xmin); - indicate_error (from_le, 0); + lay::indicate_error (from_le, 0); } catch (tl::Exception &ex) { - indicate_error (from_le, &ex); + lay::indicate_error (from_le, &ex); has_error = true; } try { tl::from_string (tl::to_string (to_le->text ()), xmax); - indicate_error (to_le, 0); + lay::indicate_error (to_le, 0); } catch (tl::Exception &ex) { - indicate_error (to_le, &ex); + lay::indicate_error (to_le, &ex); has_error = true; } if (! has_error && xmin >= xmax) { tl::Exception ex (tl::to_string (QObject::tr ("Invalid data value range (min. value must be less than max. value)"))); - indicate_error (from_le, &ex); - indicate_error (to_le, &ex); + lay::indicate_error (from_le, &ex); + lay::indicate_error (to_le, &ex); has_error = true; } @@ -356,16 +336,16 @@ PropertiesPage::value_changed () double x = 0.0; try { tl::from_string (tl::to_string (value_le->text ()), x); - indicate_error (value_le, 0); + lay::indicate_error (value_le, 0); } catch (tl::Exception &ex) { - indicate_error (value_le, &ex); + lay::indicate_error (value_le, &ex); has_error = true; } xx = (x - xmin) / (xmax - xmin); if (! has_error && (xx < 0 || xx > 1.0)) { tl::Exception ex (tl::to_string (QObject::tr ("The position entered (%g) must be between the minimum (%g) and maximum (%g) value")), x, xmin, xmax); - indicate_error (value_le, &ex); + lay::indicate_error (value_le, &ex); has_error = true; } @@ -825,41 +805,41 @@ PropertiesPage::apply () if (w <= 0.0 || h <= 0.0) { throw tl::Exception (tl::to_string (QObject::tr ("Pixel width or height must be positive, non-null values"))); } - indicate_error (width_le, 0); + lay::indicate_error (width_le, 0); } catch (tl::Exception &ex) { - indicate_error (width_le, &ex); + lay::indicate_error (width_le, &ex); has_error = true; } try { tl::from_string (tl::to_string (height_le->text ()), h); - indicate_error (height_le, 0); + lay::indicate_error (height_le, 0); } catch (tl::Exception &ex) { - indicate_error (height_le, &ex); + lay::indicate_error (height_le, &ex); has_error = true; } try { tl::from_string (tl::to_string (x_offset_le->text ()), x); - indicate_error (x_offset_le, 0); + lay::indicate_error (x_offset_le, 0); } catch (tl::Exception &ex) { - indicate_error (x_offset_le, &ex); + lay::indicate_error (x_offset_le, &ex); has_error = true; } try { tl::from_string (tl::to_string (y_offset_le->text ()), y); - indicate_error (y_offset_le, 0); + lay::indicate_error (y_offset_le, 0); } catch (tl::Exception &ex) { - indicate_error (y_offset_le, &ex); + lay::indicate_error (y_offset_le, &ex); has_error = true; } try { tl::from_string (tl::to_string (angle_le->text ()), a); - indicate_error (angle_le, 0); + lay::indicate_error (angle_le, 0); } catch (tl::Exception &ex) { - indicate_error (angle_le, &ex); + lay::indicate_error (angle_le, &ex); has_error = true; } @@ -868,9 +848,9 @@ PropertiesPage::apply () if (sa <= -45 || sa >= 45) { throw tl::Exception (tl::to_string (QObject::tr ("The shear angle must be larger than -45 and less than 45 degree"))); } - indicate_error (shear_le, 0); + lay::indicate_error (shear_le, 0); } catch (tl::Exception &ex) { - indicate_error (shear_le, &ex); + lay::indicate_error (shear_le, &ex); has_error = true; } @@ -879,9 +859,9 @@ PropertiesPage::apply () if (tx <= -90 || tx >= 90) { throw tl::Exception (tl::to_string (QObject::tr ("The perspective tilt angles must be larger than -90 and less than 90 degree"))); } - indicate_error (persp_tx_le, 0); + lay::indicate_error (persp_tx_le, 0); } catch (tl::Exception &ex) { - indicate_error (persp_tx_le, &ex); + lay::indicate_error (persp_tx_le, &ex); has_error = true; } @@ -890,9 +870,9 @@ PropertiesPage::apply () if (ty <= -90 || ty >= 90) { throw tl::Exception (tl::to_string (QObject::tr ("The perspective tilt angles must be larger than -90 and less than 90 degree"))); } - indicate_error (persp_ty_le, 0); + lay::indicate_error (persp_ty_le, 0); } catch (tl::Exception &ex) { - indicate_error (persp_ty_le, &ex); + lay::indicate_error (persp_ty_le, &ex); has_error = true; } diff --git a/src/edt/edt/edtEditorOptionsPage.cc b/src/laybasic/laybasic/layEditorOptionsPage.cc similarity index 94% rename from src/edt/edt/edtEditorOptionsPage.cc rename to src/laybasic/laybasic/layEditorOptionsPage.cc index 92c61e469..cc4a92d91 100644 --- a/src/edt/edt/edtEditorOptionsPage.cc +++ b/src/laybasic/laybasic/layEditorOptionsPage.cc @@ -22,10 +22,10 @@ #include "tlInternational.h" -#include "edtEditorOptionsPage.h" -#include "edtEditorOptionsPages.h" +#include "layEditorOptionsPage.h" +#include "layEditorOptionsPages.h" -namespace edt +namespace lay { // ------------------------------------------------------------------ diff --git a/src/edt/edt/edtEditorOptionsPage.h b/src/laybasic/laybasic/layEditorOptionsPage.h similarity index 92% rename from src/edt/edt/edtEditorOptionsPage.h rename to src/laybasic/laybasic/layEditorOptionsPage.h index 4d52beb46..3a2692a89 100644 --- a/src/edt/edt/edtEditorOptionsPage.h +++ b/src/laybasic/laybasic/layEditorOptionsPage.h @@ -20,21 +20,17 @@ */ -#ifndef HDR_edtEditorOptionsPage -#define HDR_edtEditorOptionsPage +#ifndef HDR_layEditorOptionsPage +#define HDR_layEditorOptionsPage #include namespace lay -{ - class PluginDeclaration; - class Dispatcher; - class Plugin; -} - -namespace edt { +class PluginDeclaration; +class Dispatcher; +class Plugin; class EditorOptionsPages; /** diff --git a/src/laybasic/laybasic/layEditorOptionsPages.cc b/src/laybasic/laybasic/layEditorOptionsPages.cc new file mode 100644 index 000000000..fd65eb472 --- /dev/null +++ b/src/laybasic/laybasic/layEditorOptionsPages.cc @@ -0,0 +1,204 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "tlInternational.h" +#include "layEditorOptionsPages.h" +#include "tlExceptions.h" +#include "layPlugin.h" +#include "layLayoutView.h" +#include "layQtTools.h" +#include "ui_EditorOptionsGeneric.h" + +#include +#include +#include +#include +#include + +namespace lay +{ + +// ------------------------------------------------------------------ +// EditorOptionsPages implementation + +struct EOPCompareOp +{ + bool operator() (lay::EditorOptionsPage *a, lay::EditorOptionsPage *b) const + { + return a->order () < b->order (); + } +}; + +EditorOptionsPages::EditorOptionsPages (QWidget *parent, const std::vector &pages, lay::Dispatcher *dispatcher) + : QFrame (parent), mp_dispatcher (dispatcher) +{ + QVBoxLayout *ly1 = new QVBoxLayout (this); + ly1->setMargin (0); + + mp_pages = new QTabWidget (this); + ly1->addWidget (mp_pages); + + m_pages = pages; + for (std::vector ::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) { + (*p)->set_owner (this); + } + + update (0); + setup (); +} + +EditorOptionsPages::~EditorOptionsPages () +{ + while (m_pages.size () > 0) { + delete m_pages.front (); + } +} + +void +EditorOptionsPages::focusInEvent (QFocusEvent * /*event*/) +{ + // Sends the focus to the current page's last focus owner + if (mp_pages->currentWidget () && mp_pages->currentWidget ()->focusWidget ()) { + mp_pages->currentWidget ()->focusWidget ()->setFocus (); + } +} + +void +EditorOptionsPages::unregister_page (lay::EditorOptionsPage *page) +{ + std::vector pages; + for (std::vector ::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) { + if (*p != page) { + pages.push_back (*p); + } + } + m_pages = pages; + update (0); +} + +void +EditorOptionsPages::activate_page (lay::EditorOptionsPage *page) +{ + try { + if (page->active ()) { + page->setup (mp_dispatcher); + } + } catch (...) { + // catch any errors related to configuration file errors etc. + } + + update (page); +} + +void +EditorOptionsPages::update (lay::EditorOptionsPage *page) +{ + std::vector sorted_pages = m_pages; + std::sort (sorted_pages.begin (), sorted_pages.end (), EOPCompareOp ()); + + if (! page && m_pages.size () > 0) { + page = m_pages.back (); + } + + while (mp_pages->count () > 0) { + mp_pages->removeTab (0); + } + int index = -1; + for (std::vector ::iterator p = sorted_pages.begin (); p != sorted_pages.end (); ++p) { + if ((*p)->active ()) { + if ((*p) == page) { + index = mp_pages->count (); + } + mp_pages->addTab (*p, tl::to_qstring ((*p)->title ())); + } else { + (*p)->setParent (0); + } + } + if (index < 0) { + index = mp_pages->currentIndex (); + } + if (index >= int (mp_pages->count ())) { + index = mp_pages->count () - 1; + } + mp_pages->setCurrentIndex (index); + + setVisible (mp_pages->count () > 0); +} + +void +EditorOptionsPages::setup () +{ + try { + + for (std::vector ::iterator p = m_pages.begin (); p != m_pages.end (); ++p) { + if ((*p)->active ()) { + (*p)->setup (mp_dispatcher); + } + } + + // make the display consistent with the status (this is important for + // PCell parameters where the PCell may be asked to modify the parameters) + do_apply (); + + } catch (...) { + // catch any errors related to configuration file errors etc. + } +} + +void +EditorOptionsPages::do_apply () +{ + for (std::vector ::iterator p = m_pages.begin (); p != m_pages.end (); ++p) { + if ((*p)->active ()) { + // NOTE: we apply to the root dispatcher, so other dispatchers (views) get informed too. + (*p)->apply (mp_dispatcher->dispatcher ()); + } + } +} + +void +EditorOptionsPages::apply () +{ +BEGIN_PROTECTED + do_apply (); +END_PROTECTED_W (this) +} + +// ------------------------------------------------------------------ +// Indicates an error on a line edit + +template +static void configure_from_line_edit (lay::Dispatcher *dispatcher, QLineEdit *le, const std::string &cfg_name) +{ + try { + Value value = Value (0); + tl::from_string (tl::to_string (le->text ()), value); + dispatcher->config_set (cfg_name, tl::to_string (value)); + lay::indicate_error (le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (le, &ex); + } +} + +} + diff --git a/src/laybasic/laybasic/layEditorOptionsPages.h b/src/laybasic/laybasic/layEditorOptionsPages.h new file mode 100644 index 000000000..5c0c309df --- /dev/null +++ b/src/laybasic/laybasic/layEditorOptionsPages.h @@ -0,0 +1,82 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_layEditorOptionsPages +#define HDR_layEditorOptionsPages + +#include "layEditorOptionsPage.h" + +#include + +#include +#include +#include + +class QTabWidget; +class QLabel; + +namespace lay +{ + +class PluginDeclaration; +class Dispatcher; +class Plugin; + +/** + * @brief The object properties dialog + */ +class EditorOptionsPages + : public QFrame +{ +Q_OBJECT + +public: + EditorOptionsPages (QWidget *parent, const std::vector &pages, lay::Dispatcher *root); + ~EditorOptionsPages (); + + void unregister_page (lay::EditorOptionsPage *page); + void activate_page (lay::EditorOptionsPage *page); + void focusInEvent (QFocusEvent *event); + + const std::vector &pages () const + { + return m_pages; + } + +public slots: + void apply (); + void setup (); + +private: + std::vector m_pages; + lay::Dispatcher *mp_dispatcher; + QTabWidget *mp_pages; + + void update (lay::EditorOptionsPage *page); + void do_apply (); +}; + +} + +#endif + diff --git a/src/laybasic/laybasic/layQtTools.cc b/src/laybasic/laybasic/layQtTools.cc index 4d174ade4..96d44fe05 100644 --- a/src/laybasic/laybasic/layQtTools.cc +++ b/src/laybasic/laybasic/layQtTools.cc @@ -31,6 +31,7 @@ #include #include #include +#include #include @@ -159,5 +160,24 @@ restore_dialog_state (QWidget *dialog, const std::string &s, bool with_section_s } } +void +indicate_error (QWidget *le, const tl::Exception *ex) +{ + // by the way, update the foreground color of the cell edit box as well (red, if not valid) + QPalette pl = le->palette (); + if (ex) { + pl.setColor (QPalette::Active, QPalette::Text, Qt::red); + pl.setColor (QPalette::Active, QPalette::Base, QColor (Qt::red).lighter (180)); + le->setToolTip (tl::to_qstring (ex->msg ())); + } else { + QWidget *pw = dynamic_cast (le->parent ()); + tl_assert (pw != 0); + pl.setColor (QPalette::Active, QPalette::Text, pw->palette ().color (QPalette::Text)); + pl.setColor (QPalette::Active, QPalette::Base, pw->palette ().color (QPalette::Base)); + le->setToolTip (QString ()); + } + le->setPalette (pl); +} + } diff --git a/src/laybasic/laybasic/layQtTools.h b/src/laybasic/laybasic/layQtTools.h index 36afc666c..8211f2fc8 100644 --- a/src/laybasic/laybasic/layQtTools.h +++ b/src/laybasic/laybasic/layQtTools.h @@ -32,6 +32,11 @@ class QLabel; class QWidget; class QObject; +namespace tl +{ + class Exception; +} + namespace lay { @@ -62,6 +67,14 @@ LAYBASIC_PUBLIC void activate_modal_help_links (QLabel *label); */ LAYBASIC_PUBLIC void register_help_handler (QObject *object, const char *slot, const char *modal_slot); +/** + * @brief Configures a QLineEdit or other widget to indicate an error + * + * When a non-null ex pointer is passed, the background will be turned red + * and the exception's text will be used as tooltip. Use this function with + * a null ex pointer to clear the error condition. + */ +void indicate_error (QWidget *le, const tl::Exception *ex); } // namespace lay diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index 95d6f4f00..6bc887d5f 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -115,6 +115,8 @@ SOURCES = \ layEditable.cc \ layEditStipplesForm.cc \ layEditStippleWidget.cc \ + layEditorOptionsPage.cc \ + layEditorOptionsPages.cc \ layFileDialog.cc \ layFinder.cc \ layFixedFont.cc \ @@ -216,6 +218,8 @@ HEADERS = \ layEditable.h \ layEditStipplesForm.h \ layEditStippleWidget.h \ + layEditorOptionsPage.h \ + layEditorOptionsPages.h \ layFileDialog.h \ layFinder.h \ layFixedFont.h \ From a9a9f88f92e890ca96f8f3af185925644b21ce00 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Sep 2020 21:10:26 +0200 Subject: [PATCH 079/113] WIP: further reworking. --- src/edt/edt/edtMainService.cc | 15 --- src/edt/edt/edtMainService.h | 10 -- src/edt/edt/edtPartialService.cc | 12 +-- src/edt/edt/edtPlugin.cc | 95 ++----------------- src/edt/edt/edtPlugin.h | 25 +---- src/edt/edt/edtService.cc | 11 --- src/lay/lay/layMainWindow.cc | 11 ++- .../laybasic/layEditorOptionsFrame.cc | 70 ++++++++++++++ src/laybasic/laybasic/layEditorOptionsFrame.h | 55 +++++++++++ src/laybasic/laybasic/layEditorOptionsPage.h | 4 +- .../laybasic/layEditorOptionsPages.cc | 11 +++ src/laybasic/laybasic/layEditorOptionsPages.h | 5 +- src/laybasic/laybasic/layLayoutView.cc | 64 +++++++++++-- src/laybasic/laybasic/layLayoutView.h | 25 +++-- src/laybasic/laybasic/layPlugin.h | 26 +++-- src/laybasic/laybasic/layQtTools.h | 2 +- src/laybasic/laybasic/laybasic.pro | 2 + 17 files changed, 259 insertions(+), 184 deletions(-) create mode 100644 src/laybasic/laybasic/layEditorOptionsFrame.cc create mode 100644 src/laybasic/laybasic/layEditorOptionsFrame.h diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index b2b522623..29984d719 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -59,7 +59,6 @@ MainService::MainService (db::Manager *manager, lay::LayoutView *view, lay::Disp : lay::Plugin (view), lay::Editable (view), db::Object (manager), - dm_setup_pages (this, &MainService::do_setup_pages), mp_view (view), mp_root (root), m_needs_update (false), @@ -2357,20 +2356,6 @@ private: db::cell_index_type m_topcell; }; -void -MainService::config_finalize () -{ - // It's important that the editor option pages are updated last - because the - // configuration change may trigger other configuration changes - dm_setup_pages (); -} - -void -MainService::do_setup_pages () -{ - setup_pages (view ()); -} - void MainService::paste () { diff --git a/src/edt/edt/edtMainService.h b/src/edt/edt/edtMainService.h index 2b22cefee..c2f5c97ec 100644 --- a/src/edt/edt/edtMainService.h +++ b/src/edt/edt/edtMainService.h @@ -194,16 +194,7 @@ public: */ virtual void paste (); - /** - * @brief Implementation of "config_finalize" - * This implementation will update the editor option page's parameters. - */ - virtual void config_finalize (); - private: - // The deferred execution handler for the config_finalize event - tl::DeferredMethod dm_setup_pages; - // The layout view that this service is attached to lay::LayoutView *mp_view; lay::Dispatcher *mp_root; @@ -245,7 +236,6 @@ private: lay::FlattenInstOptionsDialog *flatten_inst_options_dialog (); edt::MakeCellOptionsDialog *make_cell_options_dialog (); edt::MakeArrayOptionsDialog *make_array_options_dialog (); - void do_setup_pages (); }; } diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index 31254ae4d..989c4ae12 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -1062,25 +1062,15 @@ PartialService::move_ac () const void PartialService::deactivated () { - // make all editor option pages visible - activate_service (view (), plugin_declaration (), false); - // clear selection when this mode is left partial_select (db::DBox (), lay::Editable::Reset); clear_partial_transient_selection (); - remove_editor_options_page (view ()); } void PartialService::activated () { - if (view ()->is_editable ()) { - // Show editor options panel - show_editor_options_page (view ()); - } - - // make all editor option pages visible - activate_service (view (), plugin_declaration (), true); + // .. nothing yet .. } void diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 789bcf601..b6596218f 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -319,12 +319,19 @@ public: { return false; } - + virtual bool implements_mouse_mode (std::string & /*title*/) const { return false; } + virtual void get_editor_options_pages (std::vector &pages, lay::LayoutView * /*view*/, lay::Dispatcher *dispatcher) const + { + // NOTE: we do not set plugin_declaration which makes the page unspecific + EditorOptionsGeneric *generic_opt = new EditorOptionsGeneric (dispatcher); + pages.push_back (generic_opt); + } + virtual void initialize (lay::Dispatcher *root) { lay::Dispatcher *mp = lay::Dispatcher::instance (); @@ -403,94 +410,10 @@ private: static tl::RegisteredClass config_decl_main (new edt::MainPluginDeclaration (tl::to_string (QObject::tr ("Instances and shapes"))), 4000, "edt::MainService"); -void -show_editor_options_page (lay::LayoutView *view) -{ - if (! view->editor_options_frame ()) { - return; - } - - std::vector prop_dialog_pages; - EditorOptionsGeneric *generic_opt = new EditorOptionsGeneric (view->dispatcher ()); - prop_dialog_pages.push_back (generic_opt); - - for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { - const PluginDeclarationBase *pd_base = dynamic_cast (&*cls); - if (pd_base) { - pd_base->get_editor_options_pages (prop_dialog_pages, view, view->dispatcher ()); - } - } - - for (std::vector::const_iterator op = prop_dialog_pages.begin (); op != prop_dialog_pages.end (); ++op) { - (*op)->activate (false); - } - - remove_editor_options_page (view); - - lay::EditorOptionsPages *pages = new lay::EditorOptionsPages (view->editor_options_frame (), prop_dialog_pages, view); - view->editor_options_frame ()->layout ()->addWidget (pages); - view->editor_options_frame ()->setFocusProxy (pages); -} - -void -remove_editor_options_page (lay::LayoutView *view) -{ - if (! view->editor_options_frame ()) { - return; - } - - QObjectList children = view->editor_options_frame ()->children (); - for (QObjectList::iterator c = children.begin (); c != children.end (); ++c) { - if (dynamic_cast (*c)) { - delete *c; - } - } -} - -static -lay::EditorOptionsPages *get_pages_widget (lay::LayoutView *view) -{ - // TODO: is there a better way to find the editor options pages? - lay::EditorOptionsPages *eo_pages = 0; - QObjectList children = view->editor_options_frame ()->children (); - for (QObjectList::iterator c = children.begin (); c != children.end () && !eo_pages; ++c) { - eo_pages = dynamic_cast (*c); - } - - return eo_pages; -} - -void -activate_service (lay::LayoutView *view, const lay::PluginDeclaration *pd, bool active) -{ - lay::EditorOptionsPages *eo_pages = get_pages_widget (view); - if (!eo_pages) { - return; - } - - // TODO: this is very inefficient as each "activate" will regenerate the tabs - for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { - (*op)->activate (((*op)->plugin_declaration () == pd || ! (*op)->plugin_declaration ()) && active); - } -} - -void -setup_pages (lay::LayoutView *view) -{ - lay::EditorOptionsPages *eo_pages = get_pages_widget (view); - if (!eo_pages) { - return; - } - - for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { - (*op)->setup (view); - } -} - void commit_recent (lay::LayoutView *view) { - lay::EditorOptionsPages *eo_pages = get_pages_widget (view); + lay::EditorOptionsPages *eo_pages = view->editor_options_pages ();; if (!eo_pages) { return; } diff --git a/src/edt/edt/edtPlugin.h b/src/edt/edt/edtPlugin.h index 2c1c097bf..17dcd2389 100644 --- a/src/edt/edt/edtPlugin.h +++ b/src/edt/edt/edtPlugin.h @@ -42,32 +42,9 @@ namespace edt class PluginDeclarationBase : public lay::PluginDeclaration { - public: - virtual void get_editor_options_pages (std::vector &, lay::LayoutView *, lay::Dispatcher *) const = 0; + // .. nothing yet .. }; - /** - * @brief Creates an editor options page and installs it inside the view - */ - void show_editor_options_page (lay::LayoutView *view); - - /** - * @brief Removes the editor options page from the view - */ - void remove_editor_options_page (lay::LayoutView *view); - - /** - * @brief Activate or deactivate a certain service - * - * This will show or hide the editor properties pages for the respective service. - */ - void activate_service (lay::LayoutView *view, const lay::PluginDeclaration *pd, bool active); - - /** - * @brief Setup the editor option pages for the given view - */ - void setup_pages (lay::LayoutView *view); - /** * @brief Commits the current configuration for the recently used configuration list */ diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index 56452b024..e5d3c48d5 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -875,13 +875,7 @@ Service::activated () m_immediate = do_activated (); m_editing = false; - // Show editor options panel - show_editor_options_page (view ()); - } - - // make all editor option pages visible - activate_service (view (), plugin_declaration (), true); } void @@ -889,13 +883,8 @@ Service::deactivated () { reset_mouse_cursor (); - // make all editor option pages visible - activate_service (view (), plugin_declaration (), false); - edit_cancel (); - remove_editor_options_page (view ()); - m_immediate = false; } diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 29383c2d4..94f8e6598 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -92,6 +92,7 @@ #include "layHelpAboutDialog.h" #include "layControlWidgetStack.h" #include "layViewWidgetStack.h" +#include "layEditorOptionsPages.h" #include "layInit.h" #include "antObject.h" #include "antService.h" @@ -1681,12 +1682,14 @@ MainWindow::select_mode (int m) } bool eo_visible = false; - if (pd_sel) { + if (mp_eo_stack && pd_sel) { eo_visible = pd_sel->editable_enabled (); } - if (eo_visible && (!mp_eo_stack->currentWidget () || !mp_eo_stack->currentWidget ()->findChild ())) { - // - eo_visible = false; + if (mp_eo_stack && eo_visible) { + lay::EditorOptionsPages *eo_pages = dynamic_cast (mp_eo_stack->currentWidget ()); + if (! eo_pages || ! eo_pages->has_content ()) { + eo_visible = false; + } } if (eo_visible != m_eo_visible) { diff --git a/src/laybasic/laybasic/layEditorOptionsFrame.cc b/src/laybasic/laybasic/layEditorOptionsFrame.cc new file mode 100644 index 000000000..283462ff1 --- /dev/null +++ b/src/laybasic/laybasic/layEditorOptionsFrame.cc @@ -0,0 +1,70 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "layEditorOptionsFrame.h" +#include "layEditorOptionsPage.h" +#include "layEditorOptionsPages.h" +#include "layPlugin.h" +#include "layLayoutView.h" + +#include + +namespace lay +{ + +EditorOptionsFrame::EditorOptionsFrame (QWidget *parent) + : QFrame (parent), mp_pages (0) +{ + setObjectName (QString::fromUtf8 ("editor_options_frame")); + + QVBoxLayout *left_frame_ly = new QVBoxLayout (this); + left_frame_ly->setMargin (0); + left_frame_ly->setSpacing (0); +} + +EditorOptionsFrame::~EditorOptionsFrame () +{ + // .. nothing yet .. +} + +void +EditorOptionsFrame::populate (LayoutView *view) +{ + std::vector prop_dialog_pages; + for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { + cls->get_editor_options_pages (prop_dialog_pages, view, view->dispatcher ()); + } + + for (std::vector::const_iterator op = prop_dialog_pages.begin (); op != prop_dialog_pages.end (); ++op) { + (*op)->activate (false); + } + + if (mp_pages) { + delete mp_pages; + } + + mp_pages = new lay::EditorOptionsPages (this, prop_dialog_pages, view); + layout ()->addWidget (mp_pages); + setFocusProxy (mp_pages); +} + +} diff --git a/src/laybasic/laybasic/layEditorOptionsFrame.h b/src/laybasic/laybasic/layEditorOptionsFrame.h new file mode 100644 index 000000000..5b17d3d0f --- /dev/null +++ b/src/laybasic/laybasic/layEditorOptionsFrame.h @@ -0,0 +1,55 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_layEditorOptionsFrame +#define HDR_layEditorOptionsFrame + +#include "laybasicCommon.h" +#include + +namespace lay +{ + +class EditorOptionsPages; +class LayoutView; + +class LAYBASIC_PUBLIC EditorOptionsFrame + : public QFrame +{ +public: + EditorOptionsFrame (QWidget *parent); + virtual ~EditorOptionsFrame (); + + void populate (LayoutView *view); + + EditorOptionsPages *pages_widget () const + { + return mp_pages; + } + +public: + EditorOptionsPages *mp_pages; +}; + +} + +#endif diff --git a/src/laybasic/laybasic/layEditorOptionsPage.h b/src/laybasic/laybasic/layEditorOptionsPage.h index 3a2692a89..3ee70b1a0 100644 --- a/src/laybasic/laybasic/layEditorOptionsPage.h +++ b/src/laybasic/laybasic/layEditorOptionsPage.h @@ -23,6 +23,8 @@ #ifndef HDR_layEditorOptionsPage #define HDR_layEditorOptionsPage +#include "laybasicCommon.h" + #include namespace lay @@ -36,7 +38,7 @@ class EditorOptionsPages; /** * @brief The base class for a object properties page */ -class EditorOptionsPage +class LAYBASIC_PUBLIC EditorOptionsPage : public QWidget { Q_OBJECT diff --git a/src/laybasic/laybasic/layEditorOptionsPages.cc b/src/laybasic/laybasic/layEditorOptionsPages.cc index fd65eb472..009924898 100644 --- a/src/laybasic/laybasic/layEditorOptionsPages.cc +++ b/src/laybasic/laybasic/layEditorOptionsPages.cc @@ -80,7 +80,18 @@ EditorOptionsPages::focusInEvent (QFocusEvent * /*event*/) // Sends the focus to the current page's last focus owner if (mp_pages->currentWidget () && mp_pages->currentWidget ()->focusWidget ()) { mp_pages->currentWidget ()->focusWidget ()->setFocus (); + } +} + +bool EditorOptionsPages::has_content () const +{ + for (std::vector ::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) { + // NOTE: we ignore unspecific pages because they are always visible and don't contribute specific content + if ((*p)->active () && (*p)->plugin_declaration () != 0) { + return true; + } } + return false; } void diff --git a/src/laybasic/laybasic/layEditorOptionsPages.h b/src/laybasic/laybasic/layEditorOptionsPages.h index 5c0c309df..d20351e0a 100644 --- a/src/laybasic/laybasic/layEditorOptionsPages.h +++ b/src/laybasic/laybasic/layEditorOptionsPages.h @@ -24,6 +24,7 @@ #ifndef HDR_layEditorOptionsPages #define HDR_layEditorOptionsPages +#include "laybasicCommon.h" #include "layEditorOptionsPage.h" #include @@ -45,7 +46,7 @@ class Plugin; /** * @brief The object properties dialog */ -class EditorOptionsPages +class LAYBASIC_PUBLIC EditorOptionsPages : public QFrame { Q_OBJECT @@ -63,6 +64,8 @@ public: return m_pages; } + bool has_content () const; + public slots: void apply (); void setup (); diff --git a/src/laybasic/laybasic/layLayoutView.cc b/src/laybasic/laybasic/layLayoutView.cc index fde47bc44..cdd930078 100644 --- a/src/laybasic/laybasic/layLayoutView.cc +++ b/src/laybasic/laybasic/layLayoutView.cc @@ -66,6 +66,8 @@ #include "layBookmarkManagementForm.h" #include "layNetlistBrowserDialog.h" #include "layBookmarksView.h" +#include "layEditorOptionsFrame.h" +#include "layEditorOptionsPages.h" #include "dbClipboard.h" #include "dbLayout.h" #include "dbLayoutUtils.h" @@ -254,7 +256,8 @@ LayoutView::LayoutView (db::Manager *manager, bool editable, lay::Plugin *plugin m_editable (editable), m_options (options), m_annotation_shapes (manager), - dm_prop_changed (this, &LayoutView::do_prop_changed) + dm_prop_changed (this, &LayoutView::do_prop_changed), + dm_setup_editor_option_pages (this, &LayoutView::do_setup_editor_options_pages) { // either it's us or the parent has a dispatcher tl_assert (dispatcher () != 0); @@ -272,7 +275,8 @@ LayoutView::LayoutView (lay::LayoutView *source, db::Manager *manager, bool edit m_editable (editable), m_options (options), m_annotation_shapes (manager), - dm_prop_changed (this, &LayoutView::do_prop_changed) + dm_prop_changed (this, &LayoutView::do_prop_changed), + dm_setup_editor_option_pages (this, &LayoutView::do_setup_editor_options_pages) { // either it's us or the parent has a dispatcher tl_assert (dispatcher () != 0); @@ -559,12 +563,8 @@ LayoutView::init (db::Manager *mgr, QWidget * /*parent*/) if ((m_options & LV_NoEditorOptionsPanel) == 0 && (m_options & LV_Naked) == 0) { - mp_editor_options_frame = new QFrame (0); - mp_editor_options_frame->setObjectName (QString::fromUtf8 ("editor_options_frame")); - - QVBoxLayout *left_frame_ly = new QVBoxLayout (mp_editor_options_frame); - left_frame_ly->setMargin (0); - left_frame_ly->setSpacing (0); + mp_editor_options_frame = new lay::EditorOptionsFrame (0); + mp_editor_options_frame->populate (this); connect (mp_editor_options_frame, SIGNAL (destroyed ()), this, SLOT (side_panel_destroyed ())); @@ -717,6 +717,26 @@ QWidget *LayoutView::menu_parent_widget () return this; } +lay::EditorOptionsPages *LayoutView::editor_options_pages () +{ + if (! mp_editor_options_frame) { + return 0; + } else { + return mp_editor_options_frame->pages_widget (); + } +} + +void LayoutView::do_setup_editor_options_pages () +{ + // intialize the editor option pages + lay::EditorOptionsPages *eo_pages = editor_options_pages (); + if (eo_pages) { + for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { + (*op)->setup (this); + } + } +} + void LayoutView::side_panel_destroyed () { if (sender () == mp_control_frame) { @@ -868,6 +888,8 @@ void LayoutView::create_plugins (const lay::PluginDeclaration *except_this) } + dm_setup_editor_option_pages (); + mode (default_mode ()); } @@ -1645,6 +1667,14 @@ LayoutView::configure (const std::string &name, const std::string &value) } } +void +LayoutView::config_finalize () +{ + // It's important that the editor option pages are updated last - because the + // configuration change may trigger other configuration changes + dm_setup_editor_option_pages (); +} + void LayoutView::enable_edits (bool enable) { @@ -5525,11 +5555,13 @@ LayoutView::mode (int m) if (m != m_mode) { m_mode = m; + lay::Plugin *active_plugin = 0; if (m > 0) { for (std::vector::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) { if ((*p)->plugin_declaration ()->id () == m) { + active_plugin = *p; mp_canvas->activate ((*p)->view_service_interface ()); break; } @@ -5541,6 +5573,22 @@ LayoutView::mode (int m) mp_canvas->activate (mp_move_service); } + lay::EditorOptionsPages *eo_pages = editor_options_pages (); + if (eo_pages) { + + // TODO: this is very inefficient as each "activate" will regenerate the tabs + for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { + bool is_active = false; + if ((*op)->plugin_declaration () == 0) { + is_active = true; + } else if (active_plugin && active_plugin->plugin_declaration () == (*op)->plugin_declaration ()) { + is_active = true; + } + (*op)->activate (is_active); + } + + } + } } diff --git a/src/laybasic/laybasic/layLayoutView.h b/src/laybasic/laybasic/layLayoutView.h index 3b545fa82..77c29d23d 100644 --- a/src/laybasic/laybasic/layLayoutView.h +++ b/src/laybasic/laybasic/layLayoutView.h @@ -50,6 +50,7 @@ #include "layPlugin.h" #include "layDisplayState.h" #include "layBookmarkList.h" +#include "layEditorOptionsFrame.h" #include "gsi.h" #include "tlException.h" #include "tlEvents.h" @@ -82,6 +83,7 @@ class MoveService; class Browser; class ColorButton; class ConfigureAction; +class EditorOptionsPages; /** * @brief Stores a layer reference to create layers which have been added by some action @@ -269,6 +271,14 @@ public: return mp_libraries_frame; } + /** + * @brief Gets the container with the bookmarks view + */ + QWidget *bookmarks_frame () + { + return mp_bookmarks_frame; + } + /** * @brief Gets the container with the editor options */ @@ -278,12 +288,9 @@ public: } /** - * @brief Gets the container with the bookmarks view + * @brief Gets the editor options pages widget */ - QWidget *bookmarks_frame () - { - return mp_bookmarks_frame; - } + lay::EditorOptionsPages *editor_options_pages (); /** * @brief Pastes from clipboard @@ -2733,7 +2740,8 @@ private: lay::HierarchyControlPanel *mp_hierarchy_panel; lay::LibrariesView *mp_libraries_view; lay::BookmarksView *mp_bookmarks_view; - QWidget *mp_control_frame, *mp_hierarchy_frame, *mp_libraries_frame, *mp_editor_options_frame, *mp_bookmarks_frame; + QWidget *mp_control_frame, *mp_hierarchy_frame, *mp_libraries_frame, *mp_bookmarks_frame; + lay::EditorOptionsFrame *mp_editor_options_frame; QSpinBox *mp_min_hier_spbx; QSpinBox *mp_max_hier_spbx; std::list m_cellviews; @@ -2846,6 +2854,8 @@ private: bool m_active_cellview_changed_event_enabled; tl::DeferredMethod dm_prop_changed; + tl::DeferredMethod dm_setup_editor_option_pages; + void init (db::Manager *mgr, QWidget *parent); void init_menu (); @@ -2867,11 +2877,14 @@ private: int max_hier_level () const; bool set_hier_levels_basic (std::pair l); + void do_setup_editor_options_pages (); + void update_event_handlers (); void viewport_changed (); void cellview_changed (unsigned int index); bool configure (const std::string &name, const std::string &value); + void config_finalize (); void do_load_layer_props (const std::string &fn, bool map_cv, int cv_index, bool add_default); void finish_cellviews_changed (); diff --git a/src/laybasic/laybasic/layPlugin.h b/src/laybasic/laybasic/layPlugin.h index 2b74177dd..df042c998 100644 --- a/src/laybasic/laybasic/layPlugin.h +++ b/src/laybasic/laybasic/layPlugin.h @@ -54,6 +54,7 @@ class ViewService; class Editable; class Drawing; class TechnologyComponentProvider; +class EditorOptionsPage; /** * @brief The base class for configuration pages @@ -234,7 +235,7 @@ public: { return std::vector > (); } - + /** * @brief The global configuration * @@ -317,7 +318,7 @@ public: } /** - * @brief Fetch the menu objects for this plugin + * @brief Fetches the menu objects for this plugin * * The implementation of this method is supposed to call the base * class'es "get_menu_entries" method and add it's own entries. @@ -328,7 +329,7 @@ public: } /** - * @brief Create a plugin object of the respective kind + * @brief Creates a plugin object of the respective kind * * This method may return 0 for "dummy" plugins that just register menu entries * or configuration options. @@ -339,7 +340,7 @@ public: } /** - * @brief Tell if the plugin implements the "lay::Editable" interface + * @brief Tells if the plugin implements the "lay::Editable" interface * * This method is supposed to return true if the plugin implements the * lay::Editable interface and wants to be listed as an "edit" mode in the @@ -350,9 +351,22 @@ public: { return false; } - + /** - * @brief Tell if the plugin implements a "lay::ViewService" active mouse mode + * @brief Gets the editor options pages + * + * Editor options pages are shown in the editor options dockable tool widget. Each plugin can create such pages + * and these will be shown in tabs inside this widget. + * + * The new pages are returned in the "pages" vector. The layout view will take ownership of these pages. + */ + virtual void get_editor_options_pages (std::vector & /*pages*/, lay::LayoutView * /*view*/, lay::Dispatcher * /*dispatcher*/) const + { + // .. no pages in the default implementation .. + } + + /** + * @brief Tells if the plugin implements a "lay::ViewService" active mouse mode * * This method is supposed to return true if the plugin implements the * lay::ViewService interface and wants to be listed as an mouse mode in the diff --git a/src/laybasic/laybasic/layQtTools.h b/src/laybasic/laybasic/layQtTools.h index 8211f2fc8..49232c9d6 100644 --- a/src/laybasic/laybasic/layQtTools.h +++ b/src/laybasic/laybasic/layQtTools.h @@ -74,7 +74,7 @@ LAYBASIC_PUBLIC void register_help_handler (QObject *object, const char *slot, c * and the exception's text will be used as tooltip. Use this function with * a null ex pointer to clear the error condition. */ -void indicate_error (QWidget *le, const tl::Exception *ex); +LAYBASIC_PUBLIC void indicate_error (QWidget *le, const tl::Exception *ex); } // namespace lay diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index 6bc887d5f..2fef55427 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -115,6 +115,7 @@ SOURCES = \ layEditable.cc \ layEditStipplesForm.cc \ layEditStippleWidget.cc \ + layEditorOptionsFrame.cc \ layEditorOptionsPage.cc \ layEditorOptionsPages.cc \ layFileDialog.cc \ @@ -218,6 +219,7 @@ HEADERS = \ layEditable.h \ layEditStipplesForm.h \ layEditStippleWidget.h \ + layEditorOptionsFrame.h \ layEditorOptionsPage.h \ layEditorOptionsPages.h \ layFileDialog.h \ From 55f5132514be5491baa92e50eff08536a8d5010f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Sep 2020 21:26:06 +0200 Subject: [PATCH 080/113] WIP: fixed visibility of editor options. --- src/lay/lay/layMainWindow.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 94f8e6598..03f9732c7 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -1685,8 +1685,8 @@ MainWindow::select_mode (int m) if (mp_eo_stack && pd_sel) { eo_visible = pd_sel->editable_enabled (); } - if (mp_eo_stack && eo_visible) { - lay::EditorOptionsPages *eo_pages = dynamic_cast (mp_eo_stack->currentWidget ()); + if (current_view () && eo_visible) { + lay::EditorOptionsPages *eo_pages = current_view ()->editor_options_pages (); if (! eo_pages || ! eo_pages->has_content ()) { eo_visible = false; } From e42136ee2706b79211812055342f13a29f025892 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Sep 2020 23:03:18 +0200 Subject: [PATCH 081/113] Editor options: recent layers are created if not there. --- src/edt/edt/edtRecentConfigurationPage.cc | 2 +- src/laybasic/laybasic/layLayerControlPanel.cc | 24 +++++------ src/laybasic/laybasic/layLayerControlPanel.h | 2 +- src/laybasic/laybasic/layLayoutView.cc | 41 +++++++++++++++++-- src/laybasic/laybasic/layLayoutView.h | 12 +++++- 5 files changed, 62 insertions(+), 19 deletions(-) diff --git a/src/edt/edt/edtRecentConfigurationPage.cc b/src/edt/edt/edtRecentConfigurationPage.cc index 6c4b998cc..554f85be0 100644 --- a/src/edt/edt/edtRecentConfigurationPage.cc +++ b/src/edt/edt/edtRecentConfigurationPage.cc @@ -327,7 +327,7 @@ RecentConfigurationPage::item_clicked (QTreeWidgetItem *item) ex.read (cv_index); } - mp_view->set_current_layer (cv_index, lp); + mp_view->set_or_request_current_layer (cv_index, lp); } else { dispatcher ()->config_set (c->cfg_name, v); diff --git a/src/laybasic/laybasic/layLayerControlPanel.cc b/src/laybasic/laybasic/layLayerControlPanel.cc index 4c5c8f884..b0038635d 100644 --- a/src/laybasic/laybasic/layLayerControlPanel.cc +++ b/src/laybasic/laybasic/layLayerControlPanel.cc @@ -505,16 +505,14 @@ LayerControlPanel::cm_insert () BEGIN_PROTECTED_CLEANUP - transaction (tl::to_string (QObject::tr ("Insert views"))); + transaction (tl::to_string (QObject::tr ("Insert layer view"))); props.set_source (tl::to_string (n)); mp_view->init_layer_properties (props); + const LayerPropertiesNode &lp = mp_view->insert_layer (sel, props); - if (transacting ()) { - manager ()->queue (this, new LayerSelectionClearOp ()); - } - mp_layer_list->set_current (sel); + set_current_layer (sel); commit (); @@ -565,18 +563,12 @@ LayerControlPanel::cm_group () mp_view->insert_layer (ins_pos, node); - if (transacting ()) { - manager ()->queue (this, new LayerSelectionClearOp ()); - } + set_current_layer (*sel.rbegin ()); commit (); - end_updates (); - emit order_changed (); - mp_layer_list->set_current (*sel.rbegin ()); - } END_PROTECTED_CLEANUP { recover (); } @@ -1907,8 +1899,14 @@ LayerControlPanel::do_update_content () } void -LayerControlPanel::set_current_layer (const lay::LayerPropertiesConstIterator &l) const +LayerControlPanel::set_current_layer (const lay::LayerPropertiesConstIterator &l) { + if (transacting ()) { + manager ()->queue (this, new LayerSelectionClearOp ()); + } + + end_updates (); + mp_layer_list->set_current (l); } diff --git a/src/laybasic/laybasic/layLayerControlPanel.h b/src/laybasic/laybasic/layLayerControlPanel.h index 6fd767e74..7276bdfbe 100644 --- a/src/laybasic/laybasic/layLayerControlPanel.h +++ b/src/laybasic/laybasic/layLayerControlPanel.h @@ -250,7 +250,7 @@ public: * * This will also select this layer. */ - void set_current_layer (const lay::LayerPropertiesConstIterator &l) const; + void set_current_layer (const lay::LayerPropertiesConstIterator &l); /** * @brief Return the current layer index diff --git a/src/laybasic/laybasic/layLayoutView.cc b/src/laybasic/laybasic/layLayoutView.cc index cdd930078..fe3c642b7 100644 --- a/src/laybasic/laybasic/layLayoutView.cc +++ b/src/laybasic/laybasic/layLayoutView.cc @@ -1709,7 +1709,41 @@ LayoutView::enable_edits (bool enable) } } -void +bool +LayoutView::set_or_request_current_layer (unsigned int cv_index, const db::LayerProperties &lp) +{ + bool ok = set_current_layer (cv_index, lp); + if (ok) { + return true; + } + + if (! mp_control_panel) { + return false; + } + + const lay::CellView &cv = cellview (cv_index); + if (! cv.is_valid ()) { + return false; + } + + if (QMessageBox::question (this, tr ("Create Layer"), tr ("Layer %1 does not exist yet. Create it now?").arg (tl::to_qstring (lp.to_string ()))) == QMessageBox::Yes) { + + lay::LayerPropertiesNode lpn; + lpn.set_source (lay::ParsedLayerSource (lp, cv_index)); + init_layer_properties (lpn); + + transaction (tl::to_string (QObject::tr ("Create new layer"))); + set_current_layer (lay::LayerPropertiesConstIterator (& insert_layer (end_layers (), lpn))); + commit (); + + return true; + + } + + return false; +} + +bool LayoutView::set_current_layer (unsigned int cv_index, const db::LayerProperties &lp) { // rename the ones that got shifted. @@ -1717,13 +1751,14 @@ LayoutView::set_current_layer (unsigned int cv_index, const db::LayerProperties while (! l.at_end ()) { if (l->source (true).cv_index () == int (cv_index) && l->source (true).layer_props ().log_equal (lp)) { set_current_layer (l); - break; + return true; } ++l; } + return false; } -void +void LayoutView::set_current_layer (const lay::LayerPropertiesConstIterator &l) { if (mp_control_panel) { diff --git a/src/laybasic/laybasic/layLayoutView.h b/src/laybasic/laybasic/layLayoutView.h index 77c29d23d..66469b163 100644 --- a/src/laybasic/laybasic/layLayoutView.h +++ b/src/laybasic/laybasic/layLayoutView.h @@ -780,8 +780,18 @@ public: * * This method will look up that layer in the layer view tree and select that layer. * This method will also select this layer. + * + * Returns false if the layer is not a valid one. */ - void set_current_layer (unsigned int cv_index, const db::LayerProperties &properties); + bool set_current_layer (unsigned int cv_index, const db::LayerProperties &properties); + + /** + * @brief Sets the currently active layer by layer properties and cell view index + * + * If the layer does not exist, the user will be asked whether to create the layer. + * Returns false if the layer is not a valid one and the user defined to create the layer. + */ + bool set_or_request_current_layer (unsigned int cv_index, const db::LayerProperties &properties); /** * @brief Sets the currently active layer From 307c10f1843e179ffb94e5bf2695d5d981e2433a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 7 Sep 2020 22:35:06 +0200 Subject: [PATCH 082/113] WIP: better highlighting of snap objects. --- src/edt/edt/edtService.cc | 74 +------ src/edt/edt/edtService.h | 19 +- src/edt/edt/edtServiceImpl.cc | 15 +- src/laybasic/laybasic/layEditorServiceBase.cc | 200 ++++++++++++++++++ src/laybasic/laybasic/layEditorServiceBase.h | 77 +++++++ src/laybasic/laybasic/laybasic.pro | 2 + 6 files changed, 296 insertions(+), 91 deletions(-) create mode 100644 src/laybasic/laybasic/layEditorServiceBase.cc create mode 100644 src/laybasic/laybasic/layEditorServiceBase.h diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index e5d3c48d5..4fad85754 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -61,8 +61,7 @@ ac_from_buttons (unsigned int buttons) // ------------------------------------------------------------- Service::Service (db::Manager *manager, lay::LayoutView *view, db::ShapeIterator::flags_type flags) - : lay::ViewService (view->view_object_widget ()), - lay::Editable (view), + : lay::EditorServiceBase (view), lay::Plugin (view), db::Object (manager), mp_view (view), @@ -83,8 +82,7 @@ Service::Service (db::Manager *manager, lay::LayoutView *view, db::ShapeIterator } Service::Service (db::Manager *manager, lay::LayoutView *view) - : lay::ViewService (view->view_object_widget ()), - lay::Editable (view), + : lay::EditorServiceBase (view), lay::Plugin (view), db::Object (manager), mp_view (view), @@ -116,7 +114,6 @@ Service::~Service () } m_edit_markers.clear (); - reset_mouse_cursor (); clear_transient_selection (); } @@ -480,71 +477,6 @@ Service::selection_bbox () return box; } -namespace -{ - -class MouseCursorViewObject - : public lay::ViewObject -{ -public: - MouseCursorViewObject (lay::ViewObjectWidget *widget, const db::DPoint &pt) - : lay::ViewObject (widget, false), m_pt (pt) - { } - - virtual void render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas) - { - int dither_pattern = 0; // solid - int lw = int (0.5 + 1.0 / canvas.resolution ()); - - std::vector ops; - ops.resize (1); - ops[0] = lay::ViewOp (canvas.foreground_color ().rgb (), lay::ViewOp::Copy, 0, (unsigned int) dither_pattern, 0, lay::ViewOp::Rect, lw, 0); - lay::CanvasPlane *plane = canvas.plane (ops); - - lay::Renderer &r = canvas.renderer (); - - double rad = 4.0 / canvas.resolution () / vp.trans ().mag (); - - const size_t num_pts = 16; - db::DPoint pts [num_pts]; - for (size_t i = 0; i < num_pts; ++i) { - double x = rad * cos (M_PI * 2.0 * double (i) / double (num_pts)); - double y = rad * sin (M_PI * 2.0 * double (i) / double (num_pts)); - pts [i] = m_pt + db::DVector (x, y); - } - db::DPolygon circle; - circle.assign_hull (pts, pts + num_pts); - - r.draw (circle, vp.trans (), 0, plane, 0, 0); - - r.draw (db::DEdge (m_pt + db::DVector (0, rad * 0.5), m_pt + db::DVector (0, rad * 4)), vp.trans (), 0, plane, 0, 0); - r.draw (db::DEdge (m_pt + db::DVector (rad * 0.5, 0), m_pt + db::DVector (rad * 4, 0)), vp.trans (), 0, plane, 0, 0); - r.draw (db::DEdge (m_pt + db::DVector (0, -rad * 0.5), m_pt + db::DVector (0, -rad * 4)), vp.trans (), 0, plane, 0, 0); - r.draw (db::DEdge (m_pt + db::DVector (-rad * 0.5, 0), m_pt + db::DVector (-rad * 4, 0)), vp.trans (), 0, plane, 0, 0); - } - -private: - db::DPoint m_pt; -}; - -} - -void -Service::set_mouse_cursor (const db::DPoint &pt) -{ - reset_mouse_cursor (); - m_mouse_cursor_markers.push_back (new MouseCursorViewObject (widget (), pt)); -} - -void -Service::reset_mouse_cursor () -{ - for (std::vector::iterator r = m_mouse_cursor_markers.begin (); r != m_mouse_cursor_markers.end (); ++r) { - delete *r; - } - m_mouse_cursor_markers.clear (); -} - void Service::set_edit_marker (lay::ViewObject *edit_marker) { @@ -881,7 +813,7 @@ Service::activated () void Service::deactivated () { - reset_mouse_cursor (); + clear_mouse_cursors (); edit_cancel (); diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h index 7f2320f02..22ff094e3 100644 --- a/src/edt/edt/edtService.h +++ b/src/edt/edt/edtService.h @@ -27,9 +27,8 @@ #include "edtCommon.h" -#include "layEditable.h" +#include "layEditorServiceBase.h" #include "layPlugin.h" -#include "layViewObject.h" #include "layMarker.h" #include "laySnap.h" #include "layObjectInstPath.h" @@ -72,8 +71,7 @@ std::map pcell_parameters_from_string (const std::stri // ------------------------------------------------------------- class EDT_PUBLIC Service - : public lay::ViewService, - public lay::Editable, + : public lay::EditorServiceBase, public lay::Plugin, public db::Object { @@ -458,16 +456,6 @@ protected: */ virtual void service_configuration_changed (); - /** - * @brief Sets the mouse cursor to the given point - */ - void set_mouse_cursor (const db::DPoint &pt); - - /** - * @brief Resets the mouse cursor - */ - void reset_mouse_cursor (); - /** * @brief Install a marker for representing the edited object * @@ -578,9 +566,6 @@ private: // The marker representing the object to be edited std::vector m_edit_markers; - // The marker representing the mouse cursor - std::vector m_mouse_cursor_markers; - // True, if editing is in progress. bool m_editing; diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index 51b28a90b..b34ce269b 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -417,14 +417,23 @@ void PolygonService::do_mouse_move_inactive (const db::DPoint &p) { lay::PointSnapToObjectResult snap_details = snap2_details (p); - set_mouse_cursor (snap_details.snapped_point); + + clear_mouse_cursors (); + + add_mouse_cursor (snap_details.snapped_point, + snap_details.object_snap == lay::PointSnapToObjectResult::ObjectVertex || + (snap_details.object_snap == lay::PointSnapToObjectResult::ObjectUnspecific && snap_details.object_ref.is_degenerate ())); + + if (snap_details.object_snap == lay::PointSnapToObjectResult::ObjectEdge || + (snap_details.object_snap == lay::PointSnapToObjectResult::ObjectUnspecific && ! snap_details.object_ref.is_degenerate ())) { + add_edge_marker (snap_details.object_ref, false); + } } void PolygonService::do_mouse_move (const db::DPoint &p) { - lay::PointSnapToObjectResult snap_details = snap2_details (p); - set_mouse_cursor (snap_details.snapped_point); + do_mouse_move_inactive (p); set_cursor (lay::Cursor::cross); if (m_points.size () >= 2) { diff --git a/src/laybasic/laybasic/layEditorServiceBase.cc b/src/laybasic/laybasic/layEditorServiceBase.cc new file mode 100644 index 000000000..e05df5bec --- /dev/null +++ b/src/laybasic/laybasic/layEditorServiceBase.cc @@ -0,0 +1,200 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "layEditorServiceBase.h" +#include "layViewport.h" +#include "layLayoutView.h" + +namespace lay +{ + +// -------------------------------------------------------------------------------------- + +template +static void +make_circle (double r, const db::DPoint ¢er, db::DPolygon &poly, bool as_hole) +{ + db::DPoint pts [num_pts]; + for (size_t i = 0; i < num_pts; ++i) { + double x = r * cos (M_PI * 2.0 * double (i) / double (num_pts)); + double y = r * sin (M_PI * 2.0 * double (i) / double (num_pts)); + pts [i] = center + db::DVector (x, y); + } + + if (! as_hole) { + poly.assign_hull (pts, pts + num_pts); + } else { + poly.insert_hole (pts, pts + num_pts); + } +} + +class MouseCursorViewObject + : public lay::ViewObject +{ +public: + MouseCursorViewObject (lay::ViewObjectWidget *widget, const db::DPoint &pt, bool solid) + : lay::ViewObject (widget, false), m_pt (pt), m_solid (solid) + { } + + virtual void render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas) + { + int dither_pattern = 0; // solid + int cross_dither_pattern = 6; // dotted + + int lw = int (0.5 + 1.0 / canvas.resolution ()); + + std::vector ops; + ops.resize (1); + ops[0] = lay::ViewOp (canvas.foreground_color ().rgb (), lay::ViewOp::Copy, 0, (unsigned int) dither_pattern, 0, lay::ViewOp::Rect, lw, 0); + lay::CanvasPlane *plane = canvas.plane (ops); + + ops[0] = lay::ViewOp (canvas.foreground_color ().rgb (), lay::ViewOp::Copy, 0, (unsigned int) cross_dither_pattern, 0, lay::ViewOp::Rect, lw, 0); + lay::CanvasPlane *cross_plane = canvas.plane (ops); + + lay::Renderer &r = canvas.renderer (); + + double rad = 4.0 / canvas.resolution () / vp.trans ().mag (); + + db::DPolygon c; + if (! m_solid) { + make_circle (rad, m_pt, c, false); + r.draw (c, vp.trans (), 0, plane, 0, 0); + } else { + make_circle (rad * 2, m_pt, c, false); + r.draw (c, vp.trans (), 0, plane, 0, 0); + make_circle (rad, m_pt, c, false); + r.draw (c, vp.trans (), 0, plane, 0, 0); + } + + r.draw (db::DEdge (m_pt + db::DVector (0, -rad * 4), m_pt + db::DVector (0, rad * 4)), vp.trans (), 0, cross_plane, 0, 0); + r.draw (db::DEdge (m_pt + db::DVector (-rad * 4, 0), m_pt + db::DVector (rad * 4, 0)), vp.trans (), 0, cross_plane, 0, 0); + } + +private: + db::DPoint m_pt; + bool m_solid; +}; + +class EdgeMarkerViewObject + : public lay::ViewObject +{ +public: + EdgeMarkerViewObject (lay::ViewObjectWidget *widget, const db::DEdge &edge, bool solid) + : lay::ViewObject (widget, false), m_edge (edge), m_solid (solid) + { } + + virtual void render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas) + { + if (m_edge.is_degenerate ()) { + return; + } + + int dashed_style = 2; + int solid_style = 0; + + int lw = int (0.5 + 1.0 / canvas.resolution ()); + + std::vector ops; + ops.resize (1); + ops[0] = lay::ViewOp (canvas.foreground_color ().rgb (), lay::ViewOp::Copy, solid_style, 0, 0, lay::ViewOp::Rect, lw, 0); + lay::CanvasPlane *arrow_plane = canvas.plane (ops); + + ops[0] = lay::ViewOp (canvas.foreground_color ().rgb (), lay::ViewOp::Copy, m_solid ? solid_style : dashed_style, 1, 0, lay::ViewOp::Rect, lw, 0); + lay::CanvasPlane *edge_plane = canvas.plane (ops); + + lay::Renderer &r = canvas.renderer (); + r.draw (m_edge, vp.trans (), 0, edge_plane, 0, 0); + + double arrow_length = 12.0 / canvas.resolution () / vp.trans ().mag (); + + double arrow_width_half = arrow_length * 0.25882; // sin(15 deg) + db::DVector n = db::DVector (m_edge.dy (), -m_edge.dx ()) * (arrow_width_half / m_edge.length ()); + db::DVector d = db::DVector (m_edge.dx (), m_edge.dy ()) * (arrow_length / m_edge.length ()); + + if (m_edge.length () < 2 * arrow_length) { + + r.draw (db::DEdge (m_edge.p1 () - n, m_edge.p1 () + n), vp.trans (), 0, arrow_plane, 0, 0); + r.draw (db::DEdge (m_edge.p2 () - n, m_edge.p2 () + n), vp.trans (), 0, arrow_plane, 0, 0); + + } else { + + db::DPoint pts[3]; + db::DPolygon p; + + pts[0] = m_edge.p1 (); + pts[1] = m_edge.p1 () + d - n; + pts[2] = m_edge.p1 () + d + n; + + p.assign_hull (pts, pts + 3); + r.draw (p, vp.trans (), 0, arrow_plane, 0, 0); + + pts[0] = m_edge.p2 (); + pts[1] = m_edge.p2 () - d + n; + pts[2] = m_edge.p2 () - d - n; + + p.assign_hull (pts, pts + 3); + r.draw (p, vp.trans (), 0, arrow_plane, 0, 0); + + } + } + +private: + db::DEdge m_edge; + bool m_solid; +}; + +// -------------------------------------------------------------------------------------- + +EditorServiceBase::EditorServiceBase (lay::LayoutView *view) + : lay::ViewService (view->view_object_widget ()), + lay::Editable (view) +{ + // .. nothing yet .. +} + +EditorServiceBase::~EditorServiceBase () +{ + clear_mouse_cursors (); +} + +void +EditorServiceBase::add_mouse_cursor (const db::DPoint &pt, bool emphasize) +{ + m_mouse_cursor_markers.push_back (new MouseCursorViewObject (widget (), pt, emphasize)); +} + +void +EditorServiceBase::add_edge_marker (const db::DEdge &e, bool emphasize) +{ + m_mouse_cursor_markers.push_back (new EdgeMarkerViewObject (widget (), e, emphasize)); +} + +void +EditorServiceBase::clear_mouse_cursors () +{ + for (std::vector::iterator r = m_mouse_cursor_markers.begin (); r != m_mouse_cursor_markers.end (); ++r) { + delete *r; + } + m_mouse_cursor_markers.clear (); +} + +} diff --git a/src/laybasic/laybasic/layEditorServiceBase.h b/src/laybasic/laybasic/layEditorServiceBase.h new file mode 100644 index 000000000..1c28ccbee --- /dev/null +++ b/src/laybasic/laybasic/layEditorServiceBase.h @@ -0,0 +1,77 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_layEditorServiceBase +#define HDR_layEditorServiceBase + +#include "laybasicCommon.h" +#include "layEditable.h" +#include "layViewObject.h" + +namespace lay +{ + +/** + * @brief A generic base class for an editor service + * + * This class offers common services such as a mouse cursor. + */ +class LAYBASIC_PUBLIC EditorServiceBase + : public lay::ViewService, + public lay::Editable +{ +public: + /** + * @brief Constructor + */ + EditorServiceBase (lay::LayoutView *view); + + /** + * @brief Destructor + */ + ~EditorServiceBase (); + + /** + * @brief Adds a mouse cursor to the given point + */ + void add_mouse_cursor (const db::DPoint &pt, bool emphasize = false); + + /** + * @brief Adds an edge marker for the given edge + */ + void add_edge_marker (const db::DEdge &e, bool emphasize); + + /** + * @brief Resets the mouse cursor + */ + void clear_mouse_cursors (); + +private: + // The marker representing the mouse cursor + std::vector m_mouse_cursor_markers; +}; + +} + +#endif + diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index 2fef55427..38963208b 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -118,6 +118,7 @@ SOURCES = \ layEditorOptionsFrame.cc \ layEditorOptionsPage.cc \ layEditorOptionsPages.cc \ + layEditorServiceBase.cc \ layFileDialog.cc \ layFinder.cc \ layFixedFont.cc \ @@ -222,6 +223,7 @@ HEADERS = \ layEditorOptionsFrame.h \ layEditorOptionsPage.h \ layEditorOptionsPages.h \ + layEditorServiceBase.h \ layFileDialog.h \ layFinder.h \ layFixedFont.h \ From df1583b5db83658c42dff9e9368fb3ea1827d9c4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 7 Sep 2020 23:19:44 +0200 Subject: [PATCH 083/113] WIP: config options for mouse tracking options. --- src/edt/edt/edtService.cc | 6 +- src/edt/edt/edtService.h | 17 -- src/lay/lay/layMainWindow.cc | 1 + .../laybasic/LayoutViewConfigPage2d.ui | 151 ++++++++++++++++++ src/laybasic/laybasic/layEditorServiceBase.cc | 110 +++++++++++-- src/laybasic/laybasic/layEditorServiceBase.h | 42 ++++- .../laybasic/layLayoutViewConfigPages.cc | 40 +++++ .../laybasic/layLayoutViewConfigPages.h | 19 ++- src/laybasic/laybasic/laybasic.pro | 1 + src/laybasic/laybasic/laybasicConfig.h | 3 + 10 files changed, 354 insertions(+), 36 deletions(-) create mode 100644 src/laybasic/laybasic/LayoutViewConfigPage2d.ui diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index 4fad85754..904c91574 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -62,7 +62,6 @@ ac_from_buttons (unsigned int buttons) Service::Service (db::Manager *manager, lay::LayoutView *view, db::ShapeIterator::flags_type flags) : lay::EditorServiceBase (view), - lay::Plugin (view), db::Object (manager), mp_view (view), mp_transient_marker (0), @@ -83,7 +82,6 @@ Service::Service (db::Manager *manager, lay::LayoutView *view, db::ShapeIterator Service::Service (db::Manager *manager, lay::LayoutView *view) : lay::EditorServiceBase (view), - lay::Plugin (view), db::Object (manager), mp_view (view), mp_transient_marker (0), @@ -261,6 +259,8 @@ Service::configure (const std::string &name, const std::string &value) tl::from_string (value, m_hier_copy_mode); service_configuration_changed (); + } else { + lay::EditorServiceBase::configure (name, value); } return false; // not taken @@ -813,7 +813,7 @@ Service::activated () void Service::deactivated () { - clear_mouse_cursors (); + lay::EditorServiceBase::deactivated (); edit_cancel (); diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h index 22ff094e3..b24871cf6 100644 --- a/src/edt/edt/edtService.h +++ b/src/edt/edt/edtService.h @@ -72,7 +72,6 @@ std::map pcell_parameters_from_string (const std::stri class EDT_PUBLIC Service : public lay::EditorServiceBase, - public lay::Plugin, public db::Object { public: @@ -216,22 +215,6 @@ public: return m_color; } - /** - * @brief Obtain the lay::ViewService interface - */ - lay::ViewService *view_service_interface () - { - return this; - } - - /** - * @brief Obtain the lay::Editable interface - */ - lay::Editable *editable_interface () - { - return this; - } - /** * @brief Get the selection container */ diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 03f9732c7..425f8485f 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -4236,6 +4236,7 @@ public: menu_entries.push_back (lay::menu_item ("cm_reset_window_state", "reset_window_state", at, tl::to_string (QObject::tr ("Restore Window")))), menu_entries.push_back (lay::separator ("selection_group", at)); menu_entries.push_back (lay::config_menu_item ("transient_selection", at, tl::to_string (QObject::tr ("Highlight Object Under Mouse")), cfg_sel_transient_mode, "?")); + menu_entries.push_back (lay::config_menu_item ("mouse_tracking", at, tl::to_string (QObject::tr ("Mouse tracking")), cfg_tracking_cursor_enabled, "?")); at = "help_menu.end"; menu_entries.push_back (lay::menu_item ("cm_show_all_tips", "show_all_tips", at, tl::to_string (QObject::tr ("Show All Tips")))); diff --git a/src/laybasic/laybasic/LayoutViewConfigPage2d.ui b/src/laybasic/laybasic/LayoutViewConfigPage2d.ui new file mode 100644 index 000000000..0a9fc17f5 --- /dev/null +++ b/src/laybasic/laybasic/LayoutViewConfigPage2d.ui @@ -0,0 +1,151 @@ + + + LayoutViewConfigPage2d + + + + 0 + 0 + 608 + 318 + + + + Settings + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + Mouse tracking + + + true + + + + 9 + + + 9 + + + 9 + + + 9 + + + 6 + + + + + The color in which the rulers are drawn + + + + + + + + + + Qt::Horizontal + + + + 71 + 31 + + + + + + + + Cursor color + + + + + + + With mouse tracking enabled, a cursor will appear which indicates the snapped mouse position and whether the mouse snaps to objects. + + + true + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + lay::ColorButton + QPushButton +
layWidgets.h
+
+
+ + color_pb + + + +
diff --git a/src/laybasic/laybasic/layEditorServiceBase.cc b/src/laybasic/laybasic/layEditorServiceBase.cc index e05df5bec..1d8aad213 100644 --- a/src/laybasic/laybasic/layEditorServiceBase.cc +++ b/src/laybasic/laybasic/layEditorServiceBase.cc @@ -23,6 +23,8 @@ #include "layEditorServiceBase.h" #include "layViewport.h" #include "layLayoutView.h" +#include "laybasicConfig.h" +#include "layConverters.h" namespace lay { @@ -47,15 +49,50 @@ make_circle (double r, const db::DPoint ¢er, db::DPolygon &poly, bool as_hol } } -class MouseCursorViewObject +class TrackingCursorBase : public lay::ViewObject { public: - MouseCursorViewObject (lay::ViewObjectWidget *widget, const db::DPoint &pt, bool solid) - : lay::ViewObject (widget, false), m_pt (pt), m_solid (solid) + TrackingCursorBase (lay::EditorServiceBase *service, lay::ViewObjectWidget *widget) + : lay::ViewObject (widget, false), mp_service (service) { } + uint32_t cursor_color (lay::ViewObjectCanvas &canvas) const + { + QColor color; + if (mp_service) { + color = mp_service->tracking_cursor_color (); + } + if (! color.isValid ()) { + color = canvas.foreground_color (); + } + return color.rgb (); + } + virtual void render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas) + { + if (mp_service && mp_service->tracking_cursor_enabled ()) { + do_render (vp, canvas); + } + } + +protected: + virtual void do_render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas) = 0; + +private: + lay::EditorServiceBase *mp_service; +}; + +class MouseCursorViewObject + : public TrackingCursorBase +{ +public: + MouseCursorViewObject (lay::EditorServiceBase *service, lay::ViewObjectWidget *widget, const db::DPoint &pt, bool solid) + : TrackingCursorBase (service, widget), m_pt (pt), m_solid (solid) + { } + +protected: + virtual void do_render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas) { int dither_pattern = 0; // solid int cross_dither_pattern = 6; // dotted @@ -64,10 +101,10 @@ public: std::vector ops; ops.resize (1); - ops[0] = lay::ViewOp (canvas.foreground_color ().rgb (), lay::ViewOp::Copy, 0, (unsigned int) dither_pattern, 0, lay::ViewOp::Rect, lw, 0); + ops[0] = lay::ViewOp (cursor_color (canvas), lay::ViewOp::Copy, 0, (unsigned int) dither_pattern, 0, lay::ViewOp::Rect, lw, 0); lay::CanvasPlane *plane = canvas.plane (ops); - ops[0] = lay::ViewOp (canvas.foreground_color ().rgb (), lay::ViewOp::Copy, 0, (unsigned int) cross_dither_pattern, 0, lay::ViewOp::Rect, lw, 0); + ops[0] = lay::ViewOp (cursor_color (canvas), lay::ViewOp::Copy, 0, (unsigned int) cross_dither_pattern, 0, lay::ViewOp::Rect, lw, 0); lay::CanvasPlane *cross_plane = canvas.plane (ops); lay::Renderer &r = canvas.renderer (); @@ -95,14 +132,15 @@ private: }; class EdgeMarkerViewObject - : public lay::ViewObject + : public TrackingCursorBase { public: - EdgeMarkerViewObject (lay::ViewObjectWidget *widget, const db::DEdge &edge, bool solid) - : lay::ViewObject (widget, false), m_edge (edge), m_solid (solid) + EdgeMarkerViewObject (lay::EditorServiceBase *service, lay::ViewObjectWidget *widget, const db::DEdge &edge, bool solid) + : TrackingCursorBase (service, widget), m_edge (edge), m_solid (solid) { } - virtual void render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas) +protected: + virtual void do_render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas) { if (m_edge.is_degenerate ()) { return; @@ -115,10 +153,10 @@ public: std::vector ops; ops.resize (1); - ops[0] = lay::ViewOp (canvas.foreground_color ().rgb (), lay::ViewOp::Copy, solid_style, 0, 0, lay::ViewOp::Rect, lw, 0); + ops[0] = lay::ViewOp (cursor_color (canvas), lay::ViewOp::Copy, solid_style, 0, 0, lay::ViewOp::Rect, lw, 0); lay::CanvasPlane *arrow_plane = canvas.plane (ops); - ops[0] = lay::ViewOp (canvas.foreground_color ().rgb (), lay::ViewOp::Copy, m_solid ? solid_style : dashed_style, 1, 0, lay::ViewOp::Rect, lw, 0); + ops[0] = lay::ViewOp (cursor_color (canvas), lay::ViewOp::Copy, m_solid ? solid_style : dashed_style, 1, 0, lay::ViewOp::Rect, lw, 0); lay::CanvasPlane *edge_plane = canvas.plane (ops); lay::Renderer &r = canvas.renderer (); @@ -166,7 +204,9 @@ private: EditorServiceBase::EditorServiceBase (lay::LayoutView *view) : lay::ViewService (view->view_object_widget ()), - lay::Editable (view) + lay::Editable (view), + lay::Plugin (view), + m_cursor_enabled (true) { // .. nothing yet .. } @@ -179,13 +219,13 @@ EditorServiceBase::~EditorServiceBase () void EditorServiceBase::add_mouse_cursor (const db::DPoint &pt, bool emphasize) { - m_mouse_cursor_markers.push_back (new MouseCursorViewObject (widget (), pt, emphasize)); + m_mouse_cursor_markers.push_back (new MouseCursorViewObject (this, widget (), pt, emphasize)); } void EditorServiceBase::add_edge_marker (const db::DEdge &e, bool emphasize) { - m_mouse_cursor_markers.push_back (new EdgeMarkerViewObject (widget (), e, emphasize)); + m_mouse_cursor_markers.push_back (new EdgeMarkerViewObject (this, widget (), e, emphasize)); } void @@ -197,4 +237,46 @@ EditorServiceBase::clear_mouse_cursors () m_mouse_cursor_markers.clear (); } +bool +EditorServiceBase::configure (const std::string &name, const std::string &value) +{ + bool needs_update = false; + + if (name == cfg_tracking_cursor_color) { + + QColor color; + lay::ColorConverter ().from_string (value, color); + + if (color != m_cursor_color) { + m_cursor_color = color; + needs_update = true; + } + + } else if (name == cfg_tracking_cursor_enabled) { + + bool f = m_cursor_enabled; + tl::from_string (value, f); + if (f != m_cursor_enabled) { + m_cursor_enabled = f; + needs_update = true; + } + + } + + if (needs_update) { + for (std::vector::iterator r = m_mouse_cursor_markers.begin (); r != m_mouse_cursor_markers.end (); ++r) { + (*r)->redraw (); + } + } + + // NOTE: we don't take the value as other services may be interested too. + return false; +} + +void +EditorServiceBase::deactivated () +{ + clear_mouse_cursors (); +} + } diff --git a/src/laybasic/laybasic/layEditorServiceBase.h b/src/laybasic/laybasic/layEditorServiceBase.h index 1c28ccbee..962388e15 100644 --- a/src/laybasic/laybasic/layEditorServiceBase.h +++ b/src/laybasic/laybasic/layEditorServiceBase.h @@ -27,6 +27,7 @@ #include "laybasicCommon.h" #include "layEditable.h" #include "layViewObject.h" +#include "layPlugin.h" namespace lay { @@ -38,7 +39,8 @@ namespace lay */ class LAYBASIC_PUBLIC EditorServiceBase : public lay::ViewService, - public lay::Editable + public lay::Editable, + public lay::Plugin { public: /** @@ -51,6 +53,22 @@ public: */ ~EditorServiceBase (); + /** + * @brief Obtain the lay::ViewService interface + */ + lay::ViewService *view_service_interface () + { + return this; + } + + /** + * @brief Obtain the lay::Editable interface + */ + lay::Editable *editable_interface () + { + return this; + } + /** * @brief Adds a mouse cursor to the given point */ @@ -66,9 +84,31 @@ public: */ void clear_mouse_cursors (); + /** + * @brief Gets the tracking cursor color + */ + QColor tracking_cursor_color () const + { + return m_cursor_color; + } + + /** + * @brief Gets a value indicating whether the tracking cursor is enabled + */ + bool tracking_cursor_enabled () const + { + return m_cursor_enabled; + } + +protected: + virtual bool configure (const std::string &name, const std::string &value); + virtual void deactivated (); + private: // The marker representing the mouse cursor std::vector m_mouse_cursor_markers; + QColor m_cursor_color; + bool m_cursor_enabled; }; } diff --git a/src/laybasic/laybasic/layLayoutViewConfigPages.cc b/src/laybasic/laybasic/layLayoutViewConfigPages.cc index 30c4aa6bb..d2e66440e 100644 --- a/src/laybasic/laybasic/layLayoutViewConfigPages.cc +++ b/src/laybasic/laybasic/layLayoutViewConfigPages.cc @@ -31,6 +31,7 @@ #include "ui_LayoutViewConfigPage2a.h" #include "ui_LayoutViewConfigPage2b.h" #include "ui_LayoutViewConfigPage2c.h" +#include "ui_LayoutViewConfigPage2d.h" #include "ui_LayoutViewConfigPage3a.h" #include "ui_LayoutViewConfigPage3b.h" #include "ui_LayoutViewConfigPage3c.h" @@ -389,6 +390,42 @@ LayoutViewConfigPage2c::commit (lay::Dispatcher *root) root->config_set (cfg_search_range, (unsigned int) mp_ui->search_range_spinbx->value ()); } +// ------------------------------------------------------------ +// LayoutConfigPage2d implementation + +LayoutViewConfigPage2d::LayoutViewConfigPage2d (QWidget *parent) + : lay::ConfigPage (parent) +{ + mp_ui = new Ui::LayoutViewConfigPage2d (); + mp_ui->setupUi (this); +} + +LayoutViewConfigPage2d::~LayoutViewConfigPage2d () +{ + delete mp_ui; + mp_ui = 0; +} + +void +LayoutViewConfigPage2d::setup (lay::Dispatcher *root) +{ + QColor color; + root->config_get (cfg_tracking_cursor_color, color, lay::ColorConverter ()); + mp_ui->color_pb->set_color (color); + + bool enabled = 0; + root->config_get (cfg_tracking_cursor_enabled, enabled); + mp_ui->tracking_cb->setChecked (enabled); +} + +void +LayoutViewConfigPage2d::commit (lay::Dispatcher *root) +{ + lay::ColorConverter cc; + root->config_set (cfg_tracking_cursor_color, mp_ui->color_pb->get_color (), cc); + root->config_set (cfg_tracking_cursor_enabled, mp_ui->tracking_cb->isChecked ()); +} + // ------------------------------------------------------------ // LayoutConfigPage3a implementation @@ -1516,6 +1553,8 @@ public: options.push_back (std::pair (cfg_sel_halo, "true")); options.push_back (std::pair (cfg_sel_transient_mode, "true")); options.push_back (std::pair (cfg_sel_inside_pcells_mode, "false")); + options.push_back (std::pair (cfg_tracking_cursor_enabled, "true")); + options.push_back (std::pair (cfg_tracking_cursor_color, cc.to_string (QColor ()))); options.push_back (std::pair (cfg_background_color, cc.to_string (QColor ()))); options.push_back (std::pair (cfg_ctx_color, cc.to_string (QColor ()))); options.push_back (std::pair (cfg_ctx_dimming, "50")); @@ -1571,6 +1610,7 @@ public: pages.push_back (std::make_pair (tl::to_string (QObject::tr ("Display|Optimization")), new LayoutViewConfigPage3f (parent))); pages.push_back (std::make_pair (tl::to_string (QObject::tr ("Application|Selection")), new LayoutViewConfigPage2c (parent))); + pages.push_back (std::make_pair (tl::to_string (QObject::tr ("Application|Tracking")), new LayoutViewConfigPage2d (parent))); pages.push_back (std::make_pair (tl::to_string (QObject::tr ("Application|Layer Properties")), new LayoutViewConfigPage5 (parent))); pages.push_back (std::make_pair (tl::to_string (QObject::tr ("Application|Units")), new LayoutViewConfigPage3c (parent))); diff --git a/src/laybasic/laybasic/layLayoutViewConfigPages.h b/src/laybasic/laybasic/layLayoutViewConfigPages.h index 10ffb7a23..1170a6a19 100644 --- a/src/laybasic/laybasic/layLayoutViewConfigPages.h +++ b/src/laybasic/laybasic/layLayoutViewConfigPages.h @@ -38,6 +38,7 @@ namespace Ui { class LayoutViewConfigPage2a; class LayoutViewConfigPage2b; class LayoutViewConfigPage2c; + class LayoutViewConfigPage2d; class LayoutViewConfigPage3a; class LayoutViewConfigPage3b; class LayoutViewConfigPage3c; @@ -138,7 +139,23 @@ private: Ui::LayoutViewConfigPage2c *mp_ui; }; -class LayoutViewConfigPage3a +class LayoutViewConfigPage2d + : public lay::ConfigPage +{ +Q_OBJECT + +public: + LayoutViewConfigPage2d (QWidget *parent); + ~LayoutViewConfigPage2d (); + + virtual void setup (lay::Dispatcher *root); + virtual void commit (lay::Dispatcher *root); + +private: + Ui::LayoutViewConfigPage2d *mp_ui; +}; + +class LayoutViewConfigPage3a : public lay::ConfigPage { Q_OBJECT diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index 38963208b..449aa1f29 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -31,6 +31,7 @@ FORMS = \ LayoutViewConfigPage2a.ui \ LayoutViewConfigPage2b.ui \ LayoutViewConfigPage2c.ui \ + LayoutViewConfigPage2d.ui \ LayoutViewConfigPage3a.ui \ LayoutViewConfigPage3b.ui \ LayoutViewConfigPage3c.ui \ diff --git a/src/laybasic/laybasic/laybasicConfig.h b/src/laybasic/laybasic/laybasicConfig.h index 71a5a2019..dc772dea0 100644 --- a/src/laybasic/laybasic/laybasicConfig.h +++ b/src/laybasic/laybasic/laybasicConfig.h @@ -75,6 +75,9 @@ static const std::string cfg_sel_line_style ("sel-line-style"); static const std::string cfg_sel_transient_mode ("sel-transient-mode"); static const std::string cfg_sel_inside_pcells_mode ("sel-inside-pcells-mode"); +static const std::string cfg_tracking_cursor_color ("tracking-cursor-color"); +static const std::string cfg_tracking_cursor_enabled ("tracking-cursor-enabled"); + static const std::string cfg_markers_visible ("markers-visible"); static const std::string cfg_min_inst_label_size ("min-inst-label-size"); From 316cf771751091687f38ff381b66c658b3f56bf3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 7 Sep 2020 23:49:42 +0200 Subject: [PATCH 084/113] WIP: Ruler now has a mouse snapping indicator too. --- src/ant/ant/antService.cc | 48 ++++++++++++++----- src/ant/ant/antService.h | 12 ++--- src/edt/edt/edtServiceImpl.cc | 12 +---- src/laybasic/laybasic/layEditorServiceBase.cc | 15 ++++++ src/laybasic/laybasic/layEditorServiceBase.h | 5 ++ 5 files changed, 63 insertions(+), 29 deletions(-) diff --git a/src/ant/ant/antService.cc b/src/ant/ant/antService.cc index 282386c6a..62f1dcc36 100644 --- a/src/ant/ant/antService.cc +++ b/src/ant/ant/antService.cc @@ -706,9 +706,7 @@ View::render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas) // ant::Service implementation Service::Service (db::Manager *manager, lay::LayoutView *view) - : lay::ViewService (view->view_object_widget ()), - lay::Editable (view), - lay::Plugin (view), + : lay::EditorServiceBase (view), lay::Drawing (1/*number of planes*/, view->drawings ()), db::Object (manager), m_halo (true), @@ -801,7 +799,7 @@ Service::configure (const std::string &name, const std::string &value) m_current_template = n; } else { - taken = false; + lay::EditorServiceBase::configure (name, value); } return taken; @@ -1513,9 +1511,23 @@ Service::create_measure_ruler (const db::DPoint &pt, lay::angle_constraint_type } } -bool +bool Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) { + if (prio) { + + lay::PointSnapToObjectResult snap_details; + if (m_drawing) { + snap_details = snap2_details (m_p1, p, mp_active_ruler->ruler (), ac_from_buttons (buttons)); + } else { + const ant::Template &tpl = current_template (); + snap_details = snap1_details (p, m_obj_snap && tpl.snap ()); + } + + mouse_cursor_from_snap_details (snap_details); + + } + if (m_drawing && prio) { set_cursor (lay::Cursor::cross); @@ -1532,12 +1544,14 @@ Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) void Service::deactivated () { + lay::EditorServiceBase::deactivated (); + drag_cancel (); clear_transient_selection (); } -std::pair -Service::snap1 (const db::DPoint &p, bool obj_snap) +lay::PointSnapToObjectResult +Service::snap1_details (const db::DPoint &p, bool obj_snap) { db::DVector g; if (m_grid_snap) { @@ -1545,13 +1559,19 @@ Service::snap1 (const db::DPoint &p, bool obj_snap) } double snap_range = widget ()->mouse_event_trans ().inverted ().ctrans (m_snap_range); - lay::PointSnapToObjectResult res = lay::obj_snap (obj_snap ? mp_view : 0, p, g, snap_range); + return lay::obj_snap (obj_snap ? mp_view : 0, p, g, snap_range); +} + +std::pair +Service::snap1 (const db::DPoint &p, bool obj_snap) +{ + lay::PointSnapToObjectResult res = snap1_details (p, obj_snap); return std::make_pair (res.object_snap != lay::PointSnapToObjectResult::NoObject, res.snapped_point); } -std::pair -Service::snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac) +lay::PointSnapToObjectResult +Service::snap2_details (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac) { db::DVector g; if (m_grid_snap) { @@ -1561,7 +1581,13 @@ Service::snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *o double snap_range = widget ()->mouse_event_trans ().inverted ().ctrans (m_snap_range); lay::angle_constraint_type snap_mode = ac == lay::AC_Global ? (obj->angle_constraint () == lay::AC_Global ? m_snap_mode : obj->angle_constraint ()) : ac; - lay::PointSnapToObjectResult res = lay::obj_snap (m_obj_snap && obj->snap () ? mp_view : 0, p1, p2, g, snap_mode, snap_range); + return lay::obj_snap (m_obj_snap && obj->snap () ? mp_view : 0, p1, p2, g, snap_mode, snap_range); +} + +std::pair +Service::snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac) +{ + lay::PointSnapToObjectResult res = snap2_details (p1, p2, obj, ac); return std::make_pair (res.object_snap != lay::PointSnapToObjectResult::NoObject, res.snapped_point); } diff --git a/src/ant/ant/antService.h b/src/ant/ant/antService.h index ad7a0eaa8..0f6d3b05b 100644 --- a/src/ant/ant/antService.h +++ b/src/ant/ant/antService.h @@ -27,9 +27,7 @@ #include "antCommon.h" -#include "layViewObject.h" -#include "layEditable.h" -#include "layPlugin.h" +#include "layEditorServiceBase.h" #include "layDrawing.h" #include "laySnap.h" #include "layAnnotationShapes.h" @@ -180,9 +178,7 @@ private: // ------------------------------------------------------------- class ANT_PUBLIC Service - : public lay::ViewService, - public lay::Editable, - public lay::Plugin, + : public lay::EditorServiceBase, public lay::Drawing, public db::Object { @@ -529,8 +525,10 @@ private: unsigned int m_current_template; std::pair snap1 (const db::DPoint &p, bool obj_snap); + lay::PointSnapToObjectResult snap1_details (const db::DPoint &p, bool obj_snap); std::pair snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac); - + lay::PointSnapToObjectResult snap2_details (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac); + const ant::Template ¤t_template () const; void show_message (); diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index b34ce269b..95bb95c07 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -417,17 +417,7 @@ void PolygonService::do_mouse_move_inactive (const db::DPoint &p) { lay::PointSnapToObjectResult snap_details = snap2_details (p); - - clear_mouse_cursors (); - - add_mouse_cursor (snap_details.snapped_point, - snap_details.object_snap == lay::PointSnapToObjectResult::ObjectVertex || - (snap_details.object_snap == lay::PointSnapToObjectResult::ObjectUnspecific && snap_details.object_ref.is_degenerate ())); - - if (snap_details.object_snap == lay::PointSnapToObjectResult::ObjectEdge || - (snap_details.object_snap == lay::PointSnapToObjectResult::ObjectUnspecific && ! snap_details.object_ref.is_degenerate ())) { - add_edge_marker (snap_details.object_ref, false); - } + mouse_cursor_from_snap_details (snap_details); } void diff --git a/src/laybasic/laybasic/layEditorServiceBase.cc b/src/laybasic/laybasic/layEditorServiceBase.cc index 1d8aad213..51f93429d 100644 --- a/src/laybasic/laybasic/layEditorServiceBase.cc +++ b/src/laybasic/laybasic/layEditorServiceBase.cc @@ -237,6 +237,21 @@ EditorServiceBase::clear_mouse_cursors () m_mouse_cursor_markers.clear (); } +void +EditorServiceBase::mouse_cursor_from_snap_details (const lay::PointSnapToObjectResult &snap_details) +{ + clear_mouse_cursors (); + + add_mouse_cursor (snap_details.snapped_point, + snap_details.object_snap == lay::PointSnapToObjectResult::ObjectVertex || + (snap_details.object_snap == lay::PointSnapToObjectResult::ObjectUnspecific && snap_details.object_ref.is_degenerate ())); + + if (snap_details.object_snap == lay::PointSnapToObjectResult::ObjectEdge || + (snap_details.object_snap == lay::PointSnapToObjectResult::ObjectUnspecific && ! snap_details.object_ref.is_degenerate ())) { + add_edge_marker (snap_details.object_ref, false); + } +} + bool EditorServiceBase::configure (const std::string &name, const std::string &value) { diff --git a/src/laybasic/laybasic/layEditorServiceBase.h b/src/laybasic/laybasic/layEditorServiceBase.h index 962388e15..7735b9deb 100644 --- a/src/laybasic/laybasic/layEditorServiceBase.h +++ b/src/laybasic/laybasic/layEditorServiceBase.h @@ -84,6 +84,11 @@ public: */ void clear_mouse_cursors (); + /** + * @brief Provides a nice mouse tracking cursor from the given snap details + */ + void mouse_cursor_from_snap_details (const lay::PointSnapToObjectResult &snap_details); + /** * @brief Gets the tracking cursor color */ From 655c8c76587a756147eb4bf54492dab4e94a1119 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 8 Sep 2020 00:06:06 +0200 Subject: [PATCH 085/113] WIP: mouse tracking on instance, box, path and text editor. --- src/edt/edt/edtPartialService.cc | 9 ++++--- src/edt/edt/edtPartialService.h | 8 ++---- src/edt/edt/edtPlugin.cc | 2 ++ src/edt/edt/edtServiceImpl.cc | 44 +++++++++++++++++++++++++++++--- src/edt/edt/edtServiceImpl.h | 4 +++ 5 files changed, 53 insertions(+), 14 deletions(-) diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index 989c4ae12..073b3a45d 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -1010,9 +1010,7 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, PartialService::PartialService (db::Manager *manager, lay::LayoutView *view, lay::Dispatcher *root) : QObject (), - lay::ViewService (view->view_object_widget ()), - lay::Editable (view), - lay::Plugin (view), + lay::EditorServiceBase (view), db::Object (manager), mp_view (view), mp_root (root), @@ -1531,13 +1529,16 @@ PartialService::wheel_event (int /*delta*/, bool /*horizonal*/, const db::DPoint bool PartialService::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) { + clear_mouse_cursors (); + if (m_dragging) { set_cursor (lay::Cursor::size_all); m_alt_ac = ac_from_buttons (buttons); - // drag the vertex or edge/segment + // @@@ Provide object snapping also when moving edges, provide mouse tracking + // drag the vertex or edge/segment if (is_single_point_selection ()) { // for a single selected point, m_start is the original position and we snap the target - // thus, we can bring the point on grid or to an object's edge or vertex diff --git a/src/edt/edt/edtPartialService.h b/src/edt/edt/edtPartialService.h index 2c22ac21c..713d2dbfd 100644 --- a/src/edt/edt/edtPartialService.h +++ b/src/edt/edt/edtPartialService.h @@ -25,10 +25,8 @@ #ifndef HDR_edtPartialService #define HDR_edtPartialService -#include "layEditable.h" +#include "layEditorServiceBase.h" #include "layObjectInstPath.h" -#include "layLayoutView.h" -#include "layPlugin.h" #include "layViewObject.h" #include "layRubberBox.h" #include "laySnap.h" @@ -140,9 +138,7 @@ struct EdgeWithIndex */ class PartialService : public QObject, - public lay::ViewService, - public lay::Editable, - public lay::Plugin, + public lay::EditorServiceBase, public db::Object { Q_OBJECT diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index b6596218f..7caa8288b 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -23,6 +23,8 @@ #include "layTipDialog.h" #include "layEditorOptionsPages.h" +#include "layDispatcher.h" +#include "layLayoutView.h" #include "edtPlugin.h" #include "edtConfig.h" #include "edtService.h" diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index 95bb95c07..c15bc6106 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -728,9 +728,18 @@ BoxService::update_marker () } } -void +void +BoxService::do_mouse_move_inactive (const db::DPoint &p) +{ + lay::PointSnapToObjectResult snap_details = snap2_details (p); + mouse_cursor_from_snap_details (snap_details); +} + +void BoxService::do_mouse_move (const db::DPoint &p) { + do_mouse_move_inactive (p); + set_cursor (lay::Cursor::cross); m_p2 = snap2 (p); update_marker (); @@ -826,9 +835,18 @@ TextService::do_activated () return true; // start editing immediately } -void +void +TextService::do_mouse_move_inactive (const db::DPoint &p) +{ + lay::PointSnapToObjectResult snap_details = snap2_details (p); + mouse_cursor_from_snap_details (snap_details); +} + +void TextService::do_mouse_move (const db::DPoint &p) { + do_mouse_move_inactive (p); + set_cursor (lay::Cursor::cross); m_text.trans (db::DTrans (m_rot, snap2 (p) - db::DPoint ())); update_marker (); @@ -1010,9 +1028,18 @@ PathService::set_last_point (const db::DPoint &p) } } -void +void +PathService::do_mouse_move_inactive (const db::DPoint &p) +{ + lay::PointSnapToObjectResult snap_details = snap2_details (p); + mouse_cursor_from_snap_details (snap_details); +} + +void PathService::do_mouse_move (const db::DPoint &p) { + do_mouse_move_inactive (p); + set_cursor (lay::Cursor::cross); if (m_points.size () >= 2) { set_last_point (p); @@ -1454,9 +1481,18 @@ InstService::make_cell (const lay::CellView &cv) return std::pair (true, inst_cell_index); } -void +void +InstService::do_mouse_move_inactive (const db::DPoint &p) +{ + clear_mouse_cursors (); + add_mouse_cursor (snap (p)); +} + +void InstService::do_mouse_move (const db::DPoint &p) { + do_mouse_move_inactive (p); + set_cursor (lay::Cursor::cross); const lay::CellView &cv = view ()->cellview (m_cv_index); diff --git a/src/edt/edt/edtServiceImpl.h b/src/edt/edt/edtServiceImpl.h index 15865e2d6..f10c82a30 100644 --- a/src/edt/edt/edtServiceImpl.h +++ b/src/edt/edt/edtServiceImpl.h @@ -121,6 +121,7 @@ public: virtual lay::PropertiesPage *properties_page (db::Manager *manager, QWidget *parent); virtual void do_begin_edit (const db::DPoint &p); virtual void do_mouse_move (const db::DPoint &p); + virtual void do_mouse_move_inactive (const db::DPoint &p); virtual bool do_mouse_click (const db::DPoint &p); virtual void do_finish_edit (); virtual void do_cancel_edit (); @@ -147,6 +148,7 @@ public: virtual void do_begin_edit (const db::DPoint &p); virtual void do_mouse_transform (const db::DPoint &p, db::DFTrans trans); virtual void do_mouse_move (const db::DPoint &p); + virtual void do_mouse_move_inactive (const db::DPoint &p); virtual bool do_mouse_click (const db::DPoint &p); virtual void do_finish_edit (); virtual void do_cancel_edit (); @@ -178,6 +180,7 @@ public: virtual void do_begin_edit (const db::DPoint &p); virtual void do_mouse_move (const db::DPoint &p); virtual bool do_mouse_click (const db::DPoint &p); + virtual void do_mouse_move_inactive (const db::DPoint &p); virtual void do_finish_edit (); virtual void do_cancel_edit (); virtual bool do_activated (); @@ -210,6 +213,7 @@ public: virtual lay::PropertiesPage *properties_page (db::Manager *manager, QWidget *parent); virtual void do_begin_edit (const db::DPoint &p); + virtual void do_mouse_move_inactive (const db::DPoint &p); virtual void do_mouse_move (const db::DPoint &p); virtual bool do_mouse_click (const db::DPoint &p); virtual void do_mouse_transform (const db::DPoint &p, db::DFTrans trans); From d20aac77c6102773c900074f6d05b7c7e018688f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 13 Sep 2020 22:28:02 +0200 Subject: [PATCH 086/113] Snap indicator when moving point in partial mode (no indicator for edge move yet as edge-to-object mapping isn't implemented yet) --- src/edt/edt/edtPartialService.cc | 16 ++++++++++------ src/edt/edt/edtPartialService.h | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index 073b3a45d..14da15bc9 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -1293,11 +1293,11 @@ PartialService::snap (const db::DVector &v) const const int sr_pixels = 8; // TODO: make variable -db::DPoint +lay::PointSnapToObjectResult PartialService::snap2 (const db::DPoint &p) const { double snap_range = widget ()->mouse_event_trans ().inverted ().ctrans (sr_pixels); - return lay::obj_snap (m_snap_to_objects ? view () : 0, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, snap_range).snapped_point; + return lay::obj_snap (m_snap_to_objects ? view () : 0, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, snap_range); } void @@ -1537,15 +1537,19 @@ PartialService::mouse_move_event (const db::DPoint &p, unsigned int buttons, boo m_alt_ac = ac_from_buttons (buttons); - // @@@ Provide object snapping also when moving edges, provide mouse tracking + lay::PointSnapToObjectResult snap_details; + // drag the vertex or edge/segment if (is_single_point_selection ()) { - // for a single selected point, m_start is the original position and we snap the target - - // thus, we can bring the point on grid or to an object's edge or vertex - m_current = snap2 (p); + // for a single selected point, m_start is the original position and we snap the target - + // thus, we can bring the point on grid or to an object's edge or vertex + snap_details = snap2 (p); + m_current = snap_details.snapped_point; } else { m_current = m_start + snap (p - m_start); } + + mouse_cursor_from_snap_details (snap_details); selection_to_view (); m_alt_ac = lay::AC_Global; diff --git a/src/edt/edt/edtPartialService.h b/src/edt/edt/edtPartialService.h index 713d2dbfd..f38b994a0 100644 --- a/src/edt/edt/edtPartialService.h +++ b/src/edt/edt/edtPartialService.h @@ -322,7 +322,7 @@ private: db::DPoint snap (const db::DPoint &p) const; db::DVector snap (const db::DVector &p) const; - db::DPoint snap2 (const db::DPoint &p) const; + lay::PointSnapToObjectResult snap2 (const db::DPoint &p) const; void enter_edge (const EdgeWithIndex &e, size_t &nmarker, partial_objects::const_iterator sel, const std::map &new_points, const std::map &new_edges, const db::ICplxTrans >, const std::vector &tv, bool transient); void enter_vertices (size_t &nmarker, partial_objects::const_iterator sel, const std::map &new_points, const std::map &new_edges, const db::ICplxTrans >, const std::vector &tv, bool transient); From 9200c2924f6025ff895fc6ea3b5bf48be0b153da Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 15 Sep 2020 08:01:47 +0200 Subject: [PATCH 087/113] Fixed build. --- src/laybasic/laybasic/layEditorOptionsPages.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/laybasic/laybasic/layEditorOptionsPages.cc b/src/laybasic/laybasic/layEditorOptionsPages.cc index 009924898..0cb6ed5a0 100644 --- a/src/laybasic/laybasic/layEditorOptionsPages.cc +++ b/src/laybasic/laybasic/layEditorOptionsPages.cc @@ -27,7 +27,6 @@ #include "layPlugin.h" #include "layLayoutView.h" #include "layQtTools.h" -#include "ui_EditorOptionsGeneric.h" #include #include From edaacff59423f051a6bc61540da2c006433ea037 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 15 Sep 2020 19:26:03 +0200 Subject: [PATCH 088/113] Fixed build. --- src/laybasic/laybasic/layEditorOptionsPages.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/laybasic/laybasic/layEditorOptionsPages.cc b/src/laybasic/laybasic/layEditorOptionsPages.cc index 0cb6ed5a0..0cc55fb62 100644 --- a/src/laybasic/laybasic/layEditorOptionsPages.cc +++ b/src/laybasic/laybasic/layEditorOptionsPages.cc @@ -33,6 +33,7 @@ #include #include #include +#include namespace lay { From 66945c49441bc8dd842e789c67e01a93bdd3c8c5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 16 Sep 2020 01:17:32 +0200 Subject: [PATCH 089/113] Fixed build. --- src/edt/edt/edtEditorOptionsPages.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index 1182bcc42..5cf86b819 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -35,7 +35,6 @@ #include "layLayoutView.h" #include "layCellSelectionForm.h" #include "layQtTools.h" -#include "ui_EditorOptionsDialog.h" #include "ui_EditorOptionsGeneric.h" #include "ui_EditorOptionsPath.h" #include "ui_EditorOptionsText.h" From 15985c4ca8d12f2bb6ef33b2501425bc5115190c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 16 Sep 2020 22:23:43 +0200 Subject: [PATCH 090/113] Removed a duplicate definition --- .../lay_plugin/layLEFDEFImportDialogs.cc | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc b/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc index 1b0e6d2c2..765640e43 100644 --- a/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc +++ b/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc @@ -340,26 +340,6 @@ END_PROTECTED // ----------------------------------------------------------------------------------------------- // LEFDEF technology components editor -static void -indicate_error (QWidget *le, const tl::Exception *ex) -{ - // by the way, update the foreground color of the cell edit box as well (red, if not valid) - QPalette pl = le->palette (); - if (ex) { - pl.setColor (QPalette::Active, QPalette::Text, Qt::red); - pl.setColor (QPalette::Active, QPalette::Base, QColor (Qt::red).lighter (180)); - le->setToolTip (tl::to_qstring (ex->msg ())); - } else { - QWidget *pw = dynamic_cast (le->parent ()); - tl_assert (pw != 0); - pl.setColor (QPalette::Active, QPalette::Text, pw->palette ().color (QPalette::Text)); - pl.setColor (QPalette::Active, QPalette::Base, pw->palette ().color (QPalette::Base)); - le->setToolTip (QString ()); - } - le->setPalette (pl); -} - - LEFDEFReaderOptionsEditor::LEFDEFReaderOptionsEditor (QWidget *parent) : lay::StreamReaderOptionsPage (parent), mp_tech (0) { From 2f0fa28e5836f2ff2d2e92bf8b9ca083ec5ce7f2 Mon Sep 17 00:00:00 2001 From: Kazunari Sekigawa Date: Sat, 10 Oct 2020 07:09:34 +0900 Subject: [PATCH 091/113] * Updated the build system for Mac (#645) * Catalina env. uses Python 3.8 in MacPorts, Homebrew, and Anaconda3. --- macbuild/ReadMe.md | 46 ++-- macbuild/Resources/script-bundle-A.zip | Bin 4361604 -> 4361659 bytes macbuild/Resources/script-bundle-B.zip | Bin 4371595 -> 4371668 bytes macbuild/Resources/script-bundle-P.zip | Bin 4396762 -> 4396838 bytes macbuild/build4mac.py | 152 ++++++++------ macbuild/build4mac_env.py | 38 ++-- macbuild/build4mac_util.py | 146 +++++++++---- macbuild/macQAT.py | 278 +++++++++++++++++++++++++ macbuild/makeDMG4mac.py | 14 +- 9 files changed, 521 insertions(+), 153 deletions(-) create mode 100755 macbuild/macQAT.py diff --git a/macbuild/ReadMe.md b/macbuild/ReadMe.md index 4f87e9616..09344755b 100644 --- a/macbuild/ReadMe.md +++ b/macbuild/ReadMe.md @@ -1,4 +1,4 @@ -Relevant KLayout version: 0.26.5 +Relevant KLayout version: 0.26.7 # 1. Introduction This directory **`macbuild`** contains different files required for building KLayout (http://www.klayout.de/) version 0.26.1 or later for different 64-bit Mac OSXs including: @@ -72,16 +72,16 @@ $ [python] ./build4mac.py : MP26: use Ruby 2.6 from MacPorts | : HB27: use Ruby 2.7 from Homebrew | : Ana3: use Ruby 2.5 from Anaconda3 | - [-p|--python ] : case-insensitive type=['nil', 'Sys', 'MP37', 'HB37', 'Ana3'] | sys + [-p|--python ] : case-insensitive type=['nil', 'Sys', 'MP38', 'HB38', 'Ana3'] | sys : nil: don't bind Python | : Sys: use OS-bundled Python 2.7 [ElCapitan -- Catalina] | - : MP37: use Python 3.7 from MacPorts | - : HB37: use Python 3.7 from Homebrew | - : Ana3: use Python 3.7 from Anaconda3 | + : MP38: use Python 3.8 from MacPorts | + : HB38: use Python 3.8 from Homebrew | + : Ana3: use Python 3.8 from Anaconda3 | [-n|--noqtbinding] : don't create Qt bindings for ruby scripts | disabled [-m|--make