diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index fbe9bf596..847788b91 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -49,7 +49,7 @@ jobs:
- uses: hmarr/debug-action@v3
- name: Cancel Workflow Action
uses: styfle/cancel-workflow-action@0.12.1
- - uses: actions/checkout@v5
+ - uses: actions/checkout@v6
- name: ccache
if: matrix.os != 'ubuntu-24.04-arm'
uses: hendrikmuhs/ccache-action@v1.2
@@ -66,7 +66,7 @@ jobs:
mkdir -p $HOST_CCACHE_DIR
- name: Build wheels (ARM)
if: matrix.os == 'ubuntu-24.04-arm'
- uses: pypa/cibuildwheel@v3.2.1
+ uses: pypa/cibuildwheel@v3.3.0
env:
# override the default CentOS “yum install … ccache” and drop ccache
CIBW_BEFORE_ALL_LINUX: |
@@ -81,7 +81,7 @@ jobs:
- name: Build wheels (all other platforms)
if: matrix.os != 'ubuntu-24.04-arm'
- uses: pypa/cibuildwheel@v3.2.1
+ uses: pypa/cibuildwheel@v3.3.0
env:
CIBW_BUILD: ${{ matrix.cibuild }}
CIBW_ARCHS_MACOS: ${{ matrix.macos-arch }}
@@ -98,7 +98,7 @@ jobs:
mv ./wheelhouse/.ccache $HOST_CCACHE_DIR
ls -la $HOST_CCACHE_DIR
ccache -s
- - uses: actions/upload-artifact@v4
+ - uses: actions/upload-artifact@v5
with:
name: artifact-${{ matrix.os }}-${{ matrix.cibw_arch }}-${{ strategy.job-index }}
path: ./wheelhouse/*.whl
@@ -108,12 +108,12 @@ jobs:
name: Make SDist
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v5
+ - uses: actions/checkout@v6
- name: Build SDist
run: pipx run build --sdist
- - uses: actions/upload-artifact@v4
+ - uses: actions/upload-artifact@v5
with:
name: artifact-sdist
path: dist/*.tar.gz
@@ -122,7 +122,7 @@ jobs:
needs: [build, make_sdist]
runs-on: ubuntu-latest
steps:
- - uses: actions/download-artifact@v5
+ - uses: actions/download-artifact@v6
with:
merge-multiple: true
path: dist
@@ -139,7 +139,7 @@ jobs:
runs-on: ubuntu-latest
if: github.event_name == 'release' && github.event.action == 'published'
steps:
- - uses: actions/download-artifact@v5
+ - uses: actions/download-artifact@v6
with:
merge-multiple: true
path: dist
diff --git a/.github/workflows/cache-maintenance.yml b/.github/workflows/cache-maintenance.yml
index 33d9f5d62..f57190c28 100644
--- a/.github/workflows/cache-maintenance.yml
+++ b/.github/workflows/cache-maintenance.yml
@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: Cleanup
run: |
diff --git a/COPYRIGHT b/COPYRIGHT
index d01b4e3df..4516b586a 100644
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -7,18 +7,19 @@ Authors:
Copyright:
Copyright (C) 2006-2025 by Matthias Köfferlein.
- 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 or 3 of the License.
+ 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 3 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.
+ 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 package; if not, write to the Free Software Foundation,
- Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ along with this program. If not, see
+ .
On Debian GNU/Linux systems, the complete text of the GNU General
Public License can be found in `/usr/share/common-licenses/GPL'.
diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc
index c1d80a2bb..b2eb61acd 100644
--- a/src/edt/edt/edtPartialService.cc
+++ b/src/edt/edt/edtPartialService.cc
@@ -762,9 +762,9 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, co
{
if (! point_mode ()) {
- for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) {
+ for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) {
- if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (scan_box))) {
+ if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox (*l).touches (scan_box))) {
checkpoint ();
@@ -897,9 +897,9 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, co
} else {
- for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) {
+ for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) {
- if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (hit_box))) {
+ if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox (*l).touches (hit_box))) {
checkpoint ();
@@ -2315,8 +2315,8 @@ PartialService::mouse_release_event (const db::DPoint &p, unsigned int buttons,
if (ui ()->mouse_event_viewport ().contains (p)) {
lay::Editable::SelectionMode mode = lay::Editable::Replace;
- bool shift = ((m_buttons & lay::ShiftButton) != 0);
- bool ctrl = ((m_buttons & lay::ControlButton) != 0);
+ bool shift = ((buttons & lay::ShiftButton) != 0);
+ bool ctrl = ((buttons & lay::ControlButton) != 0);
if (shift && ctrl) {
mode = lay::Editable::Invert;
} else if (shift) {
diff --git a/src/lay/lay/gsiDeclLayMainWindow.cc b/src/lay/lay/gsiDeclLayMainWindow.cc
index 9bb58226d..4339aa8bf 100644
--- a/src/lay/lay/gsiDeclLayMainWindow.cc
+++ b/src/lay/lay/gsiDeclLayMainWindow.cc
@@ -328,6 +328,12 @@ set_menu_items_hidden (lay::MainWindow *mw, const std::map &h
mw->dispatcher ()->config_set (lay::cfg_menu_items_hidden, lay::pack_menu_items_hidden (hv));
}
+static void
+clear_message (lay::MainWindow *mw, int priority)
+{
+ mw->message (std::string (), 0, priority);
+}
+
Class decl_MainWindow (QT_EXTERNAL_BASE (QMainWindow) "lay", "MainWindow",
// Dispatcher interface and convenience functions
@@ -475,15 +481,36 @@ Class decl_MainWindow (QT_EXTERNAL_BASE (QMainWindow) "lay", "M
"\n"
"This method has been added in version 0.24."
) +
- gsi::method ("message", &lay::MainWindow::message, gsi::arg ("message"), gsi::arg ("time", -1, "infinite"),
+ gsi::method_ext ("clear_message", &clear_message, gsi::arg ("priority", -1, "all priorities"),
+ "@brief Clears the message\n"
+ "When calling this method with a priority, it will clear the message in the given priority slot, thus "
+ "unhiding the messages with lower priority. This is equivalent to calling \\message with an empty string.\n"
+ "\n"
+ "When calling without a priority, all messages in all priority slots will be cleared.\n"
+ "\n"
+ "This method has been added in version 0.30.6."
+ ) +
+ gsi::method ("message", &lay::MainWindow::message, gsi::arg ("message"), gsi::arg ("time", -1, "infinite"), gsi::arg ("priority", 0),
"@brief Displays a message in the status bar\n"
"\n"
"@param message The message to display\n"
"@param time The time how long to display the message in ms. A negative value means 'infinitely'.\n"
+ "@param priority The priority of the message. Higher-priority messages have precendence over lower-priority ones.\n"
"\n"
"This given message is shown in the status bar for the given time.\n"
+ "If a priority is given, higher-priority messages have precedence over lower-priority ones.\n"
+ "Placing an empty message clears a message with a given priority and unhides messages with lower "
+ "priority. Standard messages like selection descriptions have priority 0, which is the lowest priority.\n"
"\n"
- "This method has been added in version 0.18. The 'time' parameter was made optional in version 0.28.10."
+ "Messages generated during modal actions (e.g. 'Click on first point') by convention should have priority 10 "
+ "and should be tied to the active state of a plugin. This ensures there is only one such message.\n"
+ "Higher-priority messages must be cleared (set to empty string) explicitly to unhide lower-priority messages "
+ "when the indicated action is finished.\n"
+ "\n"
+ "\\clear_message is a convenience method that will clear messages.\n"
+ "\n"
+ "This method has been added in version 0.18. The 'time' parameter was made optional in version 0.28.10.\n"
+ "The 'priority' argument has been added in version 0.30.6."
) +
gsi::method ("resize", (void (lay::MainWindow::*)(int, int)) &lay::MainWindow::resize, gsi::arg ("width"), gsi::arg ("height"),
"@brief Resizes the window\n"
diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc
index b54499056..75a73dd16 100644
--- a/src/lay/lay/layMainWindow.cc
+++ b/src/lay/lay/layMainWindow.cc
@@ -186,6 +186,7 @@ MainWindow::MainWindow (QApplication *app, const char *name, bool undo_enabled)
m_synchronous (false),
m_busy (false),
mp_app (app),
+ m_message_timer_priority (-1),
m_manager (undo_enabled)
{
setAnimated (false);
@@ -823,10 +824,15 @@ static int fm_width (const QFontMetrics &fm, const QString &s)
void
MainWindow::format_message ()
{
+ std::string msg;
+ if (! m_messages.empty ()) {
+ msg = (--m_messages.end ())->second;
+ }
+
QFontMetrics fm (mp_msg_label->font ());
std::string full_message;
- for (const char *c = m_message.c_str (); *c; ++c) {
+ for (const char *c = msg.c_str (); *c; ++c) {
if (*c == '\\' && (c[1] == '(' || c[1] == ')')) {
++c;
} else {
@@ -847,7 +853,7 @@ MainWindow::format_message ()
short_message.clear ();
- for (const char *c = m_message.c_str (); *c; ++c) {
+ for (const char *c = msg.c_str (); *c; ++c) {
if (*c == '\\' && c[1] == '(') {
if (nsection++ < ndrop) {
in_drop = true;
@@ -875,24 +881,53 @@ MainWindow::format_message ()
}
void
-MainWindow::message (const std::string &s, int ms)
+MainWindow::message (const std::string &s, int ms, int priority)
{
- m_message = s;
+ if (s.empty () && priority < 0) {
+
+ m_messages.clear ();
+
+ } else if (s.empty ()) {
+
+ m_messages.erase (priority);
+
+ } else {
+
+ // simulate timeout of previous message timer
+ if (m_message_timer_priority >= 0) {
+ m_messages.erase (m_message_timer_priority);
+ }
+
+ m_messages[priority] = s;
+
+ if (ms >= 0) {
+ m_message_timer_priority = priority;
+ m_message_timer.start (ms);
+ } else {
+ m_message_timer_priority = -1;
+ }
+
+ }
+
format_message ();
- m_message_timer.start (ms);
}
void
-MainWindow::clear_message ()
+MainWindow::clear_messages ()
{
- m_message.clear ();
- m_message_timer.start (0);
+ m_message_timer.stop ();
+ m_message_timer_priority = -1;
+ m_messages.clear ();
+ format_message ();
}
void
MainWindow::message_timer ()
{
- m_message.clear ();
+ if (m_message_timer_priority >= 0) {
+ m_messages.erase (m_message_timer_priority);
+ }
+ m_message_timer_priority = -1;
format_message ();
}
@@ -2277,6 +2312,29 @@ MainWindow::cm_save_as ()
do_save (true);
}
+static db::SaveLayoutOptions
+get_save_options_from_cv (const lay::CellView &cv)
+{
+ db::SaveLayoutOptions options = cv->save_options ();
+ if (!cv->save_options_valid () && cv->technology ()) {
+ options = cv->technology ()->save_layout_options ();
+ options.set_format (cv->save_options ().format ());
+ }
+
+ // preconfigure options with current values
+
+ options.set_dbu (cv->layout ().dbu ());
+
+ if (cv->layout ().has_meta_info ("libname")) {
+ tl::Variant libname = cv->layout ().meta_info ("libname").value;
+ if (libname.is_a_string ()) {
+ options.set_libname (libname.to_stdstring ());
+ }
+ }
+
+ return options;
+}
+
void
MainWindow::do_save (bool as)
{
@@ -2309,22 +2367,7 @@ MainWindow::do_save (bool as)
// - if the layout's save options are valid we take the options from there, otherwise we take the options from the technology
// - on "save as" we let the user edit the options
- db::SaveLayoutOptions options = cv->save_options ();
- if (!cv->save_options_valid () && cv->technology ()) {
- options = cv->technology ()->save_layout_options ();
- options.set_format (cv->save_options ().format ());
- }
-
- // preconfigure options with current values
-
- options.set_dbu (cv->layout ().dbu ());
-
- if (cv->layout ().has_meta_info ("libname")) {
- tl::Variant libname = cv->layout ().meta_info ("libname").value;
- if (libname.is_a_string ()) {
- options.set_libname (libname.to_stdstring ());
- }
- }
+ db::SaveLayoutOptions options = get_save_options_from_cv (cv);
if (as || options.format ().empty ()) {
options.set_format_from_filename (fn);
@@ -2337,7 +2380,18 @@ MainWindow::do_save (bool as)
}
current_view ()->save_as ((unsigned int) cv_index, fn, om, options, true, m_keep_backups);
- add_mru (fn, current_view ()->cellview (cv_index)->tech_name ());
+ add_mru (fn, cv->tech_name ());
+
+ if (as) {
+
+ lay::LayoutViewNotification n ("reload", tl::to_string (tr ("The next 'save' operations will use the writer options you have picked, instead of the application-wide ones.")));
+ current_view ()->add_notification (n);
+
+ // freeze writer options in the 'save_as' case, so we can do another "save" and get the
+ // selected options again
+ cv->set_save_options (options, true);
+
+ }
}
@@ -2362,8 +2416,7 @@ MainWindow::cm_save_all ()
if (! fn.empty () || mp_layout_fdia->get_save (fn, tl::to_string (tr ("Save Layout '%1'").arg (tl::to_qstring (cv->name ()))))) {
- db::SaveLayoutOptions options (cv->save_options ());
- options.set_dbu (cv->layout ().dbu ());
+ db::SaveLayoutOptions options = get_save_options_from_cv (cv);
if (options.format ().empty ()) {
options.set_format_from_filename (fn);
@@ -2371,14 +2424,6 @@ MainWindow::cm_save_all ()
tl::OutputStream::OutputStreamMode om = tl::OutputStream::OM_Auto;
- // initialize the specific options from the configuration if required
- for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) {
- const StreamWriterPluginDeclaration *decl = dynamic_cast (&*cls);
- if (decl) {
- options.set_options (decl->create_specific_options ());
- }
- }
-
view (view_index)->save_as (cv_index, fn, om, options, true, m_keep_backups);
add_mru (fn, cv->tech_name ());
@@ -2457,7 +2502,7 @@ MainWindow::select_view (int index)
update_editor_options_dock ();
clear_current_pos ();
edits_enabled_changed ();
- clear_message ();
+ clear_messages ();
menu_needs_update ();
m_disable_tab_selected = dis;
@@ -2984,7 +3029,7 @@ MainWindow::close_view (int index)
clear_current_pos ();
edits_enabled_changed ();
menu_needs_update ();
- clear_message ();
+ clear_messages ();
update_dock_widget_state ();
@@ -3469,7 +3514,7 @@ MainWindow::add_view (lay::LayoutViewWidget *view)
connect (view, SIGNAL (dirty_changed (lay::LayoutView *)), this, SLOT (view_title_changed (lay::LayoutView *)));
connect (view, SIGNAL (edits_enabled_changed ()), this, SLOT (edits_enabled_changed ()));
connect (view, SIGNAL (menu_needs_update ()), this, SLOT (menu_needs_update ()));
- connect (view, SIGNAL (show_message (const std::string &, int)), this, SLOT (message (const std::string &, int)));
+ connect (view, SIGNAL (show_message (const std::string &, int, int)), this, SLOT (message (const std::string &, int, int)));
connect (view, SIGNAL (current_pos_changed (double, double, bool)), this, SLOT (current_pos (double, double, bool)));
connect (view, SIGNAL (clear_current_pos ()), this, SLOT (clear_current_pos ()));
connect (view, SIGNAL (mode_change (int)), this, SLOT (select_mode (int)));
diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h
index 737e24efc..0aab28d1a 100644
--- a/src/lay/lay/layMainWindow.h
+++ b/src/lay/lay/layMainWindow.h
@@ -649,12 +649,12 @@ public slots:
/**
* @brief Displays a status message next to the coordinates
*/
- void message (const std::string &s, int ms);
+ void message (const std::string &s, int ms, int);
/**
* @brief Clears the current message
*/
- void clear_message ();
+ void clear_messages ();
/**
* @brief Selects the given mode
@@ -789,7 +789,8 @@ private:
QApplication *mp_app;
lay::HelpDialog *mp_assistant;
std::string m_current_session;
- std::string m_message;
+ int m_message_timer_priority;
+ std::map m_messages;
std::unique_ptr mp_printer;
std::vector m_changed_files;
std::string m_title;
diff --git a/src/laybasic/laybasic/gsiDeclLayAdded.cc b/src/laybasic/laybasic/gsiDeclLayAdded.cc
index 32cb1c54d..13f0b4f88 100644
--- a/src/laybasic/laybasic/gsiDeclLayAdded.cc
+++ b/src/laybasic/laybasic/gsiDeclLayAdded.cc
@@ -84,11 +84,15 @@ class ButtonStateNamespace { };
static int const_ShiftButton() { return (int) lay::ShiftButton; }
static int const_ControlButton() { return (int) lay::ControlButton; }
static int const_AltButton() { return (int) lay::AltButton; }
+static int const_ModifierMask() { return (int) lay::ModifierMask; }
static int const_LeftButton() { return (int) lay::LeftButton; }
static int const_MidButton() { return (int) lay::MidButton; }
static int const_RightButton() { return (int) lay::RightButton; }
+static int const_MouseButtonMask() { return (int) lay::MouseButtonMask; }
Class decl_ButtonState ("lay", "ButtonState",
+ method ("ModifierMask", &const_ModifierMask, "@brief A bit mask that selects all keyboard modifiers in the button state mask\nThis constant has been introduced in version 0.30.6.") +
+ method ("MouseButtonMask", &const_MouseButtonMask, "@brief A bit mask that selects all mouse buttons in the button state mask\nThis constant has been introduced in version 0.30.6.") +
method ("ShiftKey", &const_ShiftButton, "@brief Indicates that the Shift key is pressed\nThis constant is combined with other constants within \\ButtonState") +
method ("ControlKey", &const_ControlButton, "@brief Indicates that the Control key is pressed\nThis constant is combined with other constants within \\ButtonState") +
method ("AltKey", &const_AltButton, "@brief Indicates that the Alt key is pressed\nThis constant is combined with other constants within \\ButtonState") +
diff --git a/src/laybasic/laybasic/gsiDeclLayPlugin.cc b/src/laybasic/laybasic/gsiDeclLayPlugin.cc
index 28d5245b3..ef3d696dc 100644
--- a/src/laybasic/laybasic/gsiDeclLayPlugin.cc
+++ b/src/laybasic/laybasic/gsiDeclLayPlugin.cc
@@ -125,58 +125,76 @@ static void drag_cancel_impl (lay::EditorServiceBase *p)
Class decl_PluginBase ("lay", "PluginBase",
gsi::method_ext ("tracking_position", &tracking_position_impl,
- "@brief Gets the tracking position (base class implementation)"
+ "@brief Gets the tracking position (base class implementation)\n"
+ "See \\Plugin#tracking_position for details."
) +
gsi::method_ext ("has_tracking_position", &has_tracking_position_impl,
- "@brief Gets a value indicating whether the plugin provides a tracking position (base class implementation)"
+ "@brief Gets a value indicating whether the plugin provides a tracking position (base class implementation)\n"
+ "See \\Plugin#has_tracking_position for details."
) +
gsi::method_ext ("menu_activated", &menu_activated_impl, gsi::arg ("symbol"),
- "@brief Gets called when a custom menu item is selected (base class implementation)"
+ "@brief Gets called when a custom menu item is selected (base class implementation)\n"
+ "See \\Plugin#menu_activated for details."
) +
gsi::method_ext ("configure", &configure_impl, gsi::arg ("name"), gsi::arg ("value"),
- "@brief Sends configuration requests to the plugin (base class implementation)"
+ "@brief Sends configuration requests to the plugin (base class implementation)\n"
+ "See \\Plugin#configure for details."
) +
gsi::method_ext ("config_finalize", &config_finalize_impl,
- "@brief Sends the post-configuration request to the plugin (base class implementation)"
+ "@brief Sends the post-configuration request to the plugin (base class implementation)\n"
+ "See \\Plugin#config_finalize for details."
) +
gsi::method_ext ("key_event", &key_event_impl, gsi::arg ("key"), gsi::arg ("buttons"),
- "@brief Handles the key pressed event (base class implementation)"
+ "@brief Handles the key pressed event (base class implementation)\n"
+ "See \\Plugin#key_event for details."
) +
gsi::method_ext ("mouse_button_pressed_event", &mouse_press_event_impl, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"),
- "@brief Handles the mouse button pressed event (base class implementation)"
+ "@brief Handles the mouse button pressed event (base class implementation)\n"
+ "See \\Plugin#mouse_button_pressed_event for details."
) +
gsi::method_ext ("mouse_click_event", &mouse_click_event_impl, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"),
- "@brief Handles the mouse button click event after the button has been released (base class implementation)"
+ "@brief Handles the mouse button click event after the button has been released (base class implementation)\n"
+ "See \\Plugin#mouse_click_event for details."
) +
gsi::method_ext ("mouse_double_click_event", &mouse_double_click_event_impl, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"),
- "@brief Handles the mouse button double-click event (base class implementation)"
+ "@brief Handles the mouse button double-click event (base class implementation)\n"
+ "See \\Plugin#mouse_double_click_event for details."
) +
gsi::method_ext ("leave_event", &leave_event_impl, gsi::arg ("prio"),
- "@brief Handles the leave event (base class implementation)"
+ "@brief Handles the leave event (base class implementation)\n"
+ "See \\Plugin#leave_event for details."
) +
gsi::method_ext ("enter_event", &enter_event_impl, gsi::arg ("prio"),
- "@brief Handles the enter event (base class implementation)"
+ "@brief Handles the enter event (base class implementation)\n"
+ "See \\Plugin#enter_event for details."
) +
gsi::method_ext ("mouse_moved_event", &mouse_move_event_impl, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"),
- "@brief Handles the mouse move event (base class implementation)"
+ "@brief Handles the mouse move event (base class implementation)\n"
+ "See \\Plugin#mouse_moved_event for details."
) +
gsi::method_ext ("mouse_button_released_event", &mouse_release_event_impl, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"),
- "@brief Handles the mouse button release event (base class implementation)"
+ "@brief Handles the mouse button release event (base class implementation)\n"
+ "See \\Plugin#mouse_button_released_event for details."
) +
gsi::method_ext ("wheel_event", &wheel_event_impl, gsi::arg ("delta"), gsi::arg ("horizontal"), gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"),
- "@brief Handles the mouse wheel event (base class implementation)"
+ "@brief Handles the mouse wheel event (base class implementation)\n"
+ "See \\Plugin#wheel_event for details."
) +
gsi::method_ext ("activated", &activated_impl,
- "@brief Gets called when the plugin is activated (base class implementation)"
+ "@brief Gets called when the plugin is activated (base class implementation)\n"
+ "See \\Plugin#activated for details."
) +
gsi::method_ext ("deactivated", &deactivated_impl,
- "@brief Gets called when the plugin is deactivated and another plugin is activated (base class implementation)"
+ "@brief Gets called when the plugin is deactivated and another plugin is activated (base class implementation)\n"
+ "See \\Plugin#deactivated for details."
) +
gsi::method_ext ("drag_cancel", &drag_cancel_impl,
- "@brief This method is called when some mouse dragging operation should be cancelled (base class implementation)"
+ "@brief This method is called when some mouse dragging operation should be cancelled (base class implementation)\n"
+ "See \\Plugin#drag_cancel for details."
) +
gsi::method_ext ("update", &update_impl,
- "@brief Gets called when the view has changed (base class implementation)"
+ "@brief Gets called when the view has changed (base class implementation)\n"
+ "See \\Plugin#update for details."
),
"@brief The plugin base class\n"
"\n"
@@ -735,36 +753,44 @@ Class decl_Plugin (decl_PluginBase, "lay", "Plugin",
) +
callback ("mouse_click_event", &gsi::PluginImpl::mouse_click_event_noref, &gsi::PluginImpl::f_mouse_click_event, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"),
"@brief Handles the mouse button click event (after the button has been released)\n"
- "The behaviour of this callback is the same than for \\mouse_press_event, except that it is called when the mouse button has been released without moving it.\n"
+ "The behaviour of this callback is the same than for \\mouse_button_pressed_event, except that it is called when the mouse button has been released without moving it.\n"
+ "A mouse click is not defined by duration, but by releasing a button without moving the mouse after the button was pressed. "
+ "As a consequence, a \\mouse_button_pressed_event is always issued at the beginning, but it is not followed by a \\mouse_button_released_event.\n"
+ "Instead, the 'mouse_click_event' is issued.\n"
+ "\n"
+ "Starting with version 0.30.6, the button mask reflects the keyboard modifiers at the moment the mouse was released. Before, the keyboard modifiers were "
+ "captured at the moment when the mouse was pressed."
) +
callback ("mouse_double_click_event", &gsi::PluginImpl::mouse_double_click_event_noref, &gsi::PluginImpl::f_mouse_double_click_event, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"),
"@brief Handles the mouse button double-click event\n"
- "The behaviour of this callback is the same than for \\mouse_press_event, except that it is called when the mouse button has been double-clicked.\n"
+ "The behaviour of this callback is the same than for \\mouse_button_pressed_event, except that it is called when the mouse button has been double-clicked.\n"
) +
callback ("leave_event", &gsi::PluginImpl::leave_event, &gsi::PluginImpl::f_leave_event, gsi::arg ("prio"),
"@brief Handles the leave event (mouse leaves canvas area of view)\n"
- "The behaviour of this callback is the same than for \\mouse_press_event, except that it is called when the mouse leaves the canvas area.\n"
+ "The behaviour of this callback is the same than for \\mouse_button_pressed_event, except that it is called when the mouse leaves the canvas area.\n"
"This method does not have a position nor button flags.\n"
) +
callback ("enter_event", &gsi::PluginImpl::enter_event, &gsi::PluginImpl::f_enter_event, gsi::arg ("prio"),
"@brief Handles the enter event (mouse enters canvas area of view)\n"
- "The behaviour of this callback is the same than for \\mouse_press_event, except that it is called when the mouse enters the canvas area.\n"
+ "The behaviour of this callback is the same than for \\mouse_button_pressed_event, except that it is called when the mouse enters the canvas area.\n"
"This method does not have a position nor button flags.\n"
) +
callback ("mouse_moved_event", &gsi::PluginImpl::mouse_move_event_noref, &gsi::PluginImpl::f_mouse_move_event, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"),
"@brief Handles the mouse move event\n"
- "The behaviour of this callback is the same than for \\mouse_press_event, except that it is called when the mouse is moved in the canvas area.\n"
+ "The behaviour of this callback is the same than for \\mouse_button_pressed_event, except that it is called when the mouse is moved in the canvas area.\n"
"\n"
"The mouse move event is important for a number of background jobs, such as coordinate display in the status bar.\n"
"Hence, you should not consume the event - i.e. you should return 'false' from this method.\n"
) +
callback ("mouse_button_released_event", &gsi::PluginImpl::mouse_release_event_noref, &gsi::PluginImpl::f_mouse_release_event, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"),
"@brief Handles the mouse button release event\n"
- "The behaviour of this callback is the same than for \\mouse_press_event, except that it is called when the mouse button is released.\n"
+ "The behaviour of this callback is the same than for \\mouse_button_pressed_event, except that it is called when the mouse button is released.\n"
+ "Starting with version 0.30.6, the button mask reflects the keyboard modifiers at the moment the mouse was released. Before, the keyboard modifiers were "
+ "captured at the moment when the mouse was pressed."
) +
callback ("wheel_event", &gsi::PluginImpl::wheel_event_noref, &gsi::PluginImpl::f_wheel_event, gsi::arg ("delta"), gsi::arg ("horizontal"), gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"),
"@brief Handles the mouse wheel event\n"
- "The behaviour of this callback is the same than for \\mouse_press_event, except that it is called when the mouse wheel is rotated.\n"
+ "The behaviour of this callback is the same than for \\mouse_button_pressed_event, except that it is called when the mouse wheel is rotated.\n"
"Additional parameters for this event are 'delta' (the rotation angle in units of 1/8th degree) and 'horizontal' which is true when the horizontal wheel was rotated and "
"false if the vertical wheel was rotated.\n"
) +
diff --git a/src/laybasic/laybasic/layCellView.cc b/src/laybasic/laybasic/layCellView.cc
index 84cbde12c..df8fb73a3 100644
--- a/src/laybasic/laybasic/layCellView.cc
+++ b/src/laybasic/laybasic/layCellView.cc
@@ -300,39 +300,11 @@ LayoutHandle::set_save_options (const db::SaveLayoutOptions &options, bool valid
m_save_options_valid = valid;
}
-void
-LayoutHandle::update_save_options (db::SaveLayoutOptions &options)
-{
-#if defined(HAVE_QT)
- for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) {
-
- const lay::StreamWriterPluginDeclaration *decl = dynamic_cast (&*cls);
- if (! decl || decl->options_alias ()) {
- continue;
- }
-
- std::unique_ptr specific_options;
- if (options.get_options (decl->format_name ())) {
- specific_options.reset (options.get_options (decl->format_name ())->clone ());
- } else {
- specific_options.reset (decl->create_specific_options ());
- }
-
- if (specific_options.get ()) {
- options.set_options (specific_options.release ());
- }
-
- }
-#endif
-}
-
void
LayoutHandle::save_as (const std::string &fn, tl::OutputStream::OutputStreamMode om, const db::SaveLayoutOptions &options, bool update, int keep_backups)
{
if (update) {
- m_save_options = options;
- m_save_options_valid = true;
// We must not load with the original options after we have saved the file - hence we reset the
// reader options.
m_load_options = db::LoadLayoutOptions ();
diff --git a/src/laybasic/laybasic/layCellView.h b/src/laybasic/laybasic/layCellView.h
index 661d6c8c1..0b5adadd1 100644
--- a/src/laybasic/laybasic/layCellView.h
+++ b/src/laybasic/laybasic/layCellView.h
@@ -242,15 +242,6 @@ public:
return m_save_options;
}
- /**
- * @brief Updates the given save options with attributes from this cell view
- *
- * Some formats will initialize attributes from the cell view and the layout's
- * metadata (example: libname of GDS2). This method will update the options
- * if the layout provides attributes for initializing the latter.
- */
- void update_save_options (db::SaveLayoutOptions &options);
-
/**
* @brief Gets a flag indicating whether the save options are valid
*
diff --git a/src/laybasic/laybasic/layFinder.cc b/src/laybasic/laybasic/layFinder.cc
index 4056d8050..2a3acffae 100644
--- a/src/laybasic/laybasic/layFinder.cc
+++ b/src/laybasic/laybasic/layFinder.cc
@@ -87,7 +87,7 @@ Finder::closer (double d)
}
void
-Finder::start (lay::LayoutViewBase *view, unsigned int cv_index, const std::vector &trans, const db::DBox ®ion, const db::DBox &scan_region, int min_level, int max_level, const std::vector &layers)
+Finder::start (lay::LayoutViewBase *view, unsigned int cv_index, const std::vector &trans, const db::DBox ®ion, const db::DBox &scan_region, int min_level, int max_level, const std::vector &layers)
{
const lay::CellView &cv = view->cellview (cv_index);
@@ -100,8 +100,8 @@ Finder::start (lay::LayoutViewBase *view, unsigned int cv_index, const std::vect
if (layers.size () == 1) {
- m_box_convert = db::box_convert (*mp_layout, (unsigned int) layers [0]);
- m_cell_box_convert = db::box_convert ((unsigned int) layers [0]);
+ m_box_convert = db::box_convert (*mp_layout, layers [0]);
+ m_cell_box_convert = db::box_convert (layers [0]);
} else {
@@ -202,7 +202,7 @@ Finder::do_find (const db::Cell &cell, int level, const db::DCplxTrans &vp, cons
if (level <= m_max_level /*take level of cell itself*/
&& cell.is_proxy ()
&& m_layers.size () == 1
- && (unsigned int) m_layers [0] == mp_layout->guiding_shape_layer ()) {
+ && m_layers [0] == mp_layout->guiding_shape_layer ()) {
// when looking at the guiding shape layer, we can visit this cell as well allowing to find the guiding shapes
@@ -339,7 +339,7 @@ ShapeFinder::find (LayoutViewBase *view, const db::DBox ®ion_mu)
std::sort (lprops.begin (), lprops.end (), LPContextCompareOp ());
- std::vector layers;
+ std::vector layers;
for (std::vector::const_iterator llp = lprops.begin (); llp != lprops.end (); ) {
layers.clear ();
@@ -347,7 +347,10 @@ ShapeFinder::find (LayoutViewBase *view, const db::DBox ®ion_mu)
lay::LayerPropertiesConstIterator lp0 = *llp;
LPContextEqualOp eq;
do {
- layers.push_back ((*llp)->layer_index ());
+ int li = (*llp)->layer_index ();
+ if (li >= 0) {
+ layers.push_back ((unsigned int) li);
+ }
++llp;
} while (llp != lprops.end () && eq(lp0, *llp));
@@ -398,16 +401,19 @@ ShapeFinder::find (lay::LayoutViewBase *view, const lay::LayerProperties &lprops
lay::TextInfo text_info (view);
mp_text_info = (m_flags & db::ShapeIterator::Texts) != 0 ? &text_info : 0;
- std::vector layers;
- layers.push_back (lprops.layer_index ());
+ std::vector layers;
+ int li = lprops.layer_index ();
+ if (li >= 0) {
+ layers.push_back ((unsigned int) li);
+ }
bool result = find_internal (view, lprops.cellview_index (), &lprops.prop_sel (), lprops.inverse_prop_sel (), lprops.hier_levels (), lprops.trans (), layers, region_mu);
mp_progress = 0;
return result;
}
-bool
-ShapeFinder::find_internal (lay::LayoutViewBase *view, unsigned int cv_index, const std::set *prop_sel, bool inv_prop_sel, const lay::HierarchyLevelSelection &hier_sel, const std::vector &trans_mu, const std::vector &layers, const db::DBox ®ion_mu)
+bool
+ShapeFinder::find_internal (lay::LayoutViewBase *view, unsigned int cv_index, const std::set *prop_sel, bool inv_prop_sel, const lay::HierarchyLevelSelection &hier_sel, const std::vector &trans_mu, const std::vector &layers, const db::DBox ®ion_mu)
{
m_cv_index = cv_index;
@@ -511,13 +517,13 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db:
if (! point_mode ()) {
- for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) {
+ for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) {
- if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (scan_box))) {
+ if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox (*l).touches (scan_box))) {
checkpoint ();
- const db::Shapes &shapes = cell.shapes ((unsigned int) *l);
+ const db::Shapes &shapes = cell.shapes (*l);
db::ShapeIterator shape = shapes.begin_touching (scan_box, m_flags, mp_prop_sel, m_inv_prop_sel);
while (! shape.at_end ()) {
@@ -563,9 +569,9 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db:
} else {
- for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) {
+ for (std::vector::const_iterator l = layers ().begin (); l != layers ().end (); ++l) {
- if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox ((unsigned int) *l).touches (scan_box))) {
+ if (layers ().size () == 1 || (layers ().size () > 1 && cell.bbox (*l).touches (scan_box))) {
checkpoint ();
@@ -793,7 +799,7 @@ InstFinder::find_internal (LayoutViewBase *view, unsigned int cv_index, const db
try {
std::vector tv;
tv.push_back (trans_mu);
- start (view, cv_index, tv, region_mu, region_mu, view->get_min_hier_levels (), view->get_max_hier_levels (), std::vector ());
+ start (view, cv_index, tv, region_mu, region_mu, view->get_min_hier_levels (), view->get_max_hier_levels (), std::vector ());
} catch (StopException) {
// ..
}
@@ -805,8 +811,12 @@ InstFinder::find_internal (LayoutViewBase *view, unsigned int cv_index, const db
void
InstFinder::checkpoint ()
{
- if (--m_tries < 0) {
- throw StopException ();
+ if (! point_mode ()) {
+ ++*mp_progress;
+ } else {
+ if (--m_tries < 0) {
+ throw StopException ();
+ }
}
}
@@ -829,15 +839,13 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d
if (! point_mode ()) {
- ++*mp_progress;
-
// look for instances to check here ..
for (db::Cell::touching_iterator inst = cell.begin_touching (search_box); ! inst.at_end (); ++inst) {
const db::CellInstArray &cell_inst = inst->cell_inst ();
const db::Cell &inst_cell = layout ().cell (cell_inst.object ().cell_index ());
- ++*mp_progress;
+ checkpoint ();
if (! consider_cell (inst_cell)) {
continue;
@@ -851,7 +859,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d
db::box_convert bc (layout ());
for (db::CellInstArray::iterator p = cell_inst.begin_touching (search_box, bc); ! p.at_end (); ++p) {
- ++*mp_progress;
+ checkpoint ();
db::Box ibox;
if (! m_visible_layers || level == mp_view->get_max_hier_levels () - 1 || inst_cell.is_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) {
diff --git a/src/laybasic/laybasic/layFinder.h b/src/laybasic/laybasic/layFinder.h
index eca725dc7..5ebe9152d 100644
--- a/src/laybasic/laybasic/layFinder.h
+++ b/src/laybasic/laybasic/layFinder.h
@@ -132,7 +132,7 @@ public:
}
protected:
- const std::vector &layers () const
+ const std::vector &layers () const
{
return m_layers;
}
@@ -183,7 +183,7 @@ protected:
* @param max_level The maximum hierarchy level to check
* @param layers A set of layers to check
*/
- void start (LayoutViewBase *view, unsigned int cv_index, const std::vector &trans, const db::DBox ®ion, const db::DBox &scan_region, int min_level, int max_level, const std::vector &layers = std::vector ());
+ void start (LayoutViewBase *view, unsigned int cv_index, const std::vector &trans, const db::DBox ®ion, const db::DBox &scan_region, int min_level, int max_level, const std::vector &layers = std::vector ());
/**
* @brief Provide a basic edge test facility
@@ -232,7 +232,7 @@ private:
unsigned int m_cv_index;
db::Box m_region;
db::Box m_scan_region;
- std::vector m_layers;
+ std::vector m_layers;
double m_distance;
bool m_point_mode;
bool m_catch_all;
@@ -267,8 +267,8 @@ public:
*/
ShapeFinder (bool point_mode, bool top_level_sel, db::ShapeIterator::flags_type flags, const std::set *excludes = 0, bool capture_all_shapes = false);
- bool find (LayoutViewBase *view, const lay::LayerProperties &lprops, const db::DBox ®ion_mu);
- bool find (LayoutViewBase *view, const db::DBox ®ion_mu);
+ bool find (lay::LayoutViewBase *view, const lay::LayerProperties &lprops, const db::DBox ®ion_mu);
+ bool find (lay::LayoutViewBase *view, const db::DBox ®ion_mu);
iterator begin () const
{
@@ -327,7 +327,7 @@ private:
bool inv_prop_sel,
const lay::HierarchyLevelSelection &hier_sel,
const std::vector &trans_mu,
- const std::vector &layers,
+ const std::vector &layers,
const db::DBox ®ion_mu);
const std::set *mp_excludes;
diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc
index c0a5b7879..67c07e246 100644
--- a/src/laybasic/laybasic/layLayoutViewBase.cc
+++ b/src/laybasic/laybasic/layLayoutViewBase.cc
@@ -342,6 +342,7 @@ LayoutViewBase::init (db::Manager *mgr)
m_box_text_transform = true;
m_box_font = 0;
m_min_size_for_label = 16;
+ m_empty_cell_dimension = 4.0;
m_cell_box_visible = true;
m_ghost_cells_visible = true;
m_text_visible = true;
@@ -693,7 +694,7 @@ LayoutViewBase::set_synchronous (bool s)
}
void
-LayoutViewBase::message (const std::string & /*s*/, int /*timeout*/)
+LayoutViewBase::message (const std::string & /*s*/, int /*timeout*/, int /*priority*/)
{
// .. nothing yet ..
}
@@ -944,6 +945,13 @@ LayoutViewBase::configure (const std::string &name, const std::string &value)
min_inst_label_size (n);
return true;
+ } else if (name == cfg_empty_cell_dimension) {
+
+ double n;
+ tl::from_string (value, n);
+ empty_cell_dimension (n);
+ return true;
+
} else if (name == cfg_cell_box_text_font) {
int n;
@@ -4156,7 +4164,7 @@ void
LayoutViewBase::cancel_edits ()
{
// clear any messages
- message ();
+ message (std::string (), 0, -1);
// the move service takes a special role here as it manages the
// transaction for the collective move operation.
@@ -5403,7 +5411,16 @@ LayoutViewBase::min_inst_label_size (int px)
}
}
-void
+void
+LayoutViewBase::empty_cell_dimension (double um)
+{
+ if (m_empty_cell_dimension != um) {
+ m_empty_cell_dimension = um;
+ redraw ();
+ }
+}
+
+void
LayoutViewBase::text_visible (bool vis)
{
if (m_text_visible != vis) {
diff --git a/src/laybasic/laybasic/layLayoutViewBase.h b/src/laybasic/laybasic/layLayoutViewBase.h
index b0ad6d28e..bc40922b1 100644
--- a/src/laybasic/laybasic/layLayoutViewBase.h
+++ b/src/laybasic/laybasic/layLayoutViewBase.h
@@ -149,6 +149,70 @@ struct LAYBASIC_PUBLIC LayerDisplayProperties
std::string name;
};
+/**
+ * @brief Descriptor for a notification inside the layout view
+ *
+ * Notifications are popups added at the top of the view to indicate need for reloading for example.
+ * Notifications have a name, a title, optional actions (id, title) and a parameter (e.g. file path to reload).
+ * Actions are mapped to QPushButtons.
+ */
+class LAYBASIC_PUBLIC LayoutViewNotification
+{
+public:
+ LayoutViewNotification (const std::string &name, const std::string &title, const tl::Variant ¶meter = tl::Variant ())
+ : m_name (name), m_title (title), m_parameter (parameter)
+ {
+ // .. nothing yet ..
+ }
+
+ void add_action (const std::string &name, const std::string &title)
+ {
+ m_actions.push_back (std::make_pair (name, title));
+ }
+
+ const std::vector > &actions () const
+ {
+ return m_actions;
+ }
+
+ const std::string &name () const
+ {
+ return m_name;
+ }
+
+ const std::string &title () const
+ {
+ return m_title;
+ }
+
+ const tl::Variant ¶meter () const
+ {
+ return m_parameter;
+ }
+
+ bool operator<(const LayoutViewNotification &other) const
+ {
+ if (m_name != other.name ()) {
+ return m_name < other.name ();
+ }
+ return m_parameter < other.parameter ();
+ }
+
+ bool operator==(const LayoutViewNotification &other) const
+ {
+ if (m_name != other.name ()) {
+ return false;
+ }
+ return m_parameter == other.parameter ();
+ }
+
+private:
+ std::string m_name;
+ std::string m_title;
+ tl::Variant m_parameter;
+ std::vector > m_actions;
+};
+
/**
* @brief The layout view object
*
@@ -279,6 +343,14 @@ public:
*/
virtual void cut ();
+ /**
+ * @brief Adds a notification
+ */
+ virtual void add_notification (const LayoutViewNotification & /*notification*/)
+ {
+ // the base implementation does nothing
+ }
+
/**
* @brief Gets the explicit title string of the view
*
@@ -293,7 +365,7 @@ public:
/**
* @brief Display a status message
*/
- virtual void message (const std::string &s = "", int timeout = 10);
+ virtual void message (const std::string &s = "", int timeout = 10, int priority = 0);
/**
* @brief Sets the keyboard focus to the view
@@ -1149,7 +1221,20 @@ public:
return m_min_size_for_label;
}
- /**
+ /**
+ * @brief Empty cell dimension for the purpose of label generation setter
+ */
+ void empty_cell_dimension (double um);
+
+ /**
+ * @brief Empty cell dimension for the purpose of label generation getter
+ */
+ int empty_cell_dimension () const
+ {
+ return m_empty_cell_dimension;
+ }
+
+ /**
* @brief Visibility of text objects
*/
void text_visible (bool vis);
@@ -2941,6 +3026,7 @@ private:
bool m_box_text_transform;
unsigned int m_box_font;
int m_min_size_for_label;
+ double m_empty_cell_dimension;
bool m_cell_box_visible;
bool m_ghost_cells_visible;
diff --git a/src/laybasic/laybasic/layLayoutViewConfig.cc b/src/laybasic/laybasic/layLayoutViewConfig.cc
index 7f7908bd8..ebf2b899e 100644
--- a/src/laybasic/laybasic/layLayoutViewConfig.cc
+++ b/src/laybasic/laybasic/layLayoutViewConfig.cc
@@ -55,6 +55,7 @@ public:
options.push_back (std::pair (cfg_current_lib_view, ""));
options.push_back (std::pair (cfg_hide_empty_layers, "false"));
options.push_back (std::pair (cfg_min_inst_label_size, "16"));
+ options.push_back (std::pair (cfg_empty_cell_dimension, "4"));
options.push_back (std::pair (cfg_cell_box_text_font, "0"));
options.push_back (std::pair (cfg_cell_box_text_transform, "true"));
options.push_back (std::pair (cfg_cell_box_color, "auto"));
diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.cc b/src/laybasic/laybasic/layRedrawThreadWorker.cc
index d8a332b50..0be5fc16b 100644
--- a/src/laybasic/laybasic/layRedrawThreadWorker.cc
+++ b/src/laybasic/laybasic/layRedrawThreadWorker.cc
@@ -40,6 +40,90 @@ static inline db::Box safe_transformed_box (const db::Box &box, const db::ICplxT
return db::Box (db);
}
+static bool
+has_zero_bit (const lay::Bitmap *bitmap, unsigned int ixmin, unsigned int iymin, unsigned int ixmax, unsigned int iymax)
+{
+ uint32_t imin = ixmin / 32;
+ uint32_t imax = ixmax / 32;
+
+ if (imin == imax) {
+
+ uint32_t m = ((unsigned int) 0xffffffff << (ixmin % 32)) & ((unsigned int) 0xffffffff >> (31 - (ixmax % 32)));
+
+ for (unsigned int y = iymin; y <= iymax; ++y) {
+
+ if (bitmap->is_scanline_empty (y)) {
+ return true;
+ }
+
+ if ((bitmap->scanline (y) [imin] & m) != m) {
+ return true;
+ }
+
+ }
+
+ } else {
+
+ uint32_t m1 = ((unsigned int) 0xffffffff << (ixmin % 32));
+ uint32_t m2 = ((unsigned int) 0xffffffff >> (31 - (ixmax % 32)));
+
+ for (unsigned int y = iymin; y <= iymax; ++y) {
+
+ if (bitmap->is_scanline_empty (y)) {
+ return true;
+ }
+
+ if ((bitmap->scanline (y) [imin] & m1) != m1) {
+ return true;
+ }
+ for (unsigned int i = imin + 1; i < imax; ++i) {
+ if (bitmap->scanline (y) [i] != 0xffffffff) {
+ return true;
+ }
+ }
+ if ((bitmap->scanline (y) [imax] & m2) != m2) {
+ return true;
+ }
+
+ }
+
+ }
+
+ return false;
+}
+
+static bool
+skip_quad (const db::Box &qb, const lay::Bitmap *vertex_bitmap, const db::CplxTrans &trans)
+{
+ double threshold = 32 / trans.mag (); // only check cells below 32x32 pixels
+ if (qb.empty () || qb.width () > threshold || qb.height () > threshold || !vertex_bitmap) {
+ return false;
+ }
+
+ db::DBox qb_trans = (trans * qb) & db::DBox (0, 0, vertex_bitmap->width () - 1.0 - 1e-6, vertex_bitmap->height () - 1.0 - 1e-6);
+ if (qb_trans.empty ()) {
+ return true;
+ }
+
+ int ixmin = (unsigned int)(qb_trans.left () + 0.5);
+ int ixmax = (unsigned int)(qb_trans.right () + 0.5);
+ int iymin = (unsigned int)(qb_trans.bottom () + 0.5);
+ int iymax = (unsigned int)(qb_trans.top () + 0.5);
+ if (! has_zero_bit (vertex_bitmap, ixmin, iymin, ixmax, iymax)) {
+ return true; // skip
+ } else {
+ return false;
+ }
+}
+
+inline void
+copy_bitmap (const lay::Bitmap *from, lay::Bitmap *to, int dx, int dy)
+{
+ if (to) {
+ to->merge (from, dx, dy);
+ }
+}
+
// -------------------------------------------------------------
// RedrawThreadWorker implementation
@@ -61,6 +145,7 @@ RedrawThreadWorker::RedrawThreadWorker (RedrawThread *redraw_thread)
m_box_text_transform = false;
m_box_font = 0;
m_min_size_for_label = 1;
+ m_empty_cell_dimension = 1.0;
m_text_font = 0;
m_text_visible = false;
m_text_lazy_rendering = false;
@@ -600,6 +685,7 @@ RedrawThreadWorker::setup (LayoutViewBase *view, RedrawThreadCanvas *canvas, con
m_from_level_default = view->get_hier_levels ().first;
m_to_level_default = view->get_hier_levels ().second;
m_min_size_for_label = view->min_inst_label_size ();
+ m_empty_cell_dimension = view->empty_cell_dimension ();
m_box_text_transform = view->cell_box_text_transform ();
m_box_font = view->cell_box_text_font ();
m_text_font = view->text_font ();
@@ -680,7 +766,7 @@ RedrawThreadWorker::test_snapshot (const UpdateSnapshotCallback *update_snapshot
}
void
-RedrawThreadWorker::draw_cell (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, bool empty_cell, const std::string &txt)
+RedrawThreadWorker::draw_cell (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, const db::Box &box_for_label, bool empty_cell, const std::string &txt, lay::Bitmap *opt_bitmap)
{
lay::Renderer &r = *mp_renderer;
@@ -699,13 +785,17 @@ RedrawThreadWorker::draw_cell (bool drawing_context, int level, const db::CplxTr
if (empty_cell) {
r.draw (dbox, 0, contour, vertices, 0);
+ if (opt_bitmap) {
+ r.draw (dbox, 0, 0, opt_bitmap, 0);
+ }
} else {
r.draw (dbox, fill, contour, 0, 0);
}
- if (! txt.empty () && (empty_cell || (dbox.width () > m_min_size_for_label && dbox.height () > m_min_size_for_label))) {
+ db::DBox dbox_for_label = trans * box_for_label;
+ if (! txt.empty () && (empty_cell || (dbox_for_label.width () > m_min_size_for_label && dbox_for_label.height () > m_min_size_for_label))) {
// Hint: we render to contour because the texts plane is reserved for properties
- r.draw (dbox, txt,
+ r.draw (dbox_for_label, txt,
db::Font (m_box_font),
db::HAlignCenter,
db::VAlignCenter,
@@ -817,34 +907,58 @@ RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type c
return;
}
+ std::unique_ptr opt_bitmap;
+
+ unsigned int plane_group = 2;
+ if (drawing_context) {
+ plane_group = 0;
+ } else if (m_child_context_enabled && level + 1 > 0) {
+ plane_group = 1;
+ }
+
+ lay::CanvasPlane *vertex = m_planes[3 + plane_group * (planes_per_layer / 3)];
+ lay::Bitmap *vertex_bitmap = dynamic_cast (vertex);
+ if (vertex_bitmap) {
+ opt_bitmap.reset (new lay::Bitmap (vertex_bitmap->width (), vertex_bitmap->height (), vertex_bitmap->resolution (), vertex_bitmap->font_resolution ()));
+ }
+
for (std::vector::const_iterator b = redraw_regions.begin (); b != redraw_regions.end (); ++b) {
- draw_boxes_impl (drawing_context, ci, trans, *b, level, for_ghosts);
+ draw_boxes_impl (drawing_context, ci, trans, *b, level, for_ghosts, opt_bitmap.get ());
}
}
void
-RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_box, int level, bool for_ghosts)
+RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_box, int level, bool for_ghosts, lay::Bitmap *opt_bitmap)
{
lay::Renderer &r = *mp_renderer;
const db::Cell &cell = mp_layout->cell (ci);
+ // small cells are dropped
+ if (m_drop_small_cells && drop_cell (cell, trans)) {
+ return;
+ }
+
// For small bboxes, the cell outline can be reduced ..
db::Box bbox = cell.bbox_with_empty ();
bool empty_cell = cell.bbox ().empty ();
- if (m_drop_small_cells && drop_cell (cell, trans)) {
+ db::Box bbox_for_label;
+ if (empty_cell) {
+ db::Coord d = db::coord_traits::rounded (0.5 * m_empty_cell_dimension / mp_layout->dbu ());
+ bbox_for_label = bbox.enlarged (db::Vector (d, d));
+ } else {
+ bbox_for_label = bbox;
+ }
- // small cell dropped
-
- } else if (for_ghosts && cell.is_ghost_cell ()) {
+ if (for_ghosts && cell.is_ghost_cell ()) {
// paint the box on this level
- draw_cell (drawing_context, level, trans, bbox, empty_cell, mp_layout->display_name (ci));
+ draw_cell (drawing_context, level, trans, bbox, bbox_for_label, empty_cell, mp_layout->display_name (ci), opt_bitmap);
} else if (! for_ghosts && ! cell.is_ghost_cell () && (level == m_to_level || (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ()))) {
// paint the box on this level
- draw_cell (drawing_context, level, trans, bbox, empty_cell, mp_layout->display_name (ci));
+ draw_cell (drawing_context, level, trans, bbox, bbox_for_label, empty_cell, mp_layout->display_name (ci), opt_bitmap);
} else if (level < m_to_level) {
@@ -855,7 +969,7 @@ RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type c
// the cell is a very small box and we know there must be
// some level at which to draw the boundary: just draw it
// here and stop looking further down ..
- draw_cell (drawing_context, level, trans, bbox, empty_cell, std::string ());
+ draw_cell (drawing_context, level, trans, bbox, bbox_for_label, empty_cell, std::string (), opt_bitmap);
}
} else {
@@ -884,6 +998,8 @@ RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type c
bool anything = false;
db::cell_index_type last_ci = std::numeric_limits::max ();
+ size_t current_quad_id = 0;
+
db::Cell::touching_iterator inst = cell.begin_touching (*v);
while (! inst.at_end ()) {
@@ -893,6 +1009,20 @@ RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type c
db::Box new_cell_box = cell_inst.bbox (bc);
bool empty_inst_cell = mp_layout->cell (new_ci).bbox ().empty ();
+ // skip this quad if we have drawn something here already
+ size_t qid = inst.quad_id ();
+ bool skip = false;
+ if (empty_inst_cell && qid != current_quad_id) {
+ current_quad_id = qid;
+ skip = opt_bitmap && skip_quad (inst.quad_box () & bbox, opt_bitmap, trans);
+ }
+
+ if (skip) {
+ // move on to the next quad
+ inst.skip_quad ();
+ continue;
+ }
+
if (last_ci != new_ci) {
// Hint: don't use any_cell_box on partially visible cells because that will degrade performance
if (new_cell_box.inside (*v)) {
@@ -940,6 +1070,9 @@ RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type c
if (empty_inst_cell) {
lay::CanvasPlane *vertices = m_planes[3 + plane_group * (planes_per_layer / 3)];
r.draw (cell_inst.bbox (bc), trans, 0, 0, vertices, 0);
+ if (opt_bitmap) {
+ r.draw (cell_inst.bbox (bc), trans, opt_bitmap, 0, 0, 0);
+ }
}
lay::CanvasPlane *contour = m_planes[1 + plane_group * (planes_per_layer / 3)];
@@ -955,9 +1088,10 @@ RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type c
for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ) {
test_snapshot (0);
+
db::ICplxTrans t (cell_inst.complex_trans (*p));
db::Box new_vp = safe_transformed_box (*v, t.inverted ());
- draw_boxes_impl (drawing_context, new_ci, trans * t, new_vp, level + 1, for_ghosts);
+ draw_boxes_impl (drawing_context, new_ci, trans * t, new_vp, level + 1, for_ghosts, opt_bitmap);
if (p.quad_id () > 0 && p.quad_id () != qid) {
@@ -1292,91 +1426,7 @@ RedrawThreadWorker::any_text_shapes (db::cell_index_type cell_index, unsigned in
return c->second;
}
-static bool
-has_zero_bit (const lay::Bitmap *bitmap, unsigned int ixmin, unsigned int iymin, unsigned int ixmax, unsigned int iymax)
-{
- uint32_t imin = ixmin / 32;
- uint32_t imax = ixmax / 32;
-
- if (imin == imax) {
-
- uint32_t m = ((unsigned int) 0xffffffff << (ixmin % 32)) & ((unsigned int) 0xffffffff >> (31 - (ixmax % 32)));
-
- for (unsigned int y = iymin; y <= iymax; ++y) {
-
- if (bitmap->is_scanline_empty (y)) {
- return true;
- }
-
- if ((bitmap->scanline (y) [imin] & m) != m) {
- return true;
- }
-
- }
-
- } else {
-
- uint32_t m1 = ((unsigned int) 0xffffffff << (ixmin % 32));
- uint32_t m2 = ((unsigned int) 0xffffffff >> (31 - (ixmax % 32)));
-
- for (unsigned int y = iymin; y <= iymax; ++y) {
-
- if (bitmap->is_scanline_empty (y)) {
- return true;
- }
-
- if ((bitmap->scanline (y) [imin] & m1) != m1) {
- return true;
- }
- for (unsigned int i = imin + 1; i < imax; ++i) {
- if (bitmap->scanline (y) [i] != 0xffffffff) {
- return true;
- }
- }
- if ((bitmap->scanline (y) [imax] & m2) != m2) {
- return true;
- }
-
- }
-
- }
-
- return false;
-}
-
-static bool
-skip_quad (const db::Box &qb, const lay::Bitmap *vertex_bitmap, const db::CplxTrans &trans)
-{
- double threshold = 32 / trans.mag (); // don't check cells below 32x32 pixels
- if (qb.empty () || qb.width () > threshold || qb.height () > threshold || !vertex_bitmap) {
- return false;
- }
-
- db::DBox qb_trans = (trans * qb) & db::DBox (0, 0, vertex_bitmap->width () - 1.0 - 1e-6, vertex_bitmap->height () - 1.0 - 1e-6);
- if (qb_trans.empty ()) {
- return true;
- }
-
- int ixmin = (unsigned int)(qb_trans.left () + 0.5);
- int ixmax = (unsigned int)(qb_trans.right () + 0.5);
- int iymin = (unsigned int)(qb_trans.bottom () + 0.5);
- int iymax = (unsigned int)(qb_trans.top () + 0.5);
- if (! has_zero_bit (vertex_bitmap, ixmin, iymin, ixmax, iymax)) {
- return true; // skip
- } else {
- return false;
- }
-}
-
-inline void
-copy_bitmap (const lay::Bitmap *from, lay::Bitmap *to, int dx, int dy)
-{
- if (to) {
- to->merge (from, dx, dy);
- }
-}
-
-std::vector
+std::vector
RedrawThreadWorker::search_regions (const db::Box &cell_bbox, const db::Box &vp, int level)
{
std::vector vv;
diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.h b/src/laybasic/laybasic/layRedrawThreadWorker.h
index 4f108c587..7b97b7eae 100644
--- a/src/laybasic/laybasic/layRedrawThreadWorker.h
+++ b/src/laybasic/laybasic/layRedrawThreadWorker.h
@@ -185,13 +185,13 @@ private:
void draw_boxes_for_ghosts (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level);
void draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level);
void draw_boxes_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level, bool for_ghosts);
- void draw_boxes_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_region, int level, bool for_ghosts);
+ void draw_boxes_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_region, int level, bool for_ghosts, Bitmap *opt_bitmap);
void draw_box_properties_for_ghosts (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level);
void draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level);
void draw_box_properties_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level, bool for_ghosts);
void draw_box_properties_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level, db::properties_id_type prop_id, bool for_ghosts);
void draw_box_properties_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_box, int level, db::properties_id_type prop_id, bool for_ghosts);
- void draw_cell (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, bool empty_cell, const std::string &txt);
+ void draw_cell (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, const db::Box &box_for_label, bool empty_cell, const std::string &txt, Bitmap *opt_bitmap);
void draw_cell_properties (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, db::properties_id_type prop_id);
void draw_cell_shapes (const db::CplxTrans &trans, const db::Cell &cell, const db::Box &vp, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text);
void test_snapshot (const UpdateSnapshotCallback *update_snapshot);
@@ -216,6 +216,7 @@ private:
int m_from_level, m_to_level;
int m_from_level_default, m_to_level_default;
int m_min_size_for_label;
+ double m_empty_cell_dimension;
bool m_box_text_transform;
unsigned int m_box_font;
unsigned int m_text_font;
diff --git a/src/laybasic/laybasic/laySelector.cc b/src/laybasic/laybasic/laySelector.cc
index 8a86a6048..b5e7cf4cb 100644
--- a/src/laybasic/laybasic/laySelector.cc
+++ b/src/laybasic/laybasic/laySelector.cc
@@ -47,7 +47,6 @@ SelectionService::SelectionService (lay::LayoutViewBase *view) :
mp_view (view),
mp_box (0),
m_color (0),
- m_buttons (0),
m_hover (false),
m_hover_wait (false),
m_mouse_in_window (false)
@@ -204,7 +203,6 @@ SelectionService::mouse_press_event (const db::DPoint &p, unsigned int buttons,
if ((buttons & lay::LeftButton) != 0) {
mp_view->stop_redraw (); // TODO: how to restart if selection is aborted?
- m_buttons = buttons;
begin (p);
return true;
}
@@ -263,7 +261,7 @@ SelectionService::mouse_click_event (const db::DPoint &p, unsigned int buttons,
}
bool
-SelectionService::mouse_release_event (const db::DPoint & /*p*/, unsigned int /*buttons*/, bool prio)
+SelectionService::mouse_release_event (const db::DPoint & /*p*/, unsigned int buttons, bool prio)
{
hover_reset ();
@@ -274,8 +272,8 @@ SelectionService::mouse_release_event (const db::DPoint & /*p*/, unsigned int /*
if (mp_view) {
lay::Editable::SelectionMode mode = lay::Editable::Replace;
- bool shift = ((m_buttons & lay::ShiftButton) != 0);
- bool ctrl = ((m_buttons & lay::ControlButton) != 0);
+ bool shift = ((buttons & lay::ShiftButton) != 0);
+ bool ctrl = ((buttons & lay::ControlButton) != 0);
if (shift && ctrl) {
mode = lay::Editable::Invert;
} else if (shift) {
diff --git a/src/laybasic/laybasic/laySelector.h b/src/laybasic/laybasic/laySelector.h
index d4f29dbd3..0d9eee884 100644
--- a/src/laybasic/laybasic/laySelector.h
+++ b/src/laybasic/laybasic/laySelector.h
@@ -91,7 +91,6 @@ private:
lay::LayoutViewBase *mp_view;
lay::RubberBox *mp_box;
unsigned int m_color;
- unsigned int m_buttons;
bool m_hover;
bool m_hover_wait;
db::DPoint m_hover_point;
diff --git a/src/laybasic/laybasic/layViewObject.cc b/src/laybasic/laybasic/layViewObject.cc
index 6efa9f881..488ff5fbb 100644
--- a/src/laybasic/laybasic/layViewObject.cc
+++ b/src/laybasic/laybasic/layViewObject.cc
@@ -907,7 +907,7 @@ ViewObjectUI::send_mouse_double_clicked_event (const db::DPoint &pt, unsigned in
}
void
-ViewObjectUI::send_mouse_release_event (const db::DPoint &pt, unsigned int /*buttons*/)
+ViewObjectUI::send_mouse_release_event (const db::DPoint &pt, unsigned int buttons)
{
try {
@@ -916,23 +916,27 @@ ViewObjectUI::send_mouse_release_event (const db::DPoint &pt, unsigned int /*but
bool done = false;
+ // Qt does not include the released button in the mask, so we take the mouse buttons that we stored
+ // on "press", but use the current modifiers (issue #2214)
+ unsigned int effective_buttons = (m_mouse_buttons & lay::MouseButtonMask) | (buttons & lay::ModifierMask);
+
m_mouse_pos = pt;
db::DPoint p = pixel_to_um (m_mouse_pos);
auto grabbed = m_grabbed;
for (auto g = grabbed.begin (); !done && g != grabbed.end (); ++g) {
if (m_mouse_pressed_state) {
- done = (*g)->enabled () && (*g)->mouse_click_event (p, m_mouse_buttons, true);
+ done = (*g)->enabled () && (*g)->mouse_click_event (p, effective_buttons, true);
} else {
- done = (*g)->enabled () && (*g)->mouse_release_event (p, m_mouse_buttons, true);
+ done = (*g)->enabled () && (*g)->mouse_release_event (p, effective_buttons, true);
}
}
if (! done && mp_active_service && mp_active_service->enabled ()) {
if (m_mouse_pressed_state) {
- done = mp_active_service->mouse_click_event (p, m_mouse_buttons, true);
+ done = mp_active_service->mouse_click_event (p, effective_buttons, true);
} else {
- done = mp_active_service->mouse_release_event (p, m_mouse_buttons, true);
+ done = mp_active_service->mouse_release_event (p, effective_buttons, true);
}
}
@@ -942,9 +946,9 @@ ViewObjectUI::send_mouse_release_event (const db::DPoint &pt, unsigned int /*but
++next;
if ((*svc)->enabled ()) {
if (m_mouse_pressed_state) {
- done = (*svc)->mouse_click_event (p, m_mouse_buttons, false);
+ done = (*svc)->mouse_click_event (p, effective_buttons, false);
} else {
- done = (*svc)->mouse_release_event (p, m_mouse_buttons, false);
+ done = (*svc)->mouse_release_event (p, effective_buttons, false);
}
}
svc = next;
@@ -952,9 +956,9 @@ ViewObjectUI::send_mouse_release_event (const db::DPoint &pt, unsigned int /*but
if (! done) {
if (m_mouse_pressed_state) {
- mouse_click_event (p, m_mouse_buttons);
+ mouse_click_event (p, effective_buttons);
} else {
- mouse_release_event (p, m_mouse_buttons);
+ mouse_release_event (p, effective_buttons);
}
}
diff --git a/src/laybasic/laybasic/layViewObject.h b/src/laybasic/laybasic/layViewObject.h
index a175e5c5d..6ef9b691e 100644
--- a/src/laybasic/laybasic/layViewObject.h
+++ b/src/laybasic/laybasic/layViewObject.h
@@ -548,12 +548,14 @@ private:
* @brief Describes the button state (supposed to be ored)
*/
enum ButtonState {
- ShiftButton = 1,
- ControlButton = 2,
- AltButton = 4,
- LeftButton = 8,
- MidButton = 16,
- RightButton = 32
+ ShiftButton = 0x01,
+ ControlButton = 0x02,
+ AltButton = 0x04,
+ ModifierMask = 0x07, // all keyboard modifiers
+ LeftButton = 0x08,
+ MidButton = 0x10,
+ RightButton = 0x20,
+ MouseButtonMask = 0x38 // all mouse buttons
};
/**
diff --git a/src/laybasic/laybasic/laybasicConfig.h b/src/laybasic/laybasic/laybasicConfig.h
index 681c3c8ac..e5f3a3777 100644
--- a/src/laybasic/laybasic/laybasicConfig.h
+++ b/src/laybasic/laybasic/laybasicConfig.h
@@ -91,6 +91,7 @@ static const std::string cfg_crosshair_cursor_enabled ("crosshair-cursor-enabled
static const std::string cfg_markers_visible ("markers-visible");
static const std::string cfg_min_inst_label_size ("min-inst-label-size");
+static const std::string cfg_empty_cell_dimension ("empty-cell-dimension");
static const std::string cfg_cell_box_text_font ("inst-label-font");
static const std::string cfg_cell_box_text_transform ("inst-label-transform");
static const std::string cfg_cell_box_color ("inst-color");
diff --git a/src/layui/layui/LayoutViewConfigPage2a.ui b/src/layui/layui/LayoutViewConfigPage2a.ui
index 23bc34c44..0dbfdb25f 100644
--- a/src/layui/layui/LayoutViewConfigPage2a.ui
+++ b/src/layui/layui/LayoutViewConfigPage2a.ui
@@ -7,7 +7,7 @@
0
0
656
- 397
+ 451
@@ -49,85 +49,14 @@
false
-
- 9
-
6
- -
-
-
- QFrame::NoFrame
+
-
+
+
+ Label font
-
- QFrame::Raised
-
-
-
- 6
-
-
- 0
-
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::Expanding
-
-
-
- 141
- 21
-
-
-
-
-
-
-
- -
-
-
- QFrame::NoFrame
-
-
- QFrame::Raised
-
-
-
- 6
-
-
- 0
-
-
-
-
-
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
-
-
@@ -190,6 +119,20 @@
+ -
+
+
+ Cell box color
+
+
+
+ -
+
+
+ Transform text with cell instance (not available for "Default" font)
+
+
+
-
@@ -197,21 +140,7 @@
- -
-
-
- Label font
-
-
-
- -
-
-
- Cell box color
-
-
-
- -
+
-
Qt::Vertical
@@ -227,10 +156,139 @@
- -
-
+
-
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
+ 6
+
+
+ 0
+
+
-
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
+ 6
+
+
+ 0
+
+
-
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Expanding
+
+
+
+ 141
+ 21
+
+
+
+
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ µm (for label scaling)
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 159
+ 20
+
+
+
+
+
+
+
+ -
+
- Transform text with cell instance (not available for "Default" font)
+ Empty cell virtual size
diff --git a/src/layui/layui/layLayoutViewConfigPages.cc b/src/layui/layui/layLayoutViewConfigPages.cc
index 55a766913..145d2476f 100644
--- a/src/layui/layui/layLayoutViewConfigPages.cc
+++ b/src/layui/layui/layLayoutViewConfigPages.cc
@@ -227,6 +227,10 @@ LayoutViewConfigPage2a::setup (lay::Dispatcher *root)
root->config_get (cfg_min_inst_label_size, n);
mp_ui->cell_min_size_for_label_edit->setText (tl::to_qstring (tl::to_string (n)));
+ double ecd = 0;
+ root->config_get (cfg_empty_cell_dimension, ecd);
+ mp_ui->empty_cell_dimension->setText (tl::to_qstring (tl::to_string (ecd)));
+
bool gs_visible = true;
root->config_get (cfg_guiding_shape_visible, gs_visible);
mp_ui->pcell_gs_group->setChecked (gs_visible);
@@ -263,6 +267,12 @@ LayoutViewConfigPage2a::commit (lay::Dispatcher *root)
tl::from_string_ext (tl::to_string (mp_ui->cell_min_size_for_label_edit->text ()), n);
root->config_set (cfg_min_inst_label_size, n);
} catch (...) { }
+
+ try {
+ double ecd;
+ tl::from_string_ext (tl::to_string (mp_ui->empty_cell_dimension->text ()), ecd);
+ root->config_set (cfg_empty_cell_dimension, ecd);
+ } catch (...) { }
}
// ------------------------------------------------------------
diff --git a/src/layview/layview/layLayoutView_qt.cc b/src/layview/layview/layLayoutView_qt.cc
index 55cad6857..2b74c67c8 100644
--- a/src/layview/layview/layLayoutView_qt.cc
+++ b/src/layview/layview/layLayoutView_qt.cc
@@ -453,6 +453,14 @@ LayoutView::LayoutView (lay::LayoutView *source, db::Manager *manager, bool edit
LayoutView::set_active_cellview_index (source->active_cellview_index ());
}
+void
+LayoutView::add_notification (const LayoutViewNotification ¬ification)
+{
+ if (mp_widget) {
+ mp_widget->add_notification (notification);
+ }
+}
+
bool
LayoutView::event_filter (QObject *obj, QEvent *event, bool &taken)
{
@@ -1577,10 +1585,10 @@ LayoutView::signal_selection_changed ()
}
void
-LayoutView::message (const std::string &s, int timeout)
+LayoutView::message (const std::string &s, int timeout, int priority)
{
if (mp_widget) {
- mp_widget->emit_show_message (s, timeout * 1000);
+ mp_widget->emit_show_message (s, timeout * 1000, priority);
}
}
diff --git a/src/layview/layview/layLayoutView_qt.h b/src/layview/layview/layLayoutView_qt.h
index 837146a9a..548e13012 100644
--- a/src/layview/layview/layLayoutView_qt.h
+++ b/src/layview/layview/layLayoutView_qt.h
@@ -1,4 +1,4 @@
-
+#
/*
KLayout Layout Viewer
@@ -149,6 +149,11 @@ public:
*/
~LayoutView ();
+ /**
+ * @brief Adds a notification
+ */
+ virtual void add_notification (const LayoutViewNotification ¬ification);
+
/**
* @brief Gets the widget object that view is embedded in
*/
@@ -182,7 +187,7 @@ public:
/**
* @brief Displays a status message
*/
- void message (const std::string &s = "", int timeout = 10);
+ virtual void message (const std::string &s = "", int timeout = 10, int priority = 0);
/**
* @brief Sets the keyboard focus to the view
@@ -695,70 +700,6 @@ private:
void activate_editor_option_pages ();
};
-/**
- * @brief Descriptor for a notification inside the layout view
- *
- * Notifications are popups added at the top of the view to indicate need for reloading for example.
- * Notifications have a name, a title, optional actions (id, title) and a parameter (e.g. file path to reload).
- * Actions are mapped to QPushButtons.
- */
-class LAYVIEW_PUBLIC LayoutViewNotification
-{
-public:
- LayoutViewNotification (const std::string &name, const std::string &title, const tl::Variant ¶meter = tl::Variant ())
- : m_name (name), m_title (title), m_parameter (parameter)
- {
- // .. nothing yet ..
- }
-
- void add_action (const std::string &name, const std::string &title)
- {
- m_actions.push_back (std::make_pair (name, title));
- }
-
- const std::vector > &actions () const
- {
- return m_actions;
- }
-
- const std::string &name () const
- {
- return m_name;
- }
-
- const std::string &title () const
- {
- return m_title;
- }
-
- const tl::Variant ¶meter () const
- {
- return m_parameter;
- }
-
- bool operator<(const LayoutViewNotification &other) const
- {
- if (m_name != other.name ()) {
- return m_name < other.name ();
- }
- return m_parameter < other.parameter ();
- }
-
- bool operator==(const LayoutViewNotification &other) const
- {
- if (m_name != other.name ()) {
- return false;
- }
- return m_parameter == other.parameter ();
- }
-
-private:
- std::string m_name;
- std::string m_title;
- tl::Variant m_parameter;
- std::vector > m_actions;
-};
-
/**
* @brief A widget representing a notification
*/
@@ -810,12 +751,12 @@ public:
/**
* @brief Adds a notification
*/
- void add_notification (const LayoutViewNotification ¬ificaton);
+ void add_notification (const LayoutViewNotification ¬ification);
/**
* @brief Removes a notification
*/
- void remove_notification (const LayoutViewNotification ¬ificaton);
+ void remove_notification (const LayoutViewNotification ¬ification);
/**
* @brief Gets the LayoutView embedded into this widget
@@ -832,7 +773,7 @@ public:
void emit_title_changed (lay::LayoutView *view) { emit title_changed (view); }
void emit_dirty_changed (lay::LayoutView *view) { emit dirty_changed (view); }
- void emit_show_message (const std::string &s, int ms) { emit show_message (s, ms); }
+ void emit_show_message (const std::string &s, int ms, int priority) { emit show_message (s, ms, priority); }
void emit_current_pos_changed (double x, double y, bool dbu_units) { emit current_pos_changed (x, y, dbu_units); }
void emit_clear_current_pos () { emit clear_current_pos (); }
void emit_edits_enabled_changed () { emit edits_enabled_changed (); }
@@ -885,7 +826,7 @@ signals:
/**
* @brief This signal is emitted when the view wants to show a message for the given time (of infinitely for ms == 0)
*/
- void show_message (const std::string &s, int ms);
+ void show_message (const std::string &s, int ms, int priority);
/**
* @brief This signal is emitted when the view wants to indicate a mouse position change
diff --git a/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.cc b/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.cc
index 45cc16007..b35abccac 100644
--- a/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.cc
+++ b/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.cc
@@ -337,6 +337,16 @@ NetTracerData::requires_booleans (unsigned int from_layer) const
return r->second;
}
+std::set
+NetTracerData::original_layers () const
+{
+ std::set ol;
+ for (std::map >::const_iterator g = m_original_layers.begin (); g != m_original_layers.end (); ++g) {
+ ol.insert (g->second.begin (), g->second.end ());
+ }
+ return ol;
+}
+
void
NetTracerData::clean_l2n_regions ()
{
diff --git a/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.h b/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.h
index ea6412243..1d926ab5a 100644
--- a/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.h
+++ b/src/plugins/tools/net_tracer/db_plugin/dbNetTracer.h
@@ -626,6 +626,13 @@ public:
*/
void configure_l2n (db::LayoutToNetlist &l2n);
+ /**
+ * @brief Gets the original layers
+ *
+ * These are the layer indexes used for computing the net tracer functions
+ */
+ std::set original_layers () const;
+
private:
unsigned int m_next_log_layer;
std::vector m_connections;
diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc
index 4106392ce..68b49a361 100644
--- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc
+++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc
@@ -207,7 +207,7 @@ NetTracerDialog::drag_cancel ()
{
if (m_mouse_state > 0) {
- view ()->message ();
+ view ()->message (std::string (), 0, 10);
ui ()->ungrab_mouse (this);
set_cursor (lay::Cursor::none);
@@ -241,7 +241,7 @@ NetTracerDialog::mouse_click_event (const db::DPoint &p, unsigned int buttons, b
m_mouse_first_point = p;
m_mouse_state = 3;
- view ()->message (tl::to_string (QObject::tr ("Click on the second point in the net")));
+ view ()->message (tl::to_string (QObject::tr ("Click on the second point in the net")), -1 /*infinitely*/, 10);
} else {
@@ -414,9 +414,24 @@ NetTracerDialog::get_net_tracer_setup (const lay::CellView &cv, db::NetTracerDat
db::NetTracerNet *
NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &stop_search_box, bool trace_path)
{
+ // determine the cellview
+ lay::CellView cv = view ()->cellview (m_cv_index);
+ if (! cv.is_valid ()) {
+ return 0;
+ }
+
+ // Set up the net tracer environment
+ db::NetTracerData tracer_data;
+ if (! get_net_tracer_setup (cv, tracer_data)) {
+ return 0;
+ }
+
+ std::set original_layers = tracer_data.original_layers ();
+
unsigned int start_layer = 0;
db::Point start_point;
db::Shape start_shape;
+ db::ICplxTrans start_trans;
// locate the seed
{
@@ -426,7 +441,7 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto
// go through all visible layers of all cellviews and find a seed shape
for (lay::LayerPropertiesConstIterator lprop = view ()->begin_layers (); ! lprop.at_end (); ++lprop) {
- if (lprop->is_visual ()) {
+ if (lprop->is_visual () && lprop->cellview_index () == m_cv_index && original_layers.find ((unsigned int) lprop->layer_index ()) != original_layers.end ()) {
finder.find (view (), *lprop, start_search_box);
}
}
@@ -440,15 +455,10 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto
m_cv_index = r->cv_index ();
start_shape = r->shape ();
start_layer = r->layer ();
+ start_trans = r->trans ();
}
- // determine the cellview
- lay::CellView cv = view ()->cellview (m_cv_index);
- if (! cv.is_valid ()) {
- return 0;
- }
-
// determine the start point
{
@@ -463,20 +473,15 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto
// stop if the center start point is not inside the start polygon
db::Polygon poly;
- if (start_shape.polygon (poly) && db::inside_poly (poly.begin_edge (), start_point) < 0) {
+ if (start_shape.polygon (poly) && db::inside_poly (poly.begin_edge (), start_trans.inverted () * start_point) < 0) {
return 0;
}
}
- // Set up the net tracer environment
- db::NetTracerData tracer_data;
- if (! get_net_tracer_setup (cv, tracer_data)) {
- return 0;
- }
-
unsigned int stop_layer = 0;
db::Point stop_point;
+ db::ICplxTrans stop_trans;
// locate the stop shape (the second mouse click)
if (trace_path) {
@@ -486,7 +491,7 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto
// go through all visible layers of all cellviews and find a seed shape
for (lay::LayerPropertiesConstIterator lprop = view ()->begin_layers (); ! lprop.at_end (); ++lprop) {
- if (lprop->is_visual ()) {
+ if (lprop->is_visual () && lprop->cellview_index () == m_cv_index && original_layers.find ((unsigned int) lprop->layer_index ()) != original_layers.end ()) {
finder.find (view (), *lprop, stop_search_box);
}
}
@@ -510,10 +515,11 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto
stop_point = tt.inverted ().trans (stop_search_box.center ());
stop_layer = r->layer ();
+ stop_trans = r->trans ();
// stop if the center stop point is not inside the stop polygon
db::Polygon poly;
- if (r->shape ().polygon (poly) && db::inside_poly (poly.begin_edge (), stop_point) < 0) {
+ if (r->shape ().polygon (poly) && db::inside_poly (poly.begin_edge (), stop_trans.inverted () * stop_point) < 0) {
return 0;
}
@@ -1258,7 +1264,7 @@ BEGIN_PROTECTED
commit ();
net_list->setCurrentItem (0);
m_mouse_state = 2;
- view ()->message (tl::to_string (QObject::tr ("Click on the first point in the net")));
+ view ()->message (tl::to_string (QObject::tr ("Click on the first point in the net")), -1 /*infinitely*/, 10);
ui ()->grab_mouse (this, false);
END_PROTECTED
}
@@ -1270,7 +1276,7 @@ BEGIN_PROTECTED
commit ();
net_list->setCurrentItem (0);
m_mouse_state = 1;
- view ()->message (tl::to_string (QObject::tr ("Click on a point in the net")));
+ view ()->message (tl::to_string (QObject::tr ("Click on a point in the net")), -1 /*infinitely*/, 10);
ui ()->grab_mouse (this, false);
END_PROTECTED
}
@@ -1293,7 +1299,7 @@ NetTracerDialog::release_mouse ()
add_pb->setChecked (false);
add2_pb->setChecked (false);
m_mouse_state = 0;
- view ()->message ();
+ view ()->message (std::string (), 0, 10);
ui ()->ungrab_mouse (this);
set_cursor (lay::Cursor::none);
}