Merge branch 'master' into issue-2224

This commit is contained in:
Matthias Koefferlein 2025-12-07 22:30:37 +01:00
commit 1e8321d8fb
30 changed files with 743 additions and 469 deletions

View File

@ -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

View File

@ -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: |

View File

@ -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
<https://www.gnu.org/licenses/>.
On Debian GNU/Linux systems, the complete text of the GNU General
Public License can be found in `/usr/share/common-licenses/GPL'.

View File

@ -762,9 +762,9 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, co
{
if (! point_mode ()) {
for (std::vector<int>::const_iterator l = layers ().begin (); l != layers ().end (); ++l) {
for (std::vector<unsigned int>::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<int>::const_iterator l = layers ().begin (); l != layers ().end (); ++l) {
for (std::vector<unsigned int>::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) {

View File

@ -328,6 +328,12 @@ set_menu_items_hidden (lay::MainWindow *mw, const std::map<std::string, bool> &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<lay::MainWindow> decl_MainWindow (QT_EXTERNAL_BASE (QMainWindow) "lay", "MainWindow",
// Dispatcher interface and convenience functions
@ -475,15 +481,36 @@ Class<lay::MainWindow> 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"

View File

@ -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<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::end (); ++cls) {
const StreamWriterPluginDeclaration *decl = dynamic_cast <const StreamWriterPluginDeclaration *> (&*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)));

View File

@ -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<int, std::string> m_messages;
std::unique_ptr<QPrinter> mp_printer;
std::vector<QString> m_changed_files;
std::string m_title;

View File

@ -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<gsi::ButtonStateNamespace> 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") +

View File

@ -125,58 +125,76 @@ static void drag_cancel_impl (lay::EditorServiceBase *p)
Class<lay::EditorServiceBase> 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<gsi::PluginImpl> 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"
) +

View File

@ -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<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::end (); ++cls) {
const lay::StreamWriterPluginDeclaration *decl = dynamic_cast <const lay::StreamWriterPluginDeclaration *> (&*cls);
if (! decl || decl->options_alias ()) {
continue;
}
std::unique_ptr<db::FormatSpecificWriterOptions> 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 ();

View File

@ -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
*

View File

@ -87,7 +87,7 @@ Finder::closer (double d)
}
void
Finder::start (lay::LayoutViewBase *view, unsigned int cv_index, const std::vector<db::DCplxTrans> &trans, const db::DBox &region, const db::DBox &scan_region, int min_level, int max_level, const std::vector<int> &layers)
Finder::start (lay::LayoutViewBase *view, unsigned int cv_index, const std::vector<db::DCplxTrans> &trans, const db::DBox &region, const db::DBox &scan_region, int min_level, int max_level, const std::vector<unsigned int> &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 <db::CellInst, false> (*mp_layout, (unsigned int) layers [0]);
m_cell_box_convert = db::box_convert <db::Cell, false> ((unsigned int) layers [0]);
m_box_convert = db::box_convert <db::CellInst, false> (*mp_layout, layers [0]);
m_cell_box_convert = db::box_convert <db::Cell, false> (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 &region_mu)
std::sort (lprops.begin (), lprops.end (), LPContextCompareOp ());
std::vector<int> layers;
std::vector<unsigned int> layers;
for (std::vector<lay::LayerPropertiesConstIterator>::const_iterator llp = lprops.begin (); llp != lprops.end (); ) {
layers.clear ();
@ -347,7 +347,10 @@ ShapeFinder::find (LayoutViewBase *view, const db::DBox &region_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<int> layers;
layers.push_back (lprops.layer_index ());
std::vector<unsigned int> 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<db::properties_id_type> *prop_sel, bool inv_prop_sel, const lay::HierarchyLevelSelection &hier_sel, const std::vector<db::DCplxTrans> &trans_mu, const std::vector<int> &layers, const db::DBox &region_mu)
bool
ShapeFinder::find_internal (lay::LayoutViewBase *view, unsigned int cv_index, const std::set<db::properties_id_type> *prop_sel, bool inv_prop_sel, const lay::HierarchyLevelSelection &hier_sel, const std::vector<db::DCplxTrans> &trans_mu, const std::vector<unsigned int> &layers, const db::DBox &region_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<int>::const_iterator l = layers ().begin (); l != layers ().end (); ++l) {
for (std::vector<unsigned int>::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<int>::const_iterator l = layers ().begin (); l != layers ().end (); ++l) {
for (std::vector<unsigned int>::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<db::DCplxTrans> 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<int> ());
start (view, cv_index, tv, region_mu, region_mu, view->get_min_hier_levels (), view->get_max_hier_levels (), std::vector<unsigned int> ());
} 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 <db::CellInst, false> 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)) {

View File

@ -132,7 +132,7 @@ public:
}
protected:
const std::vector<int> &layers () const
const std::vector<unsigned int> &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<db::DCplxTrans> &trans, const db::DBox &region, const db::DBox &scan_region, int min_level, int max_level, const std::vector<int> &layers = std::vector<int> ());
void start (LayoutViewBase *view, unsigned int cv_index, const std::vector<db::DCplxTrans> &trans, const db::DBox &region, const db::DBox &scan_region, int min_level, int max_level, const std::vector<unsigned int> &layers = std::vector<unsigned int> ());
/**
* @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<int> m_layers;
std::vector<unsigned int> 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<lay::ObjectInstPath> *excludes = 0, bool capture_all_shapes = false);
bool find (LayoutViewBase *view, const lay::LayerProperties &lprops, const db::DBox &region_mu);
bool find (LayoutViewBase *view, const db::DBox &region_mu);
bool find (lay::LayoutViewBase *view, const lay::LayerProperties &lprops, const db::DBox &region_mu);
bool find (lay::LayoutViewBase *view, const db::DBox &region_mu);
iterator begin () const
{
@ -327,7 +327,7 @@ private:
bool inv_prop_sel,
const lay::HierarchyLevelSelection &hier_sel,
const std::vector<db::DCplxTrans> &trans_mu,
const std::vector<int> &layers,
const std::vector<unsigned int> &layers,
const db::DBox &region_mu);
const std::set<lay::ObjectInstPath> *mp_excludes;

View File

@ -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) {

View File

@ -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 &parameter = 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<std::pair<std::string, std::string> > &actions () const
{
return m_actions;
}
const std::string &name () const
{
return m_name;
}
const std::string &title () const
{
return m_title;
}
const tl::Variant &parameter () 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<std::pair<std::string, std::string> > 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;

View File

@ -55,6 +55,7 @@ public:
options.push_back (std::pair<std::string, std::string> (cfg_current_lib_view, ""));
options.push_back (std::pair<std::string, std::string> (cfg_hide_empty_layers, "false"));
options.push_back (std::pair<std::string, std::string> (cfg_min_inst_label_size, "16"));
options.push_back (std::pair<std::string, std::string> (cfg_empty_cell_dimension, "4"));
options.push_back (std::pair<std::string, std::string> (cfg_cell_box_text_font, "0"));
options.push_back (std::pair<std::string, std::string> (cfg_cell_box_text_transform, "true"));
options.push_back (std::pair<std::string, std::string> (cfg_cell_box_color, "auto"));

View File

@ -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<lay::Bitmap> 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<lay::Bitmap *> (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<db::Box>::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<db::Coord>::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<db::cell_index_type>::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<db::Box>
std::vector<db::Box>
RedrawThreadWorker::search_regions (const db::Box &cell_bbox, const db::Box &vp, int level)
{
std::vector<db::Box> vv;

View File

@ -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 <db::Box> &redraw_regions, int level);
void draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector <db::Box> &redraw_regions, int level);
void draw_boxes_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector <db::Box> &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 <db::Box> &redraw_regions, int level);
void draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector <db::Box> &redraw_regions, int level);
void draw_box_properties_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector <db::Box> &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 <db::Box> &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;

View File

@ -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) {

View File

@ -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;

View File

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

View File

@ -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
};
/**

View File

@ -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");

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>656</width>
<height>397</height>
<height>451</height>
</rect>
</property>
<property name="windowTitle">
@ -49,85 +49,14 @@
<bool>false</bool>
</property>
<layout class="QGridLayout">
<property name="margin" stdset="0">
<number>9</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="1" column="1">
<widget class="QFrame" name="frame_3">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
<item row="2" column="0">
<widget class="QLabel" name="textLabel1_3">
<property name="text">
<string>Label font</string>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin" stdset="0">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="cell_font_cb"/>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>141</width>
<height>21</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="2" column="1">
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin" stdset="0">
<number>0</number>
</property>
<item>
<widget class="lay::ColorButton" name="cell_box_color_pb">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="0" column="1">
@ -190,6 +119,20 @@
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="textLabel3">
<property name="text">
<string>Cell box color</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="cell_xform_text_cbx">
<property name="text">
<string>Transform text with cell instance (not available for &quot;Default&quot; font)</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="textLabel1_2_2">
<property name="text">
@ -197,21 +140,7 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="textLabel1_3">
<property name="text">
<string>Label font</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="textLabel3">
<property name="text">
<string>Cell box color</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<item row="4" column="0" colspan="2">
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -227,10 +156,139 @@
</property>
</spacer>
</item>
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="cell_xform_text_cbx">
<item row="3" column="1">
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin" stdset="0">
<number>0</number>
</property>
<item>
<widget class="lay::ColorButton" name="cell_box_color_pb">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="2" column="1">
<widget class="QFrame" name="frame_3">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin" stdset="0">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="cell_font_cb"/>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>141</width>
<height>21</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<widget class="QFrame" name="frame_4">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="empty_cell_dimension">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>µm (for label scaling)</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>159</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Transform text with cell instance (not available for &quot;Default&quot; font)</string>
<string>Empty cell virtual size</string>
</property>
</widget>
</item>

View File

@ -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 (...) { }
}
// ------------------------------------------------------------

View File

@ -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 &notification)
{
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);
}
}

View File

@ -1,4 +1,4 @@
#
/*
KLayout Layout Viewer
@ -149,6 +149,11 @@ public:
*/
~LayoutView ();
/**
* @brief Adds a notification
*/
virtual void add_notification (const LayoutViewNotification &notification);
/**
* @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 &parameter = 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<std::pair<std::string, std::string> > &actions () const
{
return m_actions;
}
const std::string &name () const
{
return m_name;
}
const std::string &title () const
{
return m_title;
}
const tl::Variant &parameter () 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<std::pair<std::string, std::string> > m_actions;
};
/**
* @brief A widget representing a notification
*/
@ -810,12 +751,12 @@ public:
/**
* @brief Adds a notification
*/
void add_notification (const LayoutViewNotification &notificaton);
void add_notification (const LayoutViewNotification &notification);
/**
* @brief Removes a notification
*/
void remove_notification (const LayoutViewNotification &notificaton);
void remove_notification (const LayoutViewNotification &notification);
/**
* @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

View File

@ -337,6 +337,16 @@ NetTracerData::requires_booleans (unsigned int from_layer) const
return r->second;
}
std::set<unsigned int>
NetTracerData::original_layers () const
{
std::set<unsigned int> ol;
for (std::map <unsigned int, std::set <unsigned int> >::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 ()
{

View File

@ -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<unsigned int> original_layers () const;
private:
unsigned int m_next_log_layer;
std::vector <NetTracerConnection> m_connections;

View File

@ -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<unsigned int> 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);
}