From 8e4e5cde128f062dd50b81d02dbb8f8e95e9db62 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 3 Aug 2020 23:48:49 +0200 Subject: [PATCH] WIP: distribute selected objects feature --- src/edt/edt/AlignOptionsDialog.ui | 299 ++++++------ src/edt/edt/DistributeOptionsDialog.ui | 638 +++++++++++++++++++++++++ src/edt/edt/edt.pro | 3 +- src/edt/edt/edtDialogs.cc | 87 ++++ src/edt/edt/edtDialogs.h | 17 + src/edt/edt/edtMainService.cc | 234 +++++---- src/edt/edt/edtMainService.h | 15 + src/edt/edt/edtPlugin.cc | 1 + src/edt/edt/edtUtils.cc | 70 +++ src/edt/edt/edtUtils.h | 72 +++ 10 files changed, 1162 insertions(+), 274 deletions(-) create mode 100644 src/edt/edt/DistributeOptionsDialog.ui diff --git a/src/edt/edt/AlignOptionsDialog.ui b/src/edt/edt/AlignOptionsDialog.ui index 13d218095..c2002f0db 100644 --- a/src/edt/edt/AlignOptionsDialog.ui +++ b/src/edt/edt/AlignOptionsDialog.ui @@ -1,7 +1,8 @@ - + + AlignOptionsDialog - - + + 0 0 @@ -9,26 +10,26 @@ 392 - + Alignment Options - + - - + + Horizontal alignment - - - - + + + + - - + + :/align_none.png:/align_none.png - + 32 32 @@ -36,16 +37,16 @@ - - - + + + - - + + :/align_left.png:/align_left.png - + 32 32 @@ -53,16 +54,16 @@ - - - + + + - - + + :/align_hcenter.png:/align_hcenter.png - + 32 32 @@ -70,16 +71,16 @@ - - - + + + - - + + :/align_right.png:/align_right.png - + 32 32 @@ -87,12 +88,12 @@ - - - + + + Qt::Horizontal - + 243 20 @@ -100,55 +101,55 @@ - - - + + + none - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + left - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + center - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + right - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + Qt::Horizontal - + QSizePolicy::Fixed - + 20 20 @@ -156,15 +157,15 @@ - - - + + + Qt::Horizontal - + QSizePolicy::Fixed - + 20 20 @@ -172,15 +173,15 @@ - - - + + + Qt::Horizontal - + QSizePolicy::Fixed - + 20 20 @@ -192,21 +193,21 @@ - - + + Vertical alignment - - - - + + + + - - + + :/align_none.png:/align_none.png - + 32 32 @@ -214,16 +215,16 @@ - - - + + + - - + + :/align_top.png:/align_top.png - + 32 32 @@ -231,16 +232,16 @@ - - - + + + - - + + :/align_vcenter.png:/align_vcenter.png - + 32 32 @@ -248,12 +249,12 @@ - - - + + + Qt::Horizontal - + 243 34 @@ -261,56 +262,56 @@ - - - + + + none - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + top - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + center - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + bottom - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + - - + + :/align_bottom.png:/align_bottom.png - + 32 32 @@ -318,15 +319,15 @@ - - - + + + Qt::Horizontal - + QSizePolicy::Fixed - + 20 20 @@ -334,15 +335,15 @@ - - - + + + Qt::Horizontal - + QSizePolicy::Fixed - + 20 20 @@ -350,15 +351,15 @@ - - - + + + Qt::Horizontal - + QSizePolicy::Fixed - + 20 20 @@ -370,21 +371,21 @@ - - + + Layers for alignment of instances - - - - + + + + Use visible layers only - - - + + + Use all layers @@ -393,11 +394,11 @@ - - + + Qt::Vertical - + 488 16 @@ -406,11 +407,11 @@ - - + + Qt::Horizontal - + QDialogButtonBox::Cancel|QDialogButtonBox::Ok @@ -431,7 +432,7 @@ buttonBox - + @@ -440,11 +441,11 @@ AlignOptionsDialog accept() - + 248 254 - + 157 274 @@ -456,11 +457,11 @@ AlignOptionsDialog reject() - + 316 260 - + 286 274 diff --git a/src/edt/edt/DistributeOptionsDialog.ui b/src/edt/edt/DistributeOptionsDialog.ui new file mode 100644 index 000000000..0d2bf52e7 --- /dev/null +++ b/src/edt/edt/DistributeOptionsDialog.ui @@ -0,0 +1,638 @@ + + + DistributeOptionsDialog + + + + 0 + 0 + 716 + 574 + + + + Distribution Options + + + + + + Horizontal distribution + + + true + + + + + + Alignment + + + + + + + right + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + center + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 243 + 20 + + + + + + + + + + + + :/align_left.png:/align_left.png + + + + 32 + 32 + + + + + + + + + + + + :/align_right.png:/align_right.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + + + + + + + + + :/align_hcenter.png:/align_hcenter.png + + + + 32 + 32 + + + + + + + + left + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Pitch + + + + + + + + 0 + 0 + + + + + + + + µm + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Space + + + + + + + + 0 + 0 + + + + + + + + µm + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + The pitch specifies the offset at which the objects are placed relative to each other. The space is the minimum distance between the objects. + + + true + + + + + + + + + + + 0 + 200 + + + + Vertical distribution + + + true + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + top + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + :/align_vcenter.png:/align_vcenter.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + + 243 + 34 + + + + + + + + Qt::Horizontal + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Pitch + + + + + + + + 0 + 0 + + + + + + + + µm + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Space + + + + + + + + 0 + 0 + + + + + + + + µm + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + center + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + bottom + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + :/align_bottom.png:/align_bottom.png + + + + 32 + 32 + + + + + + + + The pitch specifies the offset at which the objects are placed relative to each other. The space is the minimum distance between the objects. + + + true + + + + + + + + + + + :/align_top.png:/align_top.png + + + + 32 + 32 + + + + + + + + + + + Layers for distribution of instances + + + + + + Use visible layers only + + + + + + + Use all layers + + + + + + + + + + Qt::Vertical + + + + 488 + 16 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + all_layers_rb + visible_layers_rb + h_left_rb + h_center_rb + h_right_rb + v_top_rb + v_center_rb + v_bottom_rb + buttonBox + + + + + + + buttonBox + accepted() + DistributeOptionsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + DistributeOptionsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/edt/edt/edt.pro b/src/edt/edt/edt.pro index f72811940..b57f0144e 100644 --- a/src/edt/edt/edt.pro +++ b/src/edt/edt/edt.pro @@ -42,7 +42,8 @@ FORMS = \ PolygonPropertiesPage.ui \ RoundCornerOptionsDialog.ui \ TextPropertiesPage.ui \ - PCellParametersDialog.ui + PCellParametersDialog.ui \ + DistributeOptionsDialog.ui SOURCES = \ edtConfig.cc \ diff --git a/src/edt/edt/edtDialogs.cc b/src/edt/edt/edtDialogs.cc index 0fbc02113..63ac14fdc 100644 --- a/src/edt/edt/edtDialogs.cc +++ b/src/edt/edt/edtDialogs.cc @@ -326,6 +326,93 @@ AlignOptionsDialog::exec_dialog (lay::LayoutView * /*view*/, int &hmode, int &vm } } +// -------------------------------------------------------------------------------- +// DistributeOptionsDialog implementation + +DistributeOptionsDialog::DistributeOptionsDialog (QWidget *parent) + : QDialog (parent) +{ + setObjectName (QString::fromUtf8 ("change_layer_options_dialog")); + + Ui::DistributeOptionsDialog::setupUi (this); +} + +DistributeOptionsDialog::~DistributeOptionsDialog () +{ + // .. nothing yet .. +} + +bool +DistributeOptionsDialog::exec_dialog (lay::LayoutView *view, bool &hdistribute, int &hmode, double &hpitch, double &hspace, bool &vdistribute, int &vmode, double &vpitch, double &vspace, bool &visible_layers) +{ + QRadioButton *hmode_buttons [] = { (QRadioButton *) 0, this->h_left_rb, this->h_center_rb, this->h_right_rb }; + QRadioButton *vmode_buttons [] = { (QRadioButton *) 0, this->v_top_rb, this->v_center_rb, this->v_bottom_rb }; + QRadioButton *layers_buttons [] = { this->all_layers_rb, this->visible_layers_rb }; + + this->h_distribute->setChecked (hdistribute); + for (int i = 1; i < 4; ++i) { + hmode_buttons [i]->setChecked (hmode == i); + } + + this->h_space->setText (tl::to_qstring (tl::micron_to_string (hspace))); + this->h_pitch->setText (tl::to_qstring (tl::micron_to_string (hpitch))); + + this->v_distribute->setChecked (vdistribute); + for (int i = 1; i < 4; ++i) { + vmode_buttons [i]->setChecked (vmode == i); + } + + this->v_space->setText (tl::to_qstring (tl::micron_to_string (vspace))); + this->v_pitch->setText (tl::to_qstring (tl::micron_to_string (vpitch))); + + for (int i = 0; i < 2; ++i) { + layers_buttons [i]->setChecked (int (visible_layers) == i); + } + + if (QDialog::exec ()) { + + hdistribute = this->h_distribute->isChecked (); + hmode = -1; + for (int i = 1; i < 4; ++i) { + if (hmode_buttons [i]->isChecked ()) { + hmode = i; + } + } + + hspace = 0.0; + tl::from_string (tl::to_string (this->h_space->text ()), hspace); + + hpitch = 0.0; + tl::from_string (tl::to_string (this->h_pitch->text ()), hpitch); + + vdistribute = this->v_distribute->isChecked (); + vmode = -1; + for (int i = 1; i < 4; ++i) { + if (vmode_buttons [i]->isChecked ()) { + vmode = i; + } + } + + vspace = 0.0; + tl::from_string (tl::to_string (this->v_space->text ()), vspace); + + vpitch = 0.0; + tl::from_string (tl::to_string (this->v_pitch->text ()), vpitch); + + visible_layers = false; + for (int i = 0; i < 2; ++i) { + if (layers_buttons [i]->isChecked ()) { + visible_layers = (i != 0); + } + } + + return true; + + } else { + return false; + } +} + // -------------------------------------------------------------------------------- // MakeCellOptionsDialog implementation diff --git a/src/edt/edt/edtDialogs.h b/src/edt/edt/edtDialogs.h index 4beddf680..15fc2fdf3 100644 --- a/src/edt/edt/edtDialogs.h +++ b/src/edt/edt/edtDialogs.h @@ -37,6 +37,7 @@ #include "ui_InstantiationForm.h" #include "ui_ChangeLayerOptionsDialog.h" #include "ui_AlignOptionsDialog.h" +#include "ui_DistributeOptionsDialog.h" #include "ui_CopyModeDialog.h" #include "ui_MakeCellOptionsDialog.h" #include "ui_MakeArrayOptionsDialog.h" @@ -127,6 +128,22 @@ public: bool exec_dialog (lay::LayoutView *view, int &hmode, int &vmode, bool &visible_layers); }; +/** + * @brief Distribute function options dialog + */ +class DistributeOptionsDialog + : public QDialog, + public Ui::DistributeOptionsDialog +{ +Q_OBJECT + +public: + DistributeOptionsDialog (QWidget *parent); + virtual ~DistributeOptionsDialog (); + + bool exec_dialog (lay::LayoutView *view, bool &hdistribute, int &hmode, double &hpitch, double &hspace, bool &vdistribute, int &vmode, double &vpitch, double &vspace, bool &visible_layers); +}; + /** * @brief Options dialog for the "make cell" function */ diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index 1c6d582ea..89df404ac 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -64,12 +64,18 @@ MainService::MainService (db::Manager *manager, lay::LayoutView *view, lay::Disp m_flatten_insts_levels (std::numeric_limits::max ()), m_flatten_prune (false), m_align_hmode (0), m_align_vmode (0), m_align_visible_layers (false), + m_hdistribute (true), + m_distribute_hmode (1), m_distribute_hpitch (0.0), m_distribute_hspace (0.0), + m_vdistribute (true), + m_distribute_vmode (1), m_distribute_vpitch (0.0), m_distribute_vspace (0.0), + m_distribute_visible_layers (false), m_origin_mode_x (-1), m_origin_mode_y (-1), m_origin_visible_layers_for_bbox (false), m_array_a (0.0, 1.0), m_array_b (1.0, 0.0), m_array_na (1), m_array_nb (1), m_router (0.0), m_rinner (0.0), m_npoints (64), m_undo_before_apply (true), mp_round_corners_dialog (0), mp_align_options_dialog (0), + mp_distribute_options_dialog (0), mp_flatten_inst_options_dialog (0), mp_make_cell_options_dialog (0), mp_make_array_options_dialog (0) @@ -100,6 +106,15 @@ MainService::align_options_dialog () return mp_align_options_dialog; } +edt::DistributeOptionsDialog * +MainService::distribute_options_dialog () +{ + if (! mp_distribute_options_dialog) { + mp_distribute_options_dialog = new edt::DistributeOptionsDialog (view ()); + } + return mp_distribute_options_dialog; +} + lay::FlattenInstOptionsDialog * MainService::flatten_inst_options_dialog () { @@ -138,6 +153,8 @@ MainService::menu_activated (const std::string &symbol) cm_edit_options (); } else if (symbol == "edt::sel_align") { cm_align (); + } else if (symbol == "edt::sel_distribute") { + cm_distribute (); } else if (symbol == "edt::sel_tap") { cm_tap (); } else if (symbol == "edt::sel_round_corners") { @@ -1841,7 +1858,99 @@ MainService::cm_align () } } -void +void +MainService::cm_distribute () +{ + tl_assert (view ()->is_editable ()); + check_no_guiding_shapes (); + + std::vector edt_services = view ()->get_plugins (); + + if (! distribute_options_dialog ()->exec_dialog (view (), m_hdistribute, m_distribute_hmode, m_distribute_hpitch, m_distribute_hspace, + m_vdistribute, m_distribute_vmode, m_distribute_vpitch, m_distribute_vspace, + m_align_visible_layers)) { + return; + } + + db::DBox prim_box; + bool has_secondary = false; + + // get (common) bbox index of the primary selection + for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { + for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) { + + if (s->seq () == 0) { + + const db::Layout &layout = view ()->cellview (s->cv_index ())->layout (); + db::CplxTrans tr = db::CplxTrans (layout.dbu ()) * s->trans (); + + if (! s->is_cell_inst ()) { + prim_box += tr * s->shape ().bbox (); + } else { + prim_box += inst_bbox (tr, view (), s->cv_index (), s->back (), m_distribute_visible_layers); + } + + } else { + has_secondary = true; + } + + } + } + + if (! prim_box.empty ()) { + + view ()->cancel_edits (); + manager ()->transaction (tl::to_string (QObject::tr ("Alignment"))); + + + + // do the alignment + for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { + + // create a transformation vector that describes each shape's transformation + std::vector tv; + tv.reserve ((*es)->selection ().size ()); + + for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) { + + db::DVector v; + +// @@@ + if (s->seq () > 0 || !has_secondary) { + + db::Layout &layout = view ()->cellview (s->cv_index ())->layout (); + db::CplxTrans tr = db::CplxTrans (layout.dbu ()) * s->trans (); + + if (! s->is_cell_inst ()) { + + db::DBox box = tr * s->shape ().bbox (); + v = compute_alignment_vector (prim_box, box, m_distribute_hmode, m_distribute_vmode); + + } else { + + db::DBox box = inst_bbox (tr, view (), s->cv_index (), s->back (), m_distribute_visible_layers); + v = compute_alignment_vector (prim_box, box, m_distribute_hmode, m_distribute_vmode); + + } + + } +// @@@ + + tv.push_back (db::DCplxTrans (db::DTrans (v))); + + } + + // use the "transform" method to transform the shapes and instances (with individual transformations) + (*es)->transform (db::DCplxTrans () /*dummy*/, &tv); + + } + + manager ()->commit (); + + } +} + +void MainService::cm_make_array () { size_t n = 0; @@ -2010,129 +2119,6 @@ MainService::cm_tap () } } - -/** - * @brief An iterator for the selected objects of all edt services in a layout view - */ -class EDT_PUBLIC SelectionIterator -{ -public: - typedef lay::ObjectInstPath value_type; - typedef const lay::ObjectInstPath &reference; - typedef const lay::ObjectInstPath *pointer; - - /** - * @brief Creates a new iterator iterating over all selected edt objects from the given view - * - * If "including_transient" is true, the transient selection will be used as fallback. - */ - SelectionIterator (lay::LayoutView *view, bool including_transient = true) - : m_transient_mode (false) - { - mp_edt_services = view->get_plugins (); - - m_current_service = mp_edt_services.begin (); - if (m_current_service != mp_edt_services.end ()) { - m_current_object = (*m_current_service)->selection ().begin (); - } - - next (); - - if (at_end () && including_transient) { - - m_transient_mode = true; - - m_current_service = mp_edt_services.begin (); - if (m_current_service != mp_edt_services.end ()) { - m_current_object = (*m_current_service)->transient_selection ().begin (); - } - - next (); - - } - } - - /** - * @brief Returns a value indicating whether the transient selection is taken - */ - bool is_transient () const - { - return m_transient_mode; - } - - /** - * @brief Increments the iterator - */ - void operator++ () - { - inc (); - next (); - } - - /** - * @brief Dereferencing - */ - const lay::ObjectInstPath &operator* () const - { - tl_assert (! at_end ()); - return *m_current_object; - } - - /** - * @brief Arrow operator - */ - const lay::ObjectInstPath *operator-> () const - { - return & operator* (); - } - - /** - * @brief Returns a value indicating whether the iterator has finished - */ - bool at_end () const - { - return m_current_service == mp_edt_services.end (); - } - -private: - void inc () - { - tl_assert (! at_end ()); - ++m_current_object; - } - - void next () - { - if (at_end ()) { - return; - } - - const edt::Service::objects *sel = m_transient_mode ? &(*m_current_service)->transient_selection () : &(*m_current_service)->selection (); - - while (m_current_object == sel->end ()) { - - ++m_current_service; - - if (m_current_service != mp_edt_services.end ()) { - - sel = m_transient_mode ? &(*m_current_service)->transient_selection () : &(*m_current_service)->selection (); - m_current_object = sel->begin (); - - } else { - break; - } - - } - } - -private: - std::vector mp_edt_services; - std::vector::const_iterator m_current_service; - edt::Service::obj_iterator m_current_object; - bool m_transient_mode; -}; - - void MainService::cm_change_layer () { diff --git a/src/edt/edt/edtMainService.h b/src/edt/edt/edtMainService.h index 743d8a9f4..76a733ee5 100644 --- a/src/edt/edt/edtMainService.h +++ b/src/edt/edt/edtMainService.h @@ -51,6 +51,7 @@ class RoundCornerOptionsDialog; class MakeCellOptionsDialog; class MakeArrayOptionsDialog; class AlignOptionsDialog; +class DistributeOptionsDialog; // ------------------------------------------------------------- @@ -153,6 +154,11 @@ public: */ void cm_align (); + /** + * @brief Distribute the selected shapes and instances + */ + void cm_distribute (); + /** * @brief Flatten instances */ @@ -205,6 +211,13 @@ private: int m_align_hmode; int m_align_vmode; bool m_align_visible_layers; + bool m_hdistribute; + int m_distribute_hmode; + double m_distribute_hpitch, m_distribute_hspace; + bool m_vdistribute; + int m_distribute_vmode; + double m_distribute_vpitch, m_distribute_vspace; + bool m_distribute_visible_layers; std::string m_make_cell_name; int m_origin_mode_x, m_origin_mode_y; bool m_origin_visible_layers_for_bbox; @@ -215,6 +228,7 @@ private: bool m_undo_before_apply; edt::RoundCornerOptionsDialog *mp_round_corners_dialog; edt::AlignOptionsDialog *mp_align_options_dialog; + edt::DistributeOptionsDialog *mp_distribute_options_dialog; lay::FlattenInstOptionsDialog *mp_flatten_inst_options_dialog; edt::MakeCellOptionsDialog *mp_make_cell_options_dialog; edt::MakeArrayOptionsDialog *mp_make_array_options_dialog; @@ -223,6 +237,7 @@ private: void check_no_guiding_shapes (); edt::RoundCornerOptionsDialog *round_corners_dialog (); edt::AlignOptionsDialog *align_options_dialog (); + edt::DistributeOptionsDialog *distribute_options_dialog (); lay::FlattenInstOptionsDialog *flatten_inst_options_dialog (); edt::MakeCellOptionsDialog *make_cell_options_dialog (); edt::MakeArrayOptionsDialog *make_array_options_dialog (); diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 53e7f2753..e7bd816c3 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -225,6 +225,7 @@ public: menu_entries.push_back (lay::menu_item ("edt::sel_change_layer", "change_layer:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Change Layer")))); menu_entries.push_back (lay::menu_item ("edt::sel_tap", "tap:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Tap")) + "(T)")); menu_entries.push_back (lay::menu_item ("edt::sel_align", "align:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Align")))); + menu_entries.push_back (lay::menu_item ("edt::sel_distribute", "distribute:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Distribute")))); menu_entries.push_back (lay::menu_item ("edt::sel_round_corners", "round_corners:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Round Corners")))); menu_entries.push_back (lay::menu_item ("edt::sel_size", "size:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Size Shapes")))); menu_entries.push_back (lay::menu_item ("edt::sel_union", "union:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Merge Shapes")))); diff --git a/src/edt/edt/edtUtils.cc b/src/edt/edt/edtUtils.cc index d99f85ab5..7cc997bf7 100644 --- a/src/edt/edt/edtUtils.cc +++ b/src/edt/edt/edtUtils.cc @@ -26,12 +26,82 @@ #include "dbLibrary.h" #include "edtUtils.h" +#include "edtService.h" + #include "layCellView.h" #include "layLayoutView.h" +#include "layEditable.h" #include "tlException.h" namespace edt { +// ------------------------------------------------------------- +// SelectionIterator implementation + +SelectionIterator::SelectionIterator (lay::LayoutView *view, bool including_transient) + : m_transient_mode (false) +{ + mp_edt_services = view->get_plugins (); + + m_current_service = mp_edt_services.begin (); + if (m_current_service != mp_edt_services.end ()) { + m_current_object = (*m_current_service)->selection ().begin (); + } + + next (); + + if (at_end () && including_transient) { + + m_transient_mode = true; + + m_current_service = mp_edt_services.begin (); + if (m_current_service != mp_edt_services.end ()) { + m_current_object = (*m_current_service)->transient_selection ().begin (); + } + + next (); + + } +} + +bool +SelectionIterator::at_end () const +{ + return m_current_service == mp_edt_services.end (); +} + +void +SelectionIterator::inc () +{ + tl_assert (! at_end ()); + ++m_current_object; +} + +void +SelectionIterator::next () +{ + if (at_end ()) { + return; + } + + const edt::Service::objects *sel = m_transient_mode ? &(*m_current_service)->transient_selection () : &(*m_current_service)->selection (); + + while (m_current_object == sel->end ()) { + + ++m_current_service; + + if (m_current_service != mp_edt_services.end ()) { + + sel = m_transient_mode ? &(*m_current_service)->transient_selection () : &(*m_current_service)->selection (); + m_current_object = sel->begin (); + + } else { + break; + } + + } +} + // ------------------------------------------------------------- // TransformationsVariants implementation // for a lay::LayoutView diff --git a/src/edt/edt/edtUtils.h b/src/edt/edt/edtUtils.h index db41dbd0c..a4a512e7a 100644 --- a/src/edt/edt/edtUtils.h +++ b/src/edt/edt/edtUtils.h @@ -27,9 +27,12 @@ #include #include #include +#include #include +#include "layObjectInstPath.h" + #include "dbInstElement.h" #include "dbClipboardData.h" #include "dbClipboard.h" @@ -42,6 +45,8 @@ namespace lay namespace edt { +class Service; + /** * @brief Fetch PCell parameters from a cell and merge the guiding shapes into them * @@ -79,6 +84,73 @@ private: std::map < std::pair, std::vector > m_per_cv_and_layer_tv; }; +/** + * @brief An iterator for the selected objects of all edt services in a layout view + */ +class SelectionIterator +{ +public: + typedef lay::ObjectInstPath value_type; + typedef const lay::ObjectInstPath &reference; + typedef const lay::ObjectInstPath *pointer; + + /** + * @brief Creates a new iterator iterating over all selected edt objects from the given view + * + * If "including_transient" is true, the transient selection will be used as fallback. + */ + SelectionIterator (lay::LayoutView *view, bool including_transient = true); + + /** + * @brief Returns a value indicating whether the transient selection is taken + */ + bool is_transient () const + { + return m_transient_mode; + } + + /** + * @brief Increments the iterator + */ + void operator++ () + { + inc (); + next (); + } + + /** + * @brief Dereferencing + */ + const lay::ObjectInstPath &operator* () const + { + tl_assert (! at_end ()); + return *m_current_object; + } + + /** + * @brief Arrow operator + */ + const lay::ObjectInstPath *operator-> () const + { + return & operator* (); + } + + /** + * @brief Returns a value indicating whether the iterator has finished + */ + bool at_end () const; + +private: + void inc (); + void next (); + +private: + std::vector mp_edt_services; + std::vector::const_iterator m_current_service; + std::set::const_iterator m_current_object; + bool m_transient_mode; +}; + } // namespace edt #endif