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); }