diff --git a/src/ant/ant/antPropertiesPage.cc b/src/ant/ant/antPropertiesPage.cc index 0df2c3967..d98d00122 100644 --- a/src/ant/ant/antPropertiesPage.cc +++ b/src/ant/ant/antPropertiesPage.cc @@ -50,6 +50,38 @@ PropertiesPage::PropertiesPage (ant::Service *rulers, db::Manager *manager, QWid p2_to_layout->setEnabled (! readonly()); both_to_layout->setEnabled (! readonly()); + if (! readonly ()) { + + connect (fmt_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (fmt_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (fmt_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (x1, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (x2, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (y1, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (y2, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + + connect (style_cb, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (outline_cb, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (main_position, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (main_xalign, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (main_yalign, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (xlabel_xalign, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (xlabel_yalign, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (ylabel_xalign, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (ylabel_yalign, SIGNAL (activated (int)), this, SIGNAL (edited ())); + + } else { + + fmt_le->setReadOnly (true); + fmt_x_le->setReadOnly (true); + fmt_y_le->setReadOnly (true); + x1->setReadOnly (true); + x2->setReadOnly (true); + y1->setReadOnly (true); + y2->setReadOnly (true); + + } + lay::activate_help_links (help_label); mp_rulers->clear_highlights (); @@ -91,7 +123,53 @@ PropertiesPage::swap_points_clicked () y2->setText (ty2); db::Transaction t (manager (), tl::to_string (QObject::tr ("Swap ruler points"))); - apply (); + emit edited (); +} + +void +PropertiesPage::get_points (db::DPoint &p1, db::DPoint &p2) +{ + double dx1 = 0.0, dy1 = 0.0, dx2 = 0.0, dy2 = 0.0; + bool has_error = false; + + try { + tl::from_string (tl::to_string (x1->text ()), dx1); + lay::indicate_error (x1, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (x1, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (x2->text ()), dx2); + lay::indicate_error (x2, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (x2, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (y1->text ()), dy1); + lay::indicate_error (y1, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (y1, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (y2->text ()), dy2); + lay::indicate_error (y2, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (y2, &ex); + has_error = true; + } + + if (has_error) { + throw tl::Exception (tl::to_string (tr ("At least one value is invalid - see highlighted entry fields"))); + } + + p1 = db::DPoint (dx1, dy1); + p2 = db::DPoint (dx2, dy2); } void @@ -104,13 +182,8 @@ PropertiesPage::snap_to_layout_clicked () ant::Service *service = dynamic_cast (editable ()); tl_assert (service != 0); - double dx1 = 0.0, dy1 = 0.0, dx2 = 0.0, dy2 = 0.0; - tl::from_string (tl::to_string (x1->text ()), dx1); - tl::from_string (tl::to_string (x2->text ()), dx2); - tl::from_string (tl::to_string (y1->text ()), dy1); - tl::from_string (tl::to_string (y2->text ()), dy2); - - db::DPoint p1 (dx1, dy1), p2 (dx2, dy2); + db::DPoint p1, p2; + get_points (p1, p2); ant::Object r = current (); @@ -137,11 +210,11 @@ PropertiesPage::snap_to_layout_clicked () while (snap_range < max_range) { - std::pair pp = lay::obj_snap (service->view (), snap_p1 ? p2 : p1, snap_p1 ? p1 : p2, g, ac, snap_range); - if (pp.first) { + lay::PointSnapToObjectResult pp = lay::obj_snap (service->view (), snap_p1 ? p2 : p1, snap_p1 ? p1 : p2, g, ac, snap_range); + if (pp.object_snap != lay::PointSnapToObjectResult::NoObject) { - QString xs = tl::to_qstring (tl::micron_to_string (pp.second.x ())); - QString ys = tl::to_qstring (tl::micron_to_string (pp.second.y ())); + QString xs = tl::to_qstring (tl::micron_to_string (pp.snapped_point.x ())); + QString ys = tl::to_qstring (tl::micron_to_string (pp.snapped_point.y ())); if (sender () == p1_to_layout) { x1->setText (xs); @@ -152,7 +225,7 @@ PropertiesPage::snap_to_layout_clicked () } db::Transaction t (manager (), tl::to_string (snap_p1 ? QObject::tr ("Snap first ruler point") : QObject::tr ("Snap second ruler point"))); - apply (); + emit edited (); break; @@ -168,16 +241,16 @@ PropertiesPage::snap_to_layout_clicked () double snap_range = service->widget ()->mouse_event_trans ().inverted ().ctrans (service->snap_range ()); snap_range *= 0.5; - std::pair ee = lay::obj_snap2 (service->view (), p1, p2, g, ac, snap_range, snap_range * 1000.0); - if (ee.first) { + lay::TwoPointSnapToObjectResult ee = lay::obj_snap2 (service->view (), p1, p2, g, ac, snap_range, snap_range * 1000.0); + if (ee.any) { - x1->setText (tl::to_qstring (tl::micron_to_string (ee.second.p1 ().x ()))); - y1->setText (tl::to_qstring (tl::micron_to_string (ee.second.p1 ().y ()))); - x2->setText (tl::to_qstring (tl::micron_to_string (ee.second.p2 ().x ()))); - y2->setText (tl::to_qstring (tl::micron_to_string (ee.second.p2 ().y ()))); + x1->setText (tl::to_qstring (tl::micron_to_string (ee.first.x ()))); + y1->setText (tl::to_qstring (tl::micron_to_string (ee.first.y ()))); + x2->setText (tl::to_qstring (tl::micron_to_string (ee.second.x ()))); + y2->setText (tl::to_qstring (tl::micron_to_string (ee.second.y ()))); db::Transaction t (manager (), tl::to_string (QObject::tr ("Snap both ruler points"))); - apply (); + emit edited (); } @@ -268,22 +341,9 @@ PropertiesPage::readonly () void PropertiesPage::apply () { - double dx1 = current ().p1 ().x (), dy1 = current ().p1 ().y (); - double dx2 = current ().p2 ().x (), dy2 = current ().p2 ().y (); - // only adjust the values if the text has changed - if (tl::to_qstring (tl::micron_to_string (dx1)) != x1->text ()) { - tl::from_string (tl::to_string (x1->text ()), dx1); - } - if (tl::to_qstring (tl::micron_to_string (dx2)) != x2->text ()) { - tl::from_string (tl::to_string (x2->text ()), dx2); - } - if (tl::to_qstring (tl::micron_to_string (dy1)) != y1->text ()) { - tl::from_string (tl::to_string (y1->text ()), dy1); - } - if (tl::to_qstring (tl::micron_to_string (dy2)) != y2->text ()) { - tl::from_string (tl::to_string (y2->text ()), dy2); - } + db::DPoint p1, p2; + get_points (p1, p2); std::string fmt = tl::to_string (fmt_le->text ()); std::string fmt_x = tl::to_string (fmt_x_le->text ()); @@ -291,7 +351,7 @@ PropertiesPage::apply () Object::style_type style = Object::style_type (style_cb->currentIndex ()); Object::outline_type outline = Object::outline_type (outline_cb->currentIndex ()); - ant::Object ruler (db::DPoint (dx1, dy1), db::DPoint (dx2, dy2), current ().id (), fmt_x, fmt_y, fmt, style, outline, current ().snap (), current ().angle_constraint ()); + ant::Object ruler (p1, p2, current ().id (), fmt_x, fmt_y, fmt, style, outline, current ().snap (), current ().angle_constraint ()); ruler.set_main_position (Object::position_type (main_position->currentIndex ())); ruler.set_main_xalign (Object::alignment_type (main_xalign->currentIndex ())); diff --git a/src/ant/ant/antPropertiesPage.h b/src/ant/ant/antPropertiesPage.h index 5cae57e5f..8a23b1801 100644 --- a/src/ant/ant/antPropertiesPage.h +++ b/src/ant/ant/antPropertiesPage.h @@ -64,6 +64,7 @@ private: bool m_enable_cb_callback; const ant::Object ¤t () const; + void get_points(db::DPoint &p1, db::DPoint &p2); }; } diff --git a/src/ant/ant/antService.cc b/src/ant/ant/antService.cc index fd0a50dc0..62f1dcc36 100644 --- a/src/ant/ant/antService.cc +++ b/src/ant/ant/antService.cc @@ -706,9 +706,7 @@ View::render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas) // ant::Service implementation Service::Service (db::Manager *manager, lay::LayoutView *view) - : lay::ViewService (view->view_object_widget ()), - lay::Editable (view), - lay::Plugin (view), + : lay::EditorServiceBase (view), lay::Drawing (1/*number of planes*/, view->drawings ()), db::Object (manager), m_halo (true), @@ -801,7 +799,7 @@ Service::configure (const std::string &name, const std::string &value) m_current_template = n; } else { - taken = false; + lay::EditorServiceBase::configure (name, value); } return taken; @@ -1434,14 +1432,14 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio double snap_range = widget ()->mouse_event_trans ().inverted ().ctrans (m_snap_range); snap_range *= 0.5; - std::pair ee = lay::obj_snap2 (mp_view, p, g, ac, snap_range, snap_range * 1000.0); - if (ee.first) { + lay::TwoPointSnapToObjectResult ee = lay::obj_snap2 (mp_view, p, g, ac, snap_range, snap_range * 1000.0); + if (ee.any) { // begin the transaction tl_assert (! manager ()->transacting ()); manager ()->transaction (tl::to_string (QObject::tr ("Create ruler"))); - m_current = ant::Object (ee.second.p1 (), ee.second.p2 (), 0, tpl); + m_current = ant::Object (ee.first, ee.second, 0, tpl); show_message (); insert_ruler (m_current, true); @@ -1505,17 +1503,31 @@ Service::create_measure_ruler (const db::DPoint &pt, lay::angle_constraint_type ant::Template tpl; - std::pair ee = lay::obj_snap2 (mp_view, pt, db::DVector (), ac, snap_range, snap_range * 1000.0); - if (ee.first) { - return ant::Object (ee.second.p1 (), ee.second.p2 (), 0, tpl); + lay::TwoPointSnapToObjectResult ee = lay::obj_snap2 (mp_view, pt, db::DVector (), ac, snap_range, snap_range * 1000.0); + if (ee.any) { + return ant::Object (ee.first, ee.second, 0, tpl); } else { return ant::Object (pt, pt, 0, tpl); } } -bool +bool Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) { + if (prio) { + + lay::PointSnapToObjectResult snap_details; + if (m_drawing) { + snap_details = snap2_details (m_p1, p, mp_active_ruler->ruler (), ac_from_buttons (buttons)); + } else { + const ant::Template &tpl = current_template (); + snap_details = snap1_details (p, m_obj_snap && tpl.snap ()); + } + + mouse_cursor_from_snap_details (snap_details); + + } + if (m_drawing && prio) { set_cursor (lay::Cursor::cross); @@ -1532,12 +1544,14 @@ Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) void Service::deactivated () { + lay::EditorServiceBase::deactivated (); + drag_cancel (); clear_transient_selection (); } -std::pair -Service::snap1 (const db::DPoint &p, bool obj_snap) +lay::PointSnapToObjectResult +Service::snap1_details (const db::DPoint &p, bool obj_snap) { db::DVector g; if (m_grid_snap) { @@ -1548,9 +1562,16 @@ Service::snap1 (const db::DPoint &p, bool obj_snap) return lay::obj_snap (obj_snap ? mp_view : 0, p, g, snap_range); } +std::pair +Service::snap1 (const db::DPoint &p, bool obj_snap) +{ + lay::PointSnapToObjectResult res = snap1_details (p, obj_snap); + return std::make_pair (res.object_snap != lay::PointSnapToObjectResult::NoObject, res.snapped_point); +} -std::pair -Service::snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac) + +lay::PointSnapToObjectResult +Service::snap2_details (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac) { db::DVector g; if (m_grid_snap) { @@ -1563,6 +1584,13 @@ Service::snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *o return lay::obj_snap (m_obj_snap && obj->snap () ? mp_view : 0, p1, p2, g, snap_mode, snap_range); } +std::pair +Service::snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac) +{ + lay::PointSnapToObjectResult res = snap2_details (p1, p2, obj, ac); + return std::make_pair (res.object_snap != lay::PointSnapToObjectResult::NoObject, res.snapped_point); +} + struct RulerIdComp { diff --git a/src/ant/ant/antService.h b/src/ant/ant/antService.h index ad7a0eaa8..0f6d3b05b 100644 --- a/src/ant/ant/antService.h +++ b/src/ant/ant/antService.h @@ -27,9 +27,7 @@ #include "antCommon.h" -#include "layViewObject.h" -#include "layEditable.h" -#include "layPlugin.h" +#include "layEditorServiceBase.h" #include "layDrawing.h" #include "laySnap.h" #include "layAnnotationShapes.h" @@ -180,9 +178,7 @@ private: // ------------------------------------------------------------- class ANT_PUBLIC Service - : public lay::ViewService, - public lay::Editable, - public lay::Plugin, + : public lay::EditorServiceBase, public lay::Drawing, public db::Object { @@ -529,8 +525,10 @@ private: unsigned int m_current_template; std::pair snap1 (const db::DPoint &p, bool obj_snap); + lay::PointSnapToObjectResult snap1_details (const db::DPoint &p, bool obj_snap); std::pair snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac); - + lay::PointSnapToObjectResult snap2_details (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac); + const ant::Template ¤t_template () const; void show_message (); diff --git a/src/edt/edt/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..74929ebe1 --- /dev/null +++ b/src/edt/edt/DistributeOptionsDialog.ui @@ -0,0 +1,715 @@ + + + DistributeOptionsDialog + + + + 0 + 0 + 689 + 574 + + + + Distribution Options + + + + + + Horizontal distribution + + + true + + + + + + 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 + + + + + + + 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 + + + + + + + + + + + + + + + 0 + 0 + + + + Vertical distribution + + + true + + + + + + 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 + + + + + + + + + + Horizonal alignment + + + + + + + + + + :/align_none.png:/align_none.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + :/align_left.png:/align_left.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + :/align_hcenter.png:/align_hcenter.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + :/align_right.png:/align_right.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + + 245 + 20 + + + + + + + + none + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + left + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + center + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + right + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Vertical alignment + + + + + + + + + + :/align_none.png:/align_none.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + :/align_top.png:/align_top.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + :/align_vcenter.png:/align_vcenter.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + :/align_bottom.png:/align_bottom.png + + + + 32 + 32 + + + + + + + + Qt::Horizontal + + + + 245 + 20 + + + + + + + + none + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + top + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + center + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + bottom + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + For the computation of cell instance bounding boxes ... + + + + + + Use visible layers only + + + + + + + Use all layers + + + + + + + + + + Qt::Vertical + + + + 488 + 16 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + all_layers_rb + visible_layers_rb + buttonBox + + + + + + + buttonBox + accepted() + DistributeOptionsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + DistributeOptionsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/edt/edt/EditorOptionsDialog.ui b/src/edt/edt/EditorOptionsDialog.ui deleted file mode 100644 index e34cea275..000000000 --- a/src/edt/edt/EditorOptionsDialog.ui +++ /dev/null @@ -1,139 +0,0 @@ - - EditorOptionsDialog - - - - 0 - 0 - 642 - 572 - - - - Object Editor Options - - - - 9 - - - 6 - - - - - 0 - - - - Tab 2 - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 8 - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 6 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Ok - - - - - - - Apply - - - - - - - Cancel - - - - - - - - - - - - cancel_pb - clicked() - EditorOptionsDialog - reject() - - - 506 - 388 - - - 276 - 205 - - - - - ok_pb - clicked() - EditorOptionsDialog - accept() - - - 344 - 388 - - - 276 - 205 - - - - - diff --git a/src/edt/edt/EditorOptionsGeneric.ui b/src/edt/edt/EditorOptionsGeneric.ui index 36c1aadd0..8de52d009 100644 --- a/src/edt/edt/EditorOptionsGeneric.ui +++ b/src/edt/edt/EditorOptionsGeneric.ui @@ -6,10 +6,16 @@ 0 0 - 500 - 417 + 400 + 446 + + + 0 + 0 + + Form @@ -18,387 +24,449 @@ 6 - 9 + 0 - 9 + 0 - 9 + 0 - 9 + 0 - - - Snapping + + + QFrame::NoFrame - - - 9 + + QFrame::Plain + + + true + + + + + 0 + 0 + 400 + 446 + - - 9 - - - 9 - - - 9 - - - 6 - - - - - Qt::Horizontal - - - - 148 - 16 - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 16 - 20 - - - - - - - - - 0 - 0 - - - - Enter the grid in micron. Can be anisotropic ("gx,gy") - - - - - - - Grid - - - - - - - Objects - - - - - - - - No grid + + + 2 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + Snapping - - - - Global grid + + + 4 + + + 4 + + + 4 + + + 4 + + + 6 + + + 2 + + + + + Grid + + + + + + + + 1 + 0 + + + + QComboBox::AdjustToContents + + + + No grid + + + + + Global grid + + + + + Other grid ... + + + + + + + + Snap to other objects + + + + + + + Objects + + + + + + + Qt::Horizontal + + + + 148 + 16 + + + + + + + + + 0 + 0 + + + + Enter the grid in micron. Can be anisotropic ("gx,gy") + + + + + + + + + + Angle Constraints - - - - Other grid ... + + + 4 + + + 4 + + + 4 + + + 4 + + + 6 + + + 2 + + + + + Movements + + + + + + + + 1 + 0 + + + + QComboBox::AdjustToContents + + + + Any Direction + + + + + Diagonal + + + + + Manhattan + + + + + + + + Connections + + + + + + + + 1 + 0 + + + + QComboBox::AdjustToContents + + + + Any Angle + + + + + Diagonal + + + + + Manhattan + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Hierarchical Features - - - - - - - Snap to other objects - - - - + + + 4 + + + 4 + + + 4 + + + 4 + + + 6 + + + 2 + + + + + Copy mode + + + + + + + Shallow select + + + + + + + + 1 + 0 + + + + QComboBox::AdjustToContents + + + + Shallow mode (instance only) + + + + + Deep mode (instance and cell) + + + + + Ask + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Select top level objects only + + + + + + + + + + Instance Display + + + + 2 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + Show shapes while moving (max. + + + + + + + + 0 + 0 + + + + + + + + shapes) + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + + + + Qt::Vertical + + + + 121 + 70 + + + + + + - - - - Angle Constraints - - - - 9 - - - 9 - - - 9 - - - 9 - - - 6 - - - - - Movements - - - - - - - Connections - - - - - - - - Any Angle - - - - - Diagonal - - - - - Manhattan - - - - - - - - - Any Direction - - - - - Diagonal - - - - - Manhattan - - - - - - - - Qt::Horizontal - - - - 257 - 41 - - - - - - - - - - - Hierarchical Features - - - - 9 - - - 9 - - - 9 - - - 9 - - - 6 - - - - - Copy mode - - - - - - - Shallow select - - - - - - - - 0 - 0 - - - - QComboBox::AdjustToContentsOnFirstShow - - - - Shallow mode (instance only) - - - - - Deep mode (instance and cell) - - - - - Ask - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - Select top level objects only - - - - - - - - - - Instance Display - - - - 6 - - - 9 - - - 9 - - - 9 - - - 9 - - - - - Show shapes when moving (max. - - - - - - - - 0 - 0 - - - - - - - - shapes) - - - - - - - Qt::Horizontal - - - - 20 - 20 - - - - - - - - - - - Qt::Vertical - - - - 121 - 51 - - - - + scrollArea grid_cb edit_grid_le snap_objects_cbx conn_angle_cb move_angle_cb hier_sel_cbx + hier_copy_mode_cbx show_shapes_cbx max_shapes_le diff --git a/src/edt/edt/EditorOptionsInst.ui b/src/edt/edt/EditorOptionsInst.ui index 3cd38120a..53002f9de 100644 --- a/src/edt/edt/EditorOptionsInst.ui +++ b/src/edt/edt/EditorOptionsInst.ui @@ -6,10 +6,16 @@ 0 0 - 680 - 574 + 358 + 496 + + + 0 + 0 + + Form @@ -18,163 +24,218 @@ 6 - 9 + 0 - 9 + 0 - 9 + 0 - 9 + 0 - + QFrame::NoFrame - QFrame::Raised + QFrame::Plain - - - 6 + + true + + + + + 0 + 0 + 358 + 496 + - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Cell - - - - - - - - 0 - 0 - - - - - - - - ... - - - - - - - Library - - - - - - - - 0 - 0 - - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 522 - 8 - - - - - - - - - 0 - 1 - - - - 0 - - - - Geometry - - - - 6 - + - 9 + 4 - 9 + 4 - 9 + 4 - 9 + 4 - + + 2 + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 6 + + + 2 + + + + + Library + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Cell + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + ... + + + + :/find.png:/find.png + + + true + + + + + + + + 0 + 0 + + + + false + + + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 4 + + + + + + + + Place origin of cell + + + + Rotation / Scaling - + - 9 + 4 - 9 + 4 - 9 + 4 - 9 + 4 - + 6 + + 2 + - + 0 0 @@ -194,14 +255,14 @@ - Scaling factor (magnification) + Scaling factor - + 0 0 @@ -229,10 +290,17 @@ + + + + (magnification) + + + - + @@ -246,33 +314,36 @@ true - + - 9 + 4 - 9 + 4 - 9 + 4 - 9 + 4 - + 6 + + 2 + - Column vector (x,y) + Column step - + 1 0 @@ -292,7 +363,7 @@ - + 1 0 @@ -312,7 +383,7 @@ - + 1 0 @@ -332,7 +403,7 @@ - + 1 0 @@ -352,14 +423,14 @@ - Row vector (x,y) + Row step - Rows/Columns + Dimension @@ -368,12 +439,15 @@ columns = + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + - + 1 0 @@ -385,74 +459,89 @@ rows = + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + - + 1 0 + + + 0 + 0 + + - + - Warning: although row and column vectors can be arbitrary combination, -some design systems only accept orthogonal (rectangular) arrays. + Warning: although row and column vectors can be arbitrary combination, some design systems only accept orthogonal (rectangular) arrays. true + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 4 + + + + - - + + Qt::Vertical 20 - 40 + 120 + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 4 - - - PCell - - - - - - Place origin of cell - - - - - - - Qt::Vertical - - - - 50 - 8 - - - - @@ -462,22 +551,8 @@ some design systems only accept orthogonal (rectangular) arrays.
layWidgets.h
- - cell_le - browse_pb - lib_cbx - param_tab_widget - scale_le - angle_le - mirror_cbx - rows_le - columns_le - row_x_le - row_y_le - column_x_le - column_y_le - place_origin_cb - - + + +
diff --git a/src/edt/edt/EditorOptionsInstPCellParam.ui b/src/edt/edt/EditorOptionsInstPCellParam.ui new file mode 100644 index 000000000..2e1e0178e --- /dev/null +++ b/src/edt/edt/EditorOptionsInstPCellParam.ui @@ -0,0 +1,39 @@ + + + EditorOptionsInstPCellParam + + + + 0 + 0 + 358 + 481 + + + + + 0 + 0 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + diff --git a/src/edt/edt/EditorOptionsPath.ui b/src/edt/edt/EditorOptionsPath.ui index 543354380..c50521d5a 100644 --- a/src/edt/edt/EditorOptionsPath.ui +++ b/src/edt/edt/EditorOptionsPath.ui @@ -1,7 +1,8 @@ - + + EditorOptionsPath - - + + 0 0 @@ -9,228 +10,264 @@ 289 - + Form - - - 9 - - + + 6 + + 0 + + + 0 + + + 0 + + + 0 + - - + + QFrame::NoFrame - - QFrame::Raised + + true - - - 0 + + + + 0 + 0 + 357 + 289 + - - 6 - - - - - Width - - - - - - - - 0 - 0 - 0 - 0 - - - - - - - - micron - - - - - - - Qt::Horizontal - - - - 21 - 20 - - - - - + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Width + + + + + + + + 0 + 0 + + + + + + + + µm + + + + + + + Qt::Horizontal + + + + 21 + 20 + + + + + + + + + + + + 0 + 0 + + + + Extensions + + + + 4 + + + 4 + + + 4 + + + 4 + + + 6 + + + 2 + + + + + end = + + + + + + + + 1 + 0 + + + + + + + + µm + + + + + + + + 1 + 0 + + + + + + + + + 0 + 0 + + + + + Flush + + + + + Square + + + + + Variable + + + + + Round + + + + + + + + Variable + + + + + + + µm + + + + + + + start = + + + + + + + Type + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Vertical + + + + 271 + 123 + + + + + + - - - - - 5 - 0 - 0 - 0 - - - - Extensions - - - - 9 - - - 6 - - - - - Qt::Horizontal - - - - 21 - 81 - - - - - - - - micron - - - - - - - micron - - - - - - - - 0 - 0 - 1 - 0 - - - - - - - - end = - - - - - - - start = - - - - - - - - 0 - 0 - 1 - 0 - - - - - - - - Variable - - - - - - - - 5 - 0 - 0 - 0 - - - - - Flush - - - - - Square - - - - - Variable - - - - - Round - - - - - - - - Type - - - - - - - - - - Qt::Vertical - - - - 271 - 63 - - - - - - width_le - type_cb - start_ext_le - end_ext_le - diff --git a/src/edt/edt/EditorOptionsText.ui b/src/edt/edt/EditorOptionsText.ui index 3008d9de8..1cc821fd4 100644 --- a/src/edt/edt/EditorOptionsText.ui +++ b/src/edt/edt/EditorOptionsText.ui @@ -1,7 +1,8 @@ - + + EditorOptionsText - - + + 0 0 @@ -9,225 +10,288 @@ 243 - + Form - - - 9 + + + 2 - - 6 + + 0 + + + 0 + + + 0 + + + 0 - - + + QFrame::NoFrame - - QFrame::Raised + + true - - - 9 + + + + 0 + 0 + 542 + 243 + - - 6 - - - - - Alignment - - - - - - - - 0 - 5 - 0 - 0 - - - - h = - - - - - - - Text size - - - - - - - - 7 - 0 - 0 - 0 - - - - - - - - - 0 - 5 - 0 - 0 - - - - Text - - - - - - - Hint: orientation, alignments and size cannot be saved to OASIS files -Enable a vector font and text scaling in the setup dialog -to show text objects scaled and rotated - - - - - - - - - - (Leave empty for default) - - - - - - - - 5 - 0 - 1 - 0 - - - - - (Default) + + + + + QFrame::NoFrame - - - - Top + + QFrame::Raised - - - - Center + + + 4 + + + 4 + + + 4 + + + 4 + + + 6 + + + 2 + + + + + + 0 + 0 + + + + v = + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 5 + + + + + + + + Text size + + + + + + + + 0 + 0 + + + + Text + + + + + + + + 1 + 0 + + + + + (Default) + + + + + Top + + + + + Center + + + + + Bottom + + + + + + + + + 1 + 0 + + + + + (Default) + + + + + Left + + + + + Center + + + + + Right + + + + + + + + + 0 + 0 + + + + h = + + + + + + + Alignment + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + (empty for default) + + + + + + + + + + Hint: orientation, alignments and size cannot be saved to OASIS files. Enable a vector font and text scaling in the setup dialog to show text objects scaled and rotated. + + + true + + + + + + + + + + Qt::Vertical - - - - Bottom + + + 20 + 77 + - - - - - - - - 0 - 5 - 0 - 0 - - - - v = - - - - - - - - 5 - 0 - 1 - 0 - - - - - (Default) - - - - - Left - - - - - Center - - - - - Right - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 5 - - - - - + + + + - - - - Qt::Vertical - - - - 20 - 40 - - - - - - text_le - halign_cbx - valign_cbx - size_le - diff --git a/src/edt/edt/PCellParametersDialog.ui b/src/edt/edt/PCellParametersDialog.ui deleted file mode 100644 index be3b83cde..000000000 --- a/src/edt/edt/PCellParametersDialog.ui +++ /dev/null @@ -1,73 +0,0 @@ - - - PCellParametersDialog - - - - 0 - 0 - 469 - 429 - - - - PCell Parameters - - - - - - - - - QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - edt::PCellParametersPage - QWidget -
edtPCellParametersPage.h
- 1 -
-
- - - - buttons - rejected() - PCellParametersDialog - reject() - - - 321 - 405 - - - 337 - 423 - - - - - buttons - accepted() - PCellParametersDialog - accept() - - - 427 - 405 - - - 443 - 425 - - - - -
diff --git a/src/edt/edt/edt.pro b/src/edt/edt/edt.pro index f72811940..5515e57c8 100644 --- a/src/edt/edt/edt.pro +++ b/src/edt/edt/edt.pro @@ -21,7 +21,8 @@ HEADERS = \ edtServiceImpl.h \ edtUtils.h \ edtCommon.h \ - edtPCellParametersDialog.h + edtDistribute.h \ + edtRecentConfigurationPage.h FORMS = \ AlignOptionsDialog.ui \ @@ -29,7 +30,6 @@ FORMS = \ CopyModeDialog.ui \ ChangeLayerOptionsDialog.ui \ EditablePathPropertiesPage.ui \ - EditorOptionsDialog.ui \ EditorOptionsGeneric.ui \ EditorOptionsInst.ui \ EditorOptionsPath.ui \ @@ -42,7 +42,8 @@ FORMS = \ PolygonPropertiesPage.ui \ RoundCornerOptionsDialog.ui \ TextPropertiesPage.ui \ - PCellParametersDialog.ui + DistributeOptionsDialog.ui \ + EditorOptionsInstPCellParam.ui SOURCES = \ edtConfig.cc \ @@ -59,7 +60,8 @@ SOURCES = \ edtServiceImpl.cc \ edtUtils.cc \ gsiDeclEdt.cc \ - edtPCellParametersDialog.cc + edtDistribute.cc \ + edtRecentConfigurationPage.cc INCLUDEPATH += $$TL_INC $$GSI_INC $$LAYBASIC_INC $$DB_INC DEPENDPATH += $$TL_INC $$GSI_INC $$LAYBASIC_INC $$DB_INC diff --git a/src/edt/edt/edtDialogs.cc b/src/edt/edt/edtDialogs.cc index 0fbc02113..21a0f6393 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 [] = { this->h_none_rb, this->h_left_rb, this->h_center_rb, this->h_right_rb }; + QRadioButton *vmode_buttons [] = { this->v_none_rb, this->v_top_rb, this->v_center_rb, this->v_bottom_rb }; + QRadioButton *layers_buttons [] = { this->all_layers_rb, this->visible_layers_rb }; + + this->h_distribute->setChecked (hdistribute); + 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/edtDistribute.cc b/src/edt/edt/edtDistribute.cc new file mode 100644 index 000000000..1b3631cb2 --- /dev/null +++ b/src/edt/edt/edtDistribute.cc @@ -0,0 +1,27 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "edtDistribute.h" + + +// .. nothing yet (all in header) .. + diff --git a/src/edt/edt/edtDistribute.h b/src/edt/edt/edtDistribute.h new file mode 100644 index 000000000..e7aad35eb --- /dev/null +++ b/src/edt/edt/edtDistribute.h @@ -0,0 +1,476 @@ +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_edtDistribute +#define HDR_edtDistribute + +#include "dbBox.h" + +#include "dbTypes.h" +#include "tlIntervalMap.h" + +namespace edt +{ + +/** + * @brief Gets the box position by reference position + */ +template +typename Box::coord_type box_position (const Box &box, int ref) +{ + if (horizontally) { + if (ref < 0) { + return box.left (); + } else if (ref == 0) { + return box.center ().x (); + } else { + return box.right (); + } + } else { + if (ref < 0) { + return box.bottom (); + } else if (ref == 0) { + return box.center ().y (); + } else { + return box.top (); + } + } +} + +/** + * @brief Compares boxes by their reference position + */ +template +class box_compare +{ +public: + typedef typename Box::coord_type coord_type; + + box_compare (int ref) + : m_ref (ref) + { + // .. nothing yet .. + } + + bool operator() (const std::pair &a, const std::pair &b) const + { + coord_type ca = box_position (a.first, m_ref); + coord_type cb = box_position (b.first, m_ref); + + if (! db::coord_traits::equal (ca, cb)) { + return db::coord_traits::less (ca, cb); + } else { + coord_type ca2 = box_position (a.first, m_ref); + coord_type cb2 = box_position (b.first, m_ref); + return db::coord_traits::less (ca2, cb2); + } + } + +private: + int m_ref; +}; + +/** + * @brief Does some heuristic binning of coordinates + */ +template +void do_bin (typename std::vector >::const_iterator b, typename std::vector >::const_iterator e, int ref, std::vector > &bins) +{ + typedef typename Box::coord_type coord_type; + + // determine maximum distance between adjacent coordinates + + coord_type max_dist = 0; + for (typename std::vector >::const_iterator i = b + 1; i != e; ++i) { + max_dist = std::max (max_dist, box_position (i->first, ref) - box_position ((i - 1)->first, ref)); + } + + // heuristically, everything that has a distance of less than 1/3 of the maximum distance falls into one bin + + coord_type bin_start = box_position (b->first, ref); + bins.push_back (std::vector ()); + bins.back ().push_back (b->second); + + coord_type thr = max_dist / 3; + + for (typename std::vector >::const_iterator i = b + 1; i != e; ++i) { + coord_type c = box_position (i->first, ref); + if (c - bin_start > thr) { + // start a new bin + bins.push_back (std::vector ()); + bin_start = c; + } + bins.back ().push_back (i->second); + } +} + +/** + * @brief Computes the effective box width (rounded to pitch, space added) + */ +template +inline typename Box::coord_type eff_dim (const Box &box, typename Box::coord_type pitch, typename Box::coord_type space) +{ + typedef typename Box::coord_type coord_type; + + coord_type d = (horizontal ? box.width () : box.height ()) + space; + if (pitch > 0) { + d = db::coord_traits::rounded (ceil (double (d) / double (pitch) - 1e-10) * double (pitch)); + } + return d; +} + +template +struct max_coord_join_op +{ + void operator() (Coord &a, const Coord &b) const + { + a = std::max (a, b); + } +}; + +/** + * @brief Implements an algorithm for 2d-distributing rectangular objects + */ +template +class distributed_placer +{ +public: + typedef typename Box::coord_type coord_type; + typedef std::vector > objects; + typedef typename objects::const_iterator iterator; + + /** + * @brief Constructor + */ + distributed_placer () + { + // .. nothing yet .. + } + + /** + * @brief Reserves space for n objects + */ + void reserve (size_t n) + { + m_objects.reserve (n); + } + + /** + * @brief Inserts a new object + */ + void insert (const Box &box, const Value &value) + { + tl_assert (! box.empty ()); + m_objects.push_back (std::make_pair (box, value)); + } + + /** + * @brief Stored objects iterator: begin + */ + iterator begin () const + { + return m_objects.begin (); + } + + /** + * @brief Stored objects iterator: end + */ + iterator end () const + { + return m_objects.end (); + } + + /** + * @brief Distributes the stored objects in vertical direction only + * + * @param ref The reference location (-1: bottom, 0: center, 1: top) + * @param refp The alignment in the other (horizontal) direction (-1: left, 0: center, 1: right, other: leave as is) + * @param pitch The distribution pitch (grid) or 0 for no pitch + * @param space The minimum space between the objects + */ + void distribute_v (int ref, int refp, coord_type pitch, coord_type space) + { + do_distribute_1d (ref, refp, pitch, space); + } + + /** + * @brief Distributes the stored objects in horizontal direction only + * + * @param ref The reference location (-1: left, 0: center, 1: right) + * @param refp The alignment in the other (vertical) direction (-1: bottom, 0: center, 1: top, other: leave as is) + * @param pitch The distribution pitch (grid) or 0 for no pitch + * @param space The minimum space between the objects + */ + void distribute_h (int ref, int refp, coord_type pitch, coord_type space) + { + do_distribute_1d (ref, refp, pitch, space); + } + + /** + * @brief Distributes the stored objects in horizontal and vertical direction + * + * @param href The horizontal reference location (-1: left, 0: center, 1: right) + * @param hpitch The horizontal distribution pitch (grid) or 0 for no pitch + * @param hspace The horizontal minimum space between the objects + * @param vref The vertical reference location (-1: bottom, 0: center, 1: top) + * @param vpitch The vertical distribution pitch (grid) or 0 for no pitch + * @param vspace The vertical minimum space between the objects + */ + void distribute_matrix (int href, coord_type hpitch, coord_type hspace, int vref, coord_type vpitch, coord_type vspace) + { + if (m_objects.size () < 2) { + return; + } + + // The algorithm is this: + // 1.) Bin the boxes according to their positions in horizontal and vertical direction. + // This forms the potential columns and rows + // 2.) Compute the row and column widths and heights as the maximum of their content + // 3.) position the objects inside these cells + + std::vector > indexed_boxes; + indexed_boxes.reserve (m_objects.size ()); + + Box all; + for (typename objects::iterator i = m_objects.begin (); i != m_objects.end (); ++i) { + all += i->first; + } + + size_t n = 0; + for (typename objects::iterator i = m_objects.begin (); i != m_objects.end (); ++i, ++n) { + indexed_boxes.push_back (std::make_pair (i->first, n)); + } + + std::vector > hbins, vbins; + + std::sort (indexed_boxes.begin (), indexed_boxes.end (), box_compare (href)); + do_bin (indexed_boxes.begin (), indexed_boxes.end (), href, hbins); + + std::sort (indexed_boxes.begin (), indexed_boxes.end (), box_compare (vref)); + do_bin (indexed_boxes.begin (), indexed_boxes.end (), vref, vbins); + + // rewrite the bins to cell occupation lists + + std::vector > > cells; + + cells.resize (hbins.size ()); + for (size_t i = 0; i < hbins.size (); ++i) { + cells [i].resize (vbins.size ()); + } + + { + + std::vector hbin_for_index; + hbin_for_index.resize (indexed_boxes.size (), size_t (0)); + for (std::vector >::const_iterator i = hbins.begin (); i != hbins.end (); ++i) { + for (std::vector::const_iterator j = i->begin (); j != i->end (); ++j) { + hbin_for_index [*j] = i - hbins.begin (); + } + } + + for (std::vector >::const_iterator i = vbins.begin (); i != vbins.end (); ++i) { + for (std::vector::const_iterator j = i->begin (); j != i->end (); ++j) { + cells [hbin_for_index [*j]][i - vbins.begin ()].push_back (*j); + } + } + + } + + // initialize the cell widths + + std::vector cell_widths, cell_heights; + cell_widths.resize (hbins.size (), 0); + cell_heights.resize (vbins.size (), 0); + + // compute the cell widths as the maximum of the content + + for (std::vector > >::const_iterator i = cells.begin (); i != cells.end (); ++i) { + + for (std::vector >::const_iterator j = i->begin (); j != i->end (); ++j) { + + coord_type wcell = 0, hcell = 0; + for (std::vector::const_iterator k = j->begin (); k != j->end (); ++k) { + // NOTE: intra-cell objects are distributed horizontally + wcell += eff_dim (m_objects [*k].first, hpitch, hspace); + hcell = std::max (hcell, eff_dim (m_objects [*k].first, vpitch, vspace)); + } + + cell_widths [i - cells.begin ()] = std::max (cell_widths [i - cells.begin ()], wcell); + cell_heights [j - i->begin ()] = std::max (cell_heights [j - i->begin ()], hcell); + + } + + } + + // Compute the columns and row positions + + std::vector cell_xpos, cell_ypos; + cell_xpos.reserve (cell_widths.size ()); + cell_ypos.reserve (cell_heights.size ()); + + coord_type x = 0, y = 0; + for (typename std::vector::const_iterator i = cell_widths.begin (); i != cell_widths.end (); ++i) { + cell_xpos.push_back (x); + x += *i; + } + for (typename std::vector::const_iterator i = cell_heights.begin (); i != cell_heights.end (); ++i) { + cell_ypos.push_back (y); + y += *i; + } + + // Compute the actual coordinates of the objects inside the cells + + for (std::vector > >::const_iterator i = cells.begin (); i != cells.end (); ++i) { + + for (std::vector >::const_iterator j = i->begin (); j != i->end (); ++j) { + + coord_type wcell = 0; + for (std::vector::const_iterator k = j->begin (); k != j->end (); ++k) { + // NOTE: intra-cell objects are distributed horizontally + wcell += eff_dim (m_objects [*k].first, hpitch, hspace); + } + + coord_type x = cell_xpos [i - cells.begin ()]; + if (href == 0) { + x += (cell_widths [i - cells.begin ()] - wcell) / 2; + } else if (href > 0) { + x += (cell_widths [i - cells.begin ()] - wcell); + } + + for (std::vector::const_iterator k = j->begin (); k != j->end (); ++k) { + + coord_type w = eff_dim (m_objects [*k].first, hpitch, hspace); + coord_type h = eff_dim (m_objects [*k].first, vpitch, vspace); + + coord_type y = cell_ypos [j - i->begin ()]; + if (vref == 0) { + y += (cell_heights [j - i->begin ()] - h) / 2; + } else if (href > 0) { + y += (cell_heights [j - i->begin ()] - h); + } + + m_objects [*k].first.move (db::point (x, y) - m_objects [*k].first.p1 ()); + + // NOTE: intra-cell objects are distributed horizontally + x += w; + + } + + } + + } + + // Final adjustments - align the whole matrix with the original bounding box + + Box new_all; + for (typename objects::iterator i = m_objects.begin (); i != m_objects.end (); ++i, ++n) { + new_all += i->first; + } + + coord_type dh = box_position (all, href) - box_position (new_all, href); + coord_type dv = box_position (all, vref) - box_position (new_all, vref); + db::vector mv (dh, dv); + + for (typename objects::iterator i = m_objects.begin (); i != m_objects.end (); ++i) { + i->first.move (mv); + } + } + +private: + objects m_objects; + + template + void do_distribute_1d (int ref, int refp, coord_type pitch, coord_type space) + { + if (m_objects.size () < 2) { + return; + } + + Box all; + for (typename objects::const_iterator i = m_objects.begin () + 1; i != m_objects.end (); ++i) { + all += i->first; + } + + std::sort (m_objects.begin (), m_objects.end (), box_compare (ref)); + + Box current = m_objects.front ().first; + coord_type p0 = box_position (current, ref); + + for (typename objects::iterator i = m_objects.begin () + 1; i != m_objects.end (); ++i) { + + coord_type p = box_position (i->first, -1); + coord_type offset = box_position (i->first, ref) - p; + coord_type pnew = box_position (current, 1) + space; + + if (db::coord_traits::less (0, pitch)) { + pnew = coord_type (ceil (double (pnew + offset - p0) / double (pitch) - 1e-10)) * pitch - offset + p0; + } + + db::vector mv; + if (horizontally) { + mv = db::vector (pnew - p, 0); + } else { + mv = db::vector (0, pnew - p); + } + + i->first.move (mv); + current = i->first; + + } + + // final adjustment + Box new_all = m_objects.front ().first + m_objects.back ().first; + + db::vector mv; + coord_type d = box_position (all, ref) - box_position (new_all, ref); + if (horizontally) { + mv = db::vector (d, 0); + } else { + mv = db::vector (0, d); + } + + for (typename objects::iterator i = m_objects.begin (); i != m_objects.end (); ++i) { + + i->first.move (mv); + + if (refp >= -1 && refp <= 1) { + + coord_type dp = box_position (all, refp) - box_position (i->first, refp); + + db::vector mvp; + if (horizontally) { + mvp = db::vector (0, dp); + } else { + mvp = db::vector (dp, 0); + } + + i->first.move (mvp); + + } + + } + } +}; + +} + +#endif + diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index bd1940467..5cf86b819 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -25,209 +25,67 @@ #include "dbLibrary.h" #include "dbLibraryManager.h" #include "dbPCellHeader.h" -#include "edtEditorOptionsPages.h" #include "edtPCellParametersPage.h" #include "edtConfig.h" #include "edtService.h" +#include "edtEditorOptionsPages.h" +#include "edtPropertiesPageUtils.h" #include "tlExceptions.h" #include "layPlugin.h" #include "layLayoutView.h" #include "layCellSelectionForm.h" -#include "ui_EditorOptionsDialog.h" +#include "layQtTools.h" #include "ui_EditorOptionsGeneric.h" #include "ui_EditorOptionsPath.h" #include "ui_EditorOptionsText.h" #include "ui_EditorOptionsInst.h" +#include "ui_EditorOptionsInstPCellParam.h" + +#include +#include +#include +#include +#include namespace edt { // ------------------------------------------------------------------ -// EditorOptionsPage implementation +// Configures a value from a line edit -EditorOptionsPage::EditorOptionsPage () - : mp_owner (0), m_active (true), mp_plugin_declaration (0) -{ - // nothing yet .. -} - -EditorOptionsPage::~EditorOptionsPage () -{ - set_owner (0); -} - -void -EditorOptionsPage::set_owner (EditorOptionsPages *owner) -{ - if (mp_owner) { - mp_owner->unregister_page (this); - } - mp_owner = owner; -} - -void -EditorOptionsPage::activate (bool active) -{ - if (m_active != active) { - m_active = active; - if (mp_owner) { - mp_owner->activate_page (this); - } - } -} - -// ------------------------------------------------------------------ -// EditorOptionsPages implementation - -struct EOPCompareOp -{ - bool operator() (edt::EditorOptionsPage *a, edt::EditorOptionsPage *b) const - { - return a->order () < b->order (); - } -}; - -EditorOptionsPages::EditorOptionsPages (const std::vector &pages, lay::Dispatcher *root) - : mp_root (root) -{ - mp_ui = new Ui::EditorOptionsDialog (); - mp_ui->setupUi (this); - - connect (mp_ui->apply_pb, SIGNAL (clicked ()), this, SLOT (apply ())); - - m_pages = pages; - for (std::vector ::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) { - (*p)->set_owner (this); - } - - update (0); - setup (); -} - -EditorOptionsPages::~EditorOptionsPages () -{ - while (m_pages.size () > 0) { - delete m_pages [0]; - } - - delete mp_ui; - mp_ui = 0; -} - -void -EditorOptionsPages::unregister_page (edt::EditorOptionsPage *page) -{ - std::vector pages; - for (std::vector ::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) { - if (*p != page) { - pages.push_back (*p); - } - } - m_pages = pages; - update (0); -} - -void -EditorOptionsPages::activate_page (edt::EditorOptionsPage *page) +template +static void configure_from_line_edit (lay::Dispatcher *dispatcher, QLineEdit *le, const std::string &cfg_name) { try { - page->setup (mp_root); - } catch (...) { - // catch any errors related to configuration file errors etc. + Value value = Value (0); + tl::from_string (tl::to_string (le->text ()), value); + dispatcher->config_set (cfg_name, tl::to_string (value)); + lay::indicate_error (le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (le, &ex); } - update (page); - - if (isVisible ()) { - activateWindow (); - raise (); - } -} - -void -EditorOptionsPages::update (edt::EditorOptionsPage *page) -{ - std::sort (m_pages.begin (), m_pages.end (), EOPCompareOp ()); - - while (mp_ui->pages->count () > 0) { - mp_ui->pages->removeTab (0); - } - int index = -1; - for (std::vector ::iterator p = m_pages.begin (); p != m_pages.end (); ++p) { - if ((*p)->active ()) { - mp_ui->pages->addTab ((*p)->q_frame (), tl::to_qstring ((*p)->title ())); - if ((*p) == page) { - index = int (std::distance (m_pages.begin (), p)); - } - } else { - (*p)->q_frame ()->setParent (0); - } - } - if (index < 0) { - index = mp_ui->pages->currentIndex (); - } - if (index >= int (mp_ui->pages->count ())) { - index = mp_ui->pages->count () - 1; - } - mp_ui->pages->setCurrentIndex (index); -} - -void -EditorOptionsPages::setup () -{ - try { - - for (std::vector ::iterator p = m_pages.begin (); p != m_pages.end (); ++p) { - if ((*p)->active ()) { - (*p)->setup (mp_root); - } - } - - // make the display consistent with the status (this is important for - // PCell parameters where the PCell may be asked to modify the parameters) - do_apply (); - - } catch (...) { - // catch any errors related to configuration file errors etc. - } -} - -void -EditorOptionsPages::do_apply () -{ - for (std::vector ::iterator p = m_pages.begin (); p != m_pages.end (); ++p) { - if ((*p)->active ()) { - (*p)->apply (mp_root); - } - } -} - -void -EditorOptionsPages::apply () -{ -BEGIN_PROTECTED - do_apply (); -END_PROTECTED_W (this) -} - -void -EditorOptionsPages::accept () -{ -BEGIN_PROTECTED - do_apply (); - QDialog::accept (); -END_PROTECTED_W (this) } // ------------------------------------------------------------------ // EditorOptionsGeneric implementation -EditorOptionsGeneric::EditorOptionsGeneric () - : QWidget (), EditorOptionsPage () +EditorOptionsGeneric::EditorOptionsGeneric (lay::Dispatcher *dispatcher) + : EditorOptionsPage (dispatcher) { mp_ui = new Ui::EditorOptionsGeneric (); mp_ui->setupUi (this); connect (mp_ui->grid_cb, SIGNAL (activated (int)), this, SLOT (grid_changed (int))); + + connect (mp_ui->edit_grid_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->grid_cb, SIGNAL (activated (int)), this, SLOT (edited ())); + connect (mp_ui->move_angle_cb, SIGNAL (activated (int)), this, SLOT (edited ())); + connect (mp_ui->conn_angle_cb, SIGNAL (activated (int)), this, SLOT (edited ())); + connect (mp_ui->hier_sel_cbx, SIGNAL (clicked ()), this, SLOT (edited ())); + connect (mp_ui->hier_copy_mode_cbx, SIGNAL (activated (int)), this, SLOT (edited ())); + connect (mp_ui->snap_objects_cbx, SIGNAL (clicked ()), this, SLOT (edited ())); + connect (mp_ui->max_shapes_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->show_shapes_cbx, SIGNAL (clicked ()), this, SLOT (edited ())); } EditorOptionsGeneric::~EditorOptionsGeneric () @@ -236,27 +94,32 @@ EditorOptionsGeneric::~EditorOptionsGeneric () mp_ui = 0; } -std::string +std::string EditorOptionsGeneric::title () const { return tl::to_string (QObject::tr ("Basic Editing")); } -void -EditorOptionsGeneric::apply (lay::Plugin *root) +void +EditorOptionsGeneric::apply (lay::Dispatcher *root) { // Edit grid - - db::DVector eg; + EditGridConverter egc; if (mp_ui->grid_cb->currentIndex () == 0) { - eg = db::DVector (-1.0, -1.0); + root->config_set (cfg_edit_grid, egc.to_string (db::DVector (-1.0, -1.0))); } else if (mp_ui->grid_cb->currentIndex () == 1) { - eg = db::DVector (); + root->config_set (cfg_edit_grid, egc.to_string (db::DVector ())); } else { - egc.from_string_picky (tl::to_string (mp_ui->edit_grid_le->text ()), eg); + try { + db::DVector eg; + egc.from_string_picky (tl::to_string (mp_ui->edit_grid_le->text ()), eg); + lay::indicate_error (mp_ui->edit_grid_le, 0); + root->config_set (cfg_edit_grid, egc.to_string (eg)); + } catch (tl::Exception &ex) { + lay::indicate_error (mp_ui->edit_grid_le, &ex); + } } - root->config_set (cfg_edit_grid, egc.to_string (eg)); // Edit & move angle @@ -269,9 +132,7 @@ EditorOptionsGeneric::apply (lay::Plugin *root) root->config_set (cfg_edit_hier_copy_mode, tl::to_string ((cpm < 0 || cpm > 1) ? -1 : cpm)); root->config_set (cfg_edit_snap_to_objects, tl::to_string (mp_ui->snap_objects_cbx->isChecked ())); - unsigned int max_shapes = 1000; - tl::from_string (tl::to_string (mp_ui->max_shapes_le->text ()), max_shapes); - root->config_set (cfg_edit_max_shapes_of_instances, tl::to_string (max_shapes)); + configure_from_line_edit (root, mp_ui->max_shapes_le, cfg_edit_max_shapes_of_instances); root->config_set (cfg_edit_show_shapes_of_instances, tl::to_string (mp_ui->show_shapes_cbx->isChecked ())); } @@ -287,8 +148,8 @@ EditorOptionsGeneric::show_shapes_changed () mp_ui->max_shapes_le->setEnabled (mp_ui->show_shapes_cbx->isChecked ()); } -void -EditorOptionsGeneric::setup (lay::Plugin *root) +void +EditorOptionsGeneric::setup (lay::Dispatcher *root) { // Edit grid @@ -305,12 +166,13 @@ EditorOptionsGeneric::setup (lay::Plugin *root) mp_ui->edit_grid_le->setText (tl::to_qstring (egc.to_string (eg))); } grid_changed (mp_ui->grid_cb->currentIndex ()); + lay::indicate_error (mp_ui->edit_grid_le, 0); // edit & move angle ACConverter acc; lay::angle_constraint_type ac; - + ac = lay::AC_Any; root->config_get (cfg_edit_move_angle_mode, ac, acc); mp_ui->move_angle_cb->setCurrentIndex (int (ac)); @@ -334,6 +196,7 @@ EditorOptionsGeneric::setup (lay::Plugin *root) unsigned int max_shapes = 1000; root->config_get (cfg_edit_max_shapes_of_instances, max_shapes); mp_ui->max_shapes_le->setText (tl::to_qstring (tl::to_string (max_shapes))); + lay::indicate_error (mp_ui->max_shapes_le, 0); bool show_shapes = true; root->config_get (cfg_edit_show_shapes_of_instances, show_shapes); @@ -343,11 +206,16 @@ EditorOptionsGeneric::setup (lay::Plugin *root) // ------------------------------------------------------------------ // EditorOptionsText implementation -EditorOptionsText::EditorOptionsText () - : QWidget (), EditorOptionsPage () +EditorOptionsText::EditorOptionsText (lay::Dispatcher *dispatcher) + : lay::EditorOptionsPage (dispatcher) { mp_ui = new Ui::EditorOptionsText (); mp_ui->setupUi (this); + + connect (mp_ui->text_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->halign_cbx, SIGNAL (activated (int)), this, SLOT (edited ())); + connect (mp_ui->valign_cbx, SIGNAL (activated (int)), this, SLOT (edited ())); + connect (mp_ui->size_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); } EditorOptionsText::~EditorOptionsText () @@ -363,7 +231,7 @@ EditorOptionsText::title () const } void -EditorOptionsText::apply (lay::Plugin *root) +EditorOptionsText::apply (lay::Dispatcher *root) { // Text string root->config_set (cfg_edit_text_string, tl::unescape_string (tl::to_string (mp_ui->text_le->text ()))); @@ -387,7 +255,7 @@ EditorOptionsText::apply (lay::Plugin *root) } void -EditorOptionsText::setup (lay::Plugin *root) +EditorOptionsText::setup (lay::Dispatcher *root) { // Text string std::string s; @@ -416,13 +284,18 @@ EditorOptionsText::setup (lay::Plugin *root) // ------------------------------------------------------------------ // EditorOptionsPath implementation -EditorOptionsPath::EditorOptionsPath () - : QWidget (), EditorOptionsPage () +EditorOptionsPath::EditorOptionsPath (lay::Dispatcher *dispatcher) + : lay::EditorOptionsPage (dispatcher) { mp_ui = new Ui::EditorOptionsPath (); mp_ui->setupUi (this); connect (mp_ui->type_cb, SIGNAL (currentIndexChanged (int)), this, SLOT (type_changed (int))); + + connect (mp_ui->width_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->type_cb, SIGNAL (activated (int)), this, SLOT (edited ())); + connect (mp_ui->start_ext_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->end_ext_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); } EditorOptionsPath::~EditorOptionsPath () @@ -445,13 +318,11 @@ EditorOptionsPath::type_changed (int type) } void -EditorOptionsPath::apply (lay::Plugin *root) +EditorOptionsPath::apply (lay::Dispatcher *root) { // width - double w = 0.0; - tl::from_string (tl::to_string (mp_ui->width_le->text ()), w); - root->config_set (cfg_edit_path_width, tl::to_string (w)); + configure_from_line_edit (root, mp_ui->width_le, cfg_edit_path_width); // path type and extensions @@ -465,14 +336,10 @@ EditorOptionsPath::apply (lay::Plugin *root) } else if (mp_ui->type_cb->currentIndex () == 2) { - double bgnext = 0.0, endext = 0.0; root->config_set (cfg_edit_path_ext_type, "variable"); - tl::from_string (tl::to_string (mp_ui->start_ext_le->text ()), bgnext); - root->config_set (cfg_edit_path_ext_var_begin, tl::to_string (bgnext)); - - tl::from_string (tl::to_string (mp_ui->end_ext_le->text ()), endext); - root->config_set (cfg_edit_path_ext_var_end, tl::to_string (endext)); + configure_from_line_edit (root, mp_ui->start_ext_le, cfg_edit_path_ext_var_begin); + configure_from_line_edit (root, mp_ui->end_ext_le, cfg_edit_path_ext_var_end); } else if (mp_ui->type_cb->currentIndex () == 3) { @@ -482,13 +349,14 @@ EditorOptionsPath::apply (lay::Plugin *root) } void -EditorOptionsPath::setup (lay::Plugin *root) +EditorOptionsPath::setup (lay::Dispatcher *root) { // width double w = 0.0; root->config_get (cfg_edit_path_width, w); mp_ui->width_le->setText (tl::to_qstring (tl::to_string (w))); + lay::indicate_error (mp_ui->width_le, 0); // path type and extensions @@ -509,26 +377,35 @@ EditorOptionsPath::setup (lay::Plugin *root) root->config_get (cfg_edit_path_ext_var_begin, bgnext); root->config_get (cfg_edit_path_ext_var_end, endext); mp_ui->start_ext_le->setText (tl::to_qstring (tl::to_string (bgnext))); + lay::indicate_error (mp_ui->start_ext_le, 0); mp_ui->end_ext_le->setText (tl::to_qstring (tl::to_string (endext))); + lay::indicate_error (mp_ui->end_ext_le, 0); } // ------------------------------------------------------------------ // EditorOptionsInst implementation -EditorOptionsInst::EditorOptionsInst (lay::Dispatcher *root) - : QWidget (), EditorOptionsPage (), mp_root (root), mp_pcell_parameters (0) +EditorOptionsInst::EditorOptionsInst (lay::Dispatcher *dispatcher) + : lay::EditorOptionsPage (dispatcher) { mp_ui = new Ui::EditorOptionsInst (); mp_ui->setupUi (this); connect (mp_ui->array_grp, SIGNAL (clicked ()), this, SLOT (array_changed ())); connect (mp_ui->browse_pb, SIGNAL (clicked ()), this, SLOT (browse_cell ())); - connect (mp_ui->lib_cbx, SIGNAL (currentIndexChanged (int)), this, SLOT (library_changed (int))); - connect (mp_ui->cell_le, SIGNAL (textChanged (const QString &)), this, SLOT (cell_name_changed (const QString &))); - - QHBoxLayout *layout = new QHBoxLayout (mp_ui->pcell_tab); - layout->setMargin (0); - mp_ui->pcell_tab->setLayout (layout); + connect (mp_ui->lib_cbx, SIGNAL (currentIndexChanged (int)), this, SLOT (library_changed ())); + connect (mp_ui->cell_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->angle_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->scale_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->rows_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->row_x_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->row_y_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->columns_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->column_x_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->column_y_le, SIGNAL (editingFinished ()), this, SLOT (edited ())); + connect (mp_ui->mirror_cbx, SIGNAL (clicked ()), this, SLOT (edited ())); + connect (mp_ui->array_grp, SIGNAL (clicked ()), this, SLOT (edited ())); + connect (mp_ui->place_origin_cb, SIGNAL (clicked ()), this, SLOT (edited ())); m_cv_index = -1; } @@ -546,19 +423,61 @@ EditorOptionsInst::title () const } void -EditorOptionsInst::library_changed (int) +EditorOptionsInst::library_changed () { -BEGIN_PROTECTED - update_pcell_parameters (); -END_PROTECTED + update_cell_edits (); + edited (); } +// Maximum number of cells for which to offer a cell name completer +const static size_t max_cells = 10000; + void -EditorOptionsInst::cell_name_changed (const QString &) +EditorOptionsInst::update_cell_edits () { -BEGIN_PROTECTED - update_pcell_parameters (); -END_PROTECTED + if (mp_ui->cell_le->completer ()) { + mp_ui->cell_le->completer ()->deleteLater (); + } + + db::Layout *layout = 0; + lay::LayoutView *view = lay::LayoutView::current (); + + // find the layout the cell has to be looked up: that is either the layout of the current instance or + // the library selected + if (mp_ui->lib_cbx->current_library ()) { + layout = &mp_ui->lib_cbx->current_library ()->layout (); + } else if (view && view->cellview (m_cv_index).is_valid ()) { + layout = &view->cellview (m_cv_index)->layout (); + } + + if (! layout) { + return; + } + + QStringList cellnames; + if (layout->cells () < max_cells) { + for (db::Layout::iterator c = layout->begin (); c != layout->end (); ++c) { + cellnames.push_back (tl::to_qstring (layout->cell_name (c->cell_index ()))); + } + for (db::Layout::pcell_iterator pc = layout->begin_pcells (); pc != layout->end_pcells () && size_t (cellnames.size ()) < max_cells; ++pc) { + cellnames.push_back (tl::to_qstring (pc->first)); + } + } + + if (size_t (cellnames.size ()) < max_cells) { + QCompleter *completer = new QCompleter (cellnames, this); + completer->setCaseSensitivity (Qt::CaseSensitive); + mp_ui->cell_le->setCompleter (completer); + } else { + mp_ui->cell_le->setCompleter (0); + } + + std::pair pc = layout->pcell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ()); + std::pair cc = layout->cell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ()); + + // by the way, update the foreground color of the cell edit box as well (red, if not valid) + tl::Exception ex ("No cell or PCell with this name"); + lay::indicate_error (mp_ui->cell_le, (! pc.first && ! cc.first) ? &ex : 0); } void @@ -602,7 +521,7 @@ BEGIN_PROTECTED } else if (layout->is_valid_cell_index (form.selected_cell_index ())) { mp_ui->cell_le->setText (tl::to_qstring (layout->cell_name (form.selected_cell_index ()))); } - update_pcell_parameters (); + edited (); } } @@ -620,74 +539,40 @@ EditorOptionsInst::array_changed () mp_ui->columns_le->setEnabled (array); mp_ui->column_x_le->setEnabled (array); mp_ui->column_y_le->setEnabled (array); + edited (); } void -EditorOptionsInst::apply (lay::Plugin *root) +EditorOptionsInst::apply (lay::Dispatcher *root) { // cell name root->config_set (cfg_edit_inst_cell_name, tl::to_string (mp_ui->cell_le->text ())); - // cell name + // lib name if (mp_ui->lib_cbx->current_library ()) { root->config_set (cfg_edit_inst_lib_name, mp_ui->lib_cbx->current_library ()->get_name ()); } else { root->config_set (cfg_edit_inst_lib_name, std::string ()); } - // pcell parameters - std::string param; - db::Layout *layout = 0; - - if (mp_ui->lib_cbx->current_library ()) { - layout = &mp_ui->lib_cbx->current_library ()->layout (); - } else if (m_cv_index >= 0 && lay::LayoutView::current () && lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) { - layout = &lay::LayoutView::current ()->cellview (m_cv_index)->layout (); - } - - if (layout && mp_pcell_parameters) { - std::pair pc = layout->pcell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ()); - if (pc.first) { - const db::PCellDeclaration *pc_decl = layout->pcell_declaration (pc.second); - if (pc_decl) { - param = pcell_parameters_to_string (pc_decl->named_parameters (mp_pcell_parameters->get_parameters ())); - } - } - } - - root->config_set (cfg_edit_inst_pcell_parameters, param); - // rotation, scaling - double angle = 0.0; - tl::from_string (tl::to_string (mp_ui->angle_le->text ()), angle); - root->config_set (cfg_edit_inst_angle, tl::to_string (angle)); + configure_from_line_edit (root, mp_ui->angle_le, cfg_edit_inst_angle); bool mirror = mp_ui->mirror_cbx->isChecked (); root->config_set (cfg_edit_inst_mirror, tl::to_string (mirror)); - double scale = 1.0; - tl::from_string (tl::to_string (mp_ui->scale_le->text ()), scale); - root->config_set (cfg_edit_inst_scale, tl::to_string (scale)); + configure_from_line_edit (root, mp_ui->scale_le, cfg_edit_inst_scale); // array bool array = mp_ui->array_grp->isChecked (); root->config_set (cfg_edit_inst_array, tl::to_string (array)); - int rows = 1, columns = 1; - double row_x = 0.0, row_y = 0.0, column_x = 0.0, column_y = 0.0; - tl::from_string (tl::to_string (mp_ui->rows_le->text ()), rows); - tl::from_string (tl::to_string (mp_ui->row_x_le->text ()), row_x); - tl::from_string (tl::to_string (mp_ui->row_y_le->text ()), row_y); - tl::from_string (tl::to_string (mp_ui->columns_le->text ()), columns); - tl::from_string (tl::to_string (mp_ui->column_x_le->text ()), column_x); - tl::from_string (tl::to_string (mp_ui->column_y_le->text ()), column_y); - - root->config_set (cfg_edit_inst_rows, tl::to_string (rows)); - root->config_set (cfg_edit_inst_row_x, tl::to_string (row_x)); - root->config_set (cfg_edit_inst_row_y, tl::to_string (row_y)); - root->config_set (cfg_edit_inst_columns, tl::to_string (columns)); - root->config_set (cfg_edit_inst_column_x, tl::to_string (column_x)); - root->config_set (cfg_edit_inst_column_y, tl::to_string (column_y)); + configure_from_line_edit (root, mp_ui->rows_le, cfg_edit_inst_rows); + configure_from_line_edit (root, mp_ui->row_x_le, cfg_edit_inst_row_x); + configure_from_line_edit (root, mp_ui->row_y_le, cfg_edit_inst_row_y); + configure_from_line_edit (root, mp_ui->columns_le, cfg_edit_inst_columns); + configure_from_line_edit (root, mp_ui->column_x_le, cfg_edit_inst_column_x); + configure_from_line_edit (root, mp_ui->column_y_le, cfg_edit_inst_column_y); // place origin of cell flag bool place_origin = mp_ui->place_origin_cb->isChecked (); @@ -695,45 +580,187 @@ EditorOptionsInst::apply (lay::Plugin *root) } void -EditorOptionsInst::setup (lay::Plugin *root) +EditorOptionsInst::setup (lay::Dispatcher *root) { m_cv_index = -1; if (lay::LayoutView::current ()) { m_cv_index = lay::LayoutView::current ()->active_cellview_index (); } - mp_ui->lib_cbx->update_list (); - if (m_cv_index >= 0 && lay::LayoutView::current () && lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) { - mp_ui->lib_cbx->set_technology_filter (lay::LayoutView::current ()->cellview (m_cv_index)->tech_name (), true); - } else { - mp_ui->lib_cbx->set_technology_filter (std::string (), false); + + try { + + mp_ui->lib_cbx->blockSignals (true); + + mp_ui->lib_cbx->update_list (); + if (m_cv_index >= 0 && lay::LayoutView::current () && lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) { + mp_ui->lib_cbx->set_technology_filter (lay::LayoutView::current ()->cellview (m_cv_index)->tech_name (), true); + } else { + mp_ui->lib_cbx->set_technology_filter (std::string (), false); + } + + // cell name + std::string s; + root->config_get (cfg_edit_inst_cell_name, s); + mp_ui->cell_le->setText (tl::to_qstring (s)); + + // library + std::string l; + root->config_get (cfg_edit_inst_lib_name, l); + mp_ui->lib_cbx->set_current_library (db::LibraryManager::instance ().lib_ptr_by_name (l)); + + mp_ui->lib_cbx->blockSignals (false); + update_cell_edits (); + + } catch (...) { + mp_ui->lib_cbx->blockSignals (false); + throw; } + // rotation, scaling + double angle = 0.0; + root->config_get (cfg_edit_inst_angle, angle); + mp_ui->angle_le->setText (tl::to_qstring (tl::to_string (angle))); + lay::indicate_error (mp_ui->angle_le, 0); + + bool mirror = false; + root->config_get (cfg_edit_inst_mirror, mirror); + mp_ui->mirror_cbx->setChecked (mirror); + + double scale = 1.0; + root->config_get (cfg_edit_inst_scale, scale); + mp_ui->scale_le->setText (tl::to_qstring (tl::to_string (scale))); + lay::indicate_error (mp_ui->scale_le, 0); + + // array + bool array = false; + root->config_get (cfg_edit_inst_array, array); + mp_ui->array_grp->setChecked (array); + + int rows = 1, columns = 1; + double row_x = 0.0, row_y = 0.0, column_x = 0.0, column_y = 0.0; + root->config_get (cfg_edit_inst_rows, rows); + root->config_get (cfg_edit_inst_row_x, row_x); + root->config_get (cfg_edit_inst_row_y, row_y); + root->config_get (cfg_edit_inst_columns, columns); + root->config_get (cfg_edit_inst_column_x, column_x); + root->config_get (cfg_edit_inst_column_y, column_y); + + mp_ui->rows_le->setText (tl::to_qstring (tl::to_string (rows))); + lay::indicate_error (mp_ui->rows_le, 0); + mp_ui->row_x_le->setText (tl::to_qstring (tl::to_string (row_x))); + lay::indicate_error (mp_ui->row_x_le, 0); + mp_ui->row_y_le->setText (tl::to_qstring (tl::to_string (row_y))); + lay::indicate_error (mp_ui->row_y_le, 0); + mp_ui->columns_le->setText (tl::to_qstring (tl::to_string (columns))); + lay::indicate_error (mp_ui->columns_le, 0); + mp_ui->column_x_le->setText (tl::to_qstring (tl::to_string (column_x))); + lay::indicate_error (mp_ui->column_x_le, 0); + mp_ui->column_y_le->setText (tl::to_qstring (tl::to_string (column_y))); + lay::indicate_error (mp_ui->column_y_le, 0); + + // place origin of cell flag + bool place_origin = false; + root->config_get (cfg_edit_inst_place_origin, place_origin); + mp_ui->place_origin_cb->setChecked (place_origin); +} + +// ------------------------------------------------------------------ +// EditorOptionsInstPCellParam implementation + +EditorOptionsInstPCellParam::EditorOptionsInstPCellParam (lay::Dispatcher *dispatcher) + : lay::EditorOptionsPage (dispatcher), mp_pcell_parameters (0), mp_placeholder_label (0) +{ + mp_ui = new Ui::EditorOptionsInstPCellParam (); + mp_ui->setupUi (this); +} + +EditorOptionsInstPCellParam::~EditorOptionsInstPCellParam () +{ + delete mp_ui; + mp_ui = 0; +} + +std::string +EditorOptionsInstPCellParam::title () const +{ + return tl::to_string (QObject::tr ("PCell")); +} + +void +EditorOptionsInstPCellParam::apply (lay::Dispatcher *root) +{ + // pcell parameters + std::string param; + db::Layout *layout = 0; + + db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name); + if (lib) { + layout = &lib->layout (); + } else if (m_cv_index >= 0 && lay::LayoutView::current () && lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) { + layout = &lay::LayoutView::current ()->cellview (m_cv_index)->layout (); + } + + bool ok = true; + + if (layout && mp_pcell_parameters) { + std::pair pc = layout->pcell_by_name (tl::to_string (m_cell_name).c_str ()); + if (pc.first) { + const db::PCellDeclaration *pc_decl = layout->pcell_declaration (pc.second); + if (pc_decl) { + param = pcell_parameters_to_string (pc_decl->named_parameters (mp_pcell_parameters->get_parameters (&ok))); + } + } + } + + if (ok) { + root->config_set (cfg_edit_inst_pcell_parameters, param); + } +} + +void +EditorOptionsInstPCellParam::setup (lay::Dispatcher *root) +{ + m_cv_index = -1; + if (lay::LayoutView::current ()) { + m_cv_index = lay::LayoutView::current ()->active_cellview_index (); + } + + bool needs_update = (mp_pcell_parameters == 0); + // cell name - std::string s; - root->config_get (cfg_edit_inst_cell_name, s); - mp_ui->cell_le->setText (tl::to_qstring (s)); + std::string cn; + root->config_get (cfg_edit_inst_cell_name, cn); + if (cn != m_cell_name) { + m_cell_name = cn; + needs_update = true; + } // library - std::string l; - root->config_get (cfg_edit_inst_lib_name, l); - mp_ui->lib_cbx->set_current_library (db::LibraryManager::instance ().lib_ptr_by_name (l)); + std::string ln; + root->config_get (cfg_edit_inst_lib_name, ln); + if (ln != m_lib_name) { + m_lib_name = ln; + needs_update = true; + } + + db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name); // pcell parameters std::string param; root->config_get (cfg_edit_inst_pcell_parameters, param); db::Layout *layout = 0; - if (mp_ui->lib_cbx->current_library ()) { - layout = &mp_ui->lib_cbx->current_library ()->layout (); + if (lib) { + layout = &lib->layout (); } else if (m_cv_index >= 0 && lay::LayoutView::current () && lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) { layout = &lay::LayoutView::current ()->cellview (m_cv_index)->layout (); } std::vector pv; - if (layout && mp_pcell_parameters) { + if (layout) { - std::pair pc = layout->pcell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ()); + std::pair pc = layout->pcell_by_name (tl::to_string (m_cell_name).c_str ()); if (pc.first) { @@ -768,88 +795,49 @@ EditorOptionsInst::setup (lay::Plugin *root) } } - + + if (! needs_update) { + bool ok = false; + if (mp_pcell_parameters->get_parameters (&ok) != pv || ! ok) { + needs_update = true; + } + } + try { - update_pcell_parameters (pv); + if (needs_update) { + update_pcell_parameters (pv); + } } catch (...) { } - - // rotation, scaling - double angle = 0.0; - root->config_get (cfg_edit_inst_angle, angle); - mp_ui->angle_le->setText (tl::to_qstring (tl::to_string (angle))); - - bool mirror = false; - root->config_get (cfg_edit_inst_mirror, mirror); - mp_ui->mirror_cbx->setChecked (mirror); - - double scale = 1.0; - root->config_get (cfg_edit_inst_scale, scale); - mp_ui->scale_le->setText (tl::to_qstring (tl::to_string (scale))); - - // array - bool array = false; - root->config_get (cfg_edit_inst_array, array); - mp_ui->array_grp->setChecked (array); - - int rows = 1, columns = 1; - double row_x = 0.0, row_y = 0.0, column_x = 0.0, column_y = 0.0; - root->config_get (cfg_edit_inst_rows, rows); - root->config_get (cfg_edit_inst_row_x, row_x); - root->config_get (cfg_edit_inst_row_y, row_y); - root->config_get (cfg_edit_inst_columns, columns); - root->config_get (cfg_edit_inst_column_x, column_x); - root->config_get (cfg_edit_inst_column_y, column_y); - - mp_ui->rows_le->setText (tl::to_qstring (tl::to_string (rows))); - mp_ui->row_x_le->setText (tl::to_qstring (tl::to_string (row_x))); - mp_ui->row_y_le->setText (tl::to_qstring (tl::to_string (row_y))); - mp_ui->columns_le->setText (tl::to_qstring (tl::to_string (columns))); - mp_ui->column_x_le->setText (tl::to_qstring (tl::to_string (column_x))); - mp_ui->column_y_le->setText (tl::to_qstring (tl::to_string (column_y))); - - // place origin of cell flag - bool place_origin = false; - root->config_get (cfg_edit_inst_place_origin, place_origin); - mp_ui->place_origin_cb->setChecked (place_origin); } -void -EditorOptionsInst::update_pcell_parameters () +void +EditorOptionsInstPCellParam::update_pcell_parameters () { update_pcell_parameters (std::vector ()); } -void -EditorOptionsInst::update_pcell_parameters (const std::vector ¶meters) +void +EditorOptionsInstPCellParam::update_pcell_parameters (const std::vector ¶meters) { db::Layout *layout = 0; + lay::LayoutView *view = lay::LayoutView::current (); - if (m_cv_index < 0 || !lay::LayoutView::current () || !lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) { - mp_ui->param_tab_widget->setTabEnabled (1, false); - return; - } - - // find the layout the cell has to be looked up: that is either the layout of the current instance or + // find the layout the cell has to be looked up: that is either the layout of the current instance or // the library selected - if (mp_ui->lib_cbx->current_library ()) { - layout = &mp_ui->lib_cbx->current_library ()->layout (); - } else { - layout = &lay::LayoutView::current ()->cellview (m_cv_index)->layout (); + db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name); + if (lib) { + layout = &lib->layout (); + } else if (view) { + const lay::CellView &cv = view->cellview (m_cv_index); + if (cv.is_valid ()) { + layout = &cv->layout (); + } } - std::pair pc = layout->pcell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ()); - std::pair cc = layout->cell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ()); - - // by the way, update the foreground color of the cell edit box as well (red, if not valid) - QPalette pl = mp_ui->cell_le->palette (); - if (! pc.first && ! cc.first) { - pl.setColor (QPalette::Text, Qt::red); - pl.setColor (QPalette::Base, QColor (Qt::red).lighter (180)); - } else { - pl.setColor (QPalette::Text, palette ().color (QPalette::Text)); - pl.setColor (QPalette::Base, palette ().color (QPalette::Base)); + std::pair pc (false, 0); + if (layout) { + pc = layout->pcell_by_name (tl::to_string (m_cell_name).c_str ()); } - mp_ui->cell_le->setPalette (pl); PCellParametersPage::State pcp_state; @@ -860,19 +848,30 @@ EditorOptionsInst::update_pcell_parameters (const std::vector &par mp_pcell_parameters->deleteLater (); } + if (mp_placeholder_label) { + mp_placeholder_label->hide (); + mp_placeholder_label->deleteLater (); + } + mp_pcell_parameters = 0; + mp_placeholder_label = 0; - if (pc.first && layout->pcell_declaration (pc.second)) { + if (pc.first && layout->pcell_declaration (pc.second) && view && view->cellview (m_cv_index).is_valid ()) { - mp_ui->param_tab_widget->setTabEnabled (1, true); - lay::LayoutView *view = lay::LayoutView::current (); - mp_pcell_parameters = new PCellParametersPage (mp_ui->pcell_tab, &view->cellview (m_cv_index)->layout (), view, m_cv_index, layout->pcell_declaration (pc.second), parameters); - mp_ui->pcell_tab->layout ()->addWidget (mp_pcell_parameters); + mp_pcell_parameters = new PCellParametersPage (this, true /*dense*/); + mp_pcell_parameters->setup (&view->cellview (m_cv_index)->layout (), view, m_cv_index, layout->pcell_declaration (pc.second), parameters); + this->layout ()->addWidget (mp_pcell_parameters); mp_pcell_parameters->set_state (pcp_state); + connect (mp_pcell_parameters, SIGNAL (edited ()), this, SLOT (edited ())); } else { - mp_ui->param_tab_widget->setTabEnabled (1, false); + + mp_placeholder_label = new QLabel (this); + mp_placeholder_label->setText (tr ("Not a PCell")); + mp_placeholder_label->setAlignment (Qt::AlignHCenter | Qt::AlignVCenter); + this->layout ()->addWidget (mp_placeholder_label); + } } diff --git a/src/edt/edt/edtEditorOptionsPages.h b/src/edt/edt/edtEditorOptionsPages.h index 65145212f..3b639c5d8 100644 --- a/src/edt/edt/edtEditorOptionsPages.h +++ b/src/edt/edt/edtEditorOptionsPages.h @@ -24,12 +24,17 @@ #ifndef HDR_edtEditorOptionsPages #define HDR_edtEditorOptionsPages +#include "layEditorOptionsPage.h" + #include -#include +#include #include #include +class QTabWidget; +class QLabel; + namespace Ui { class EditorOptionsDialog; @@ -39,6 +44,7 @@ namespace Ui class EditorOptionsPath; class EditorOptionsText; class EditorOptionsInst; + class EditorOptionsInstPCellParam; } namespace lay @@ -53,83 +59,22 @@ namespace edt class PCellParametersPage; -class EditorOptionsPages; - -/** - * @brief The base class for a object properties page - */ -class EditorOptionsPage -{ -public: - EditorOptionsPage (); - virtual ~EditorOptionsPage (); - - virtual QWidget *q_frame () = 0; - virtual std::string title () const = 0; - virtual int order () const = 0; - virtual void apply (lay::Plugin *root) = 0; - virtual void setup (lay::Plugin *root) = 0; - - bool active () const { return m_active; } - void activate (bool active); - void set_owner (EditorOptionsPages *owner); - - const lay::PluginDeclaration *plugin_declaration () const { return mp_plugin_declaration; } - void set_plugin_declaration (const lay::PluginDeclaration *pd) { mp_plugin_declaration = pd; } - -private: - EditorOptionsPages *mp_owner; - bool m_active; - const lay::PluginDeclaration *mp_plugin_declaration; -}; - -/** - * @brief The object properties dialog - */ -class EditorOptionsPages - : public QDialog -{ -Q_OBJECT - -public: - EditorOptionsPages (const std::vector &pages, lay::Dispatcher *root); - ~EditorOptionsPages (); - - void unregister_page (edt::EditorOptionsPage *page); - void activate_page (edt::EditorOptionsPage *page); - -public slots: - void apply (); - void setup (); - void accept (); - -private: - std::vector m_pages; - Ui::EditorOptionsDialog *mp_ui; - lay::Dispatcher *mp_root; - - void update (edt::EditorOptionsPage *page); - void do_apply (); -}; - /** * @brief The generic properties page */ class EditorOptionsGeneric - : public QWidget, public EditorOptionsPage + : public lay::EditorOptionsPage { Q_OBJECT public: - EditorOptionsGeneric (); + EditorOptionsGeneric (lay::Dispatcher *dispatcher); ~EditorOptionsGeneric (); - virtual QWidget *q_frame () { return this; } - virtual std::string title () const; virtual int order () const { return 0; } - void apply (lay::Plugin *root); - void setup (lay::Plugin *root); + void apply (lay::Dispatcher *root); + void setup (lay::Dispatcher *root); public slots: void grid_changed (int); @@ -143,18 +88,16 @@ private: * @brief The text properties page */ class EditorOptionsText - : public QWidget, public EditorOptionsPage + : public lay::EditorOptionsPage { public: - EditorOptionsText (); + EditorOptionsText (lay::Dispatcher *dispatcher); ~EditorOptionsText (); - virtual QWidget *q_frame () { return this; } - virtual std::string title () const; virtual int order () const { return 10; } - void apply (lay::Plugin *root); - void setup (lay::Plugin *root); + void apply (lay::Dispatcher *root); + void setup (lay::Dispatcher *root); private: Ui::EditorOptionsText *mp_ui; @@ -164,20 +107,18 @@ private: * @brief The path properties page */ class EditorOptionsPath - : public QWidget, public EditorOptionsPage + : public lay::EditorOptionsPage { Q_OBJECT public: - EditorOptionsPath (); + EditorOptionsPath (lay::Dispatcher *dispatcher); ~EditorOptionsPath (); - virtual QWidget *q_frame () { return this; } - virtual std::string title () const; - virtual int order () const { return 20; } - void apply (lay::Plugin *root); - void setup (lay::Plugin *root); + virtual int order () const { return 30; } + void apply (lay::Dispatcher *root); + void setup (lay::Dispatcher *root); public slots: void type_changed (int); @@ -190,7 +131,7 @@ private: * @brief The instance properties page */ class EditorOptionsInst - : public QWidget, public EditorOptionsPage + : public lay::EditorOptionsPage { Q_OBJECT @@ -198,25 +139,49 @@ public: EditorOptionsInst (lay::Dispatcher *root); ~EditorOptionsInst (); - virtual QWidget *q_frame () { return this; } - virtual std::string title () const; virtual int order () const { return 20; } - void apply (lay::Plugin *root); - void setup (lay::Plugin *root); + void apply (lay::Dispatcher *root); + void setup (lay::Dispatcher *root); -public slots: +private slots: void array_changed (); void browse_cell (); - void update_pcell_parameters (); - void library_changed (int index); - void cell_name_changed (const QString &s); + void library_changed (); + void update_cell_edits (); private: Ui::EditorOptionsInst *mp_ui; - lay::Dispatcher *mp_root; edt::PCellParametersPage *mp_pcell_parameters; int m_cv_index; +}; + +/** + * @brief The instance properties page (PCell parameters) + */ +class EditorOptionsInstPCellParam + : public lay::EditorOptionsPage +{ +Q_OBJECT + +public: + EditorOptionsInstPCellParam (lay::Dispatcher *root); + ~EditorOptionsInstPCellParam (); + + virtual std::string title () const; + virtual int order () const { return 21; } + void apply (lay::Dispatcher *root); + void setup (lay::Dispatcher *root); + +private slots: + void update_pcell_parameters (); + +private: + Ui::EditorOptionsInstPCellParam *mp_ui; + edt::PCellParametersPage *mp_pcell_parameters; + QLabel *mp_placeholder_label; + int m_cv_index; + std::string m_lib_name, m_cell_name; void update_pcell_parameters (const std::vector ¶meters); }; diff --git a/src/edt/edt/edtInstPropertiesPage.cc b/src/edt/edt/edtInstPropertiesPage.cc index 6f62c5654..5266ff485 100644 --- a/src/edt/edt/edtInstPropertiesPage.cc +++ b/src/edt/edt/edtInstPropertiesPage.cc @@ -31,6 +31,7 @@ #include "layObjectInstPath.h" #include "layLayoutView.h" #include "layCellSelectionForm.h" +#include "layQtTools.h" #include "tlExceptions.h" #include "tlString.h" @@ -65,6 +66,43 @@ InstPropertiesPage::InstPropertiesPage (edt::Service *service, db::Manager *mana connect (lib_cbx, SIGNAL (currentIndexChanged (int)), this, SLOT (library_changed (int))); connect (cell_name_le, SIGNAL (textChanged (const QString &)), this, SLOT (cell_name_changed (const QString &))); + if (! readonly ()) { + + connect (lib_cbx, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (cell_name_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (array_grp, SIGNAL (clicked ()), this, SIGNAL (edited ())); + connect (rows_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (columns_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (row_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (row_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (column_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (column_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (pos_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (pos_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (angle_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (mag_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (mirror_cbx, SIGNAL (clicked ()), this, SIGNAL (edited ())); + + } else { + + browse_pb->setEnabled (false); + cell_name_le->setReadOnly (true); + rows_le->setReadOnly (true); + columns_le->setReadOnly (true); + row_x_le->setReadOnly (true); + row_y_le->setReadOnly (true); + column_x_le->setReadOnly (true); + column_y_le->setReadOnly (true); + pos_x_le->setReadOnly (true); + pos_y_le->setReadOnly (true); + angle_le->setReadOnly (true); + mag_le->setReadOnly (true); + lib_cbx->setEnabled (false); + array_grp->setEnabled (false); + mirror_cbx->setEnabled (false); + + } + QHBoxLayout *layout = new QHBoxLayout (pcell_tab); layout->setMargin (0); pcell_tab->setLayout (layout); @@ -125,12 +163,19 @@ BEGIN_PROTECTED } if (form.exec ()) { + + cell_name_le->blockSignals (true); if (form.selected_cell_is_pcell ()) { cell_name_le->setText (tl::to_qstring (layout->pcell_header (form.selected_pcell_id ())->get_name ())); } else if (layout->is_valid_cell_index (form.selected_cell_index ())) { cell_name_le->setText (tl::to_qstring (layout->cell_name (form.selected_cell_index ()))); } + cell_name_le->blockSignals (false); + update_pcell_parameters (); + + emit edited (); + } END_PROTECTED @@ -140,7 +185,9 @@ void InstPropertiesPage::show_props () { lay::UserPropertiesForm props_form (this); - props_form.show (mp_service->view (), m_selection_ptrs [m_index]->cv_index (), m_prop_id); + if (props_form.show (mp_service->view (), m_selection_ptrs [m_index]->cv_index (), m_prop_id)) { + emit edited (); + } } void @@ -222,6 +269,7 @@ InstPropertiesPage::update () db::cell_index_type def_cell_index = pos->back ().inst_ptr.cell_index (); const db::Cell &def_cell = def_layout->cell (def_cell_index); + lib_cbx->blockSignals (true); std::pair dl = def_layout->defining_library (def_cell_index); lib_cbx->set_technology_filter (cv->tech_name (), true); lib_cbx->set_current_library (dl.first); @@ -229,13 +277,16 @@ InstPropertiesPage::update () def_layout = &dl.first->layout (); def_cell_index = dl.second; } + lib_cbx->blockSignals (false); std::pair pci = def_layout->is_pcell_instance (def_cell_index); + cell_name_le->blockSignals (true); if (pci.first && def_layout->pcell_declaration (pci.second)) { cell_name_le->setText (tl::to_qstring (def_layout->pcell_header (pci.second)->get_name ())); } else { cell_name_le->setText (tl::to_qstring (def_layout->cell_name (def_cell_index))); } + cell_name_le->blockSignals (false); db::Vector rowv, columnv; unsigned long rows, columns; @@ -325,6 +376,9 @@ InstPropertiesPage::readonly () ChangeApplicator * InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance & /*inst*/, double dbu) { + bool has_error = false; + bool has_pcell_error = false; + std::auto_ptr appl (new CombinedChangeApplicator ()); edt::Service::obj_iterator pos = m_selection_ptrs [m_index]; @@ -343,34 +397,69 @@ InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance & layout = &cv->layout (); } - std::pair ci = layout->cell_by_name (tl::to_string (cell_name_le->text ()).c_str ()); - std::pair pci = layout->pcell_by_name (tl::to_string (cell_name_le->text ()).c_str ()); - if (! ci.first && ! pci.first) { - throw tl::Exception (tl::to_string (QObject::tr ("Not a valid cell name: %s")).c_str (), tl::to_string (cell_name_le->text ()).c_str ()); + try { + + std::pair ci = layout->cell_by_name (tl::to_string (cell_name_le->text ()).c_str ()); + std::pair pci = layout->pcell_by_name (tl::to_string (cell_name_le->text ()).c_str ()); + if (! ci.first && ! pci.first) { + throw tl::Exception (tl::to_string (QObject::tr ("Not a valid cell or PCell name: %s")).c_str (), tl::to_string (cell_name_le->text ()).c_str ()); + } + + lay::indicate_error (cell_name_le, 0); + + } catch (tl::Exception &ex) { + lay::indicate_error (cell_name_le, &ex); + has_error = true; } - db::cell_index_type inst_cell_index = ci.second; + try { - // instantiate the PCell - if (pci.first) { - tl_assert (mp_pcell_parameters != 0); - tl_assert (layout->pcell_declaration (pci.second) == mp_pcell_parameters->pcell_decl ()); - inst_cell_index = layout->get_pcell_variant (pci.second, mp_pcell_parameters->get_parameters ()); - } + std::pair ci = layout->cell_by_name (tl::to_string (cell_name_le->text ()).c_str ()); + std::pair pci = layout->pcell_by_name (tl::to_string (cell_name_le->text ()).c_str ()); + tl_assert (ci.first || pci.first); - // reference the library - if (lib) { - layout = & cv->layout (); - inst_cell_index = layout->get_lib_proxy (lib, inst_cell_index); - } + db::cell_index_type inst_cell_index = 0; - if (inst_cell_index != pos->back ().inst_ptr.cell_index ()) { - appl->add (new ChangeTargetCellApplicator (inst_cell_index)); + // instantiate the PCell + if (pci.first) { + tl_assert (mp_pcell_parameters != 0); + tl_assert (layout->pcell_declaration (pci.second) == mp_pcell_parameters->pcell_decl ()); + inst_cell_index = layout->get_pcell_variant (pci.second, mp_pcell_parameters->get_parameters ()); + } else { + inst_cell_index = ci.second; + } + + // reference the library + if (lib) { + layout = & cv->layout (); + inst_cell_index = layout->get_lib_proxy (lib, inst_cell_index); + } + + if (inst_cell_index != pos->back ().inst_ptr.cell_index ()) { + appl->add (new ChangeTargetCellApplicator (inst_cell_index)); + } + + } catch (tl::Exception &ex) { + has_pcell_error = true; } double x = 0.0, y = 0.0; - tl::from_string (tl::to_string (pos_x_le->text ()), x); - tl::from_string (tl::to_string (pos_y_le->text ()), y); + + try { + tl::from_string (tl::to_string (pos_x_le->text ()), x); + lay::indicate_error (pos_x_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (pos_x_le, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (pos_y_le->text ()), y); + lay::indicate_error (pos_y_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (pos_y_le, &ex); + has_error = true; + } db::DCplxTrans t; if (abs_cb->isChecked ()) { @@ -381,20 +470,32 @@ InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance & bool mirror = mirror_cbx->isChecked (); double angle = 0.0; - tl::from_string (tl::to_string (angle_le->text ()), angle); + try { + tl::from_string (tl::to_string (angle_le->text ()), angle); + lay::indicate_error (angle_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (angle_le, &ex); + has_error = true; + } double mag = 0.0; - tl::from_string (tl::to_string (mag_le->text ()), mag); + try { + tl::from_string (tl::to_string (mag_le->text ()), mag); + lay::indicate_error (mag_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (mag_le, &ex); + has_error = true; + } angle -= (floor (angle / 360.0) + 1.0) * 360.0; while (angle < -1e-6) { angle += 360.0; } - db::CellInstArray::complex_trans_type tr = pos->back ().inst_ptr.complex_trans (); + db::CellInstArray::complex_trans_type trans = pos->back ().inst_ptr.complex_trans (); - if (fabs (angle - tr.angle ()) > 1e-6 || mirror != tr.is_mirror () || fabs (mag - tr.mag ()) > 1e-6 || ! disp.equal (tr.disp () * dbu)) { - appl->add (new ChangeInstanceTransApplicator (angle, tr.angle (), mirror, tr.is_mirror (), mag, tr.mag (), disp, tr.disp () * dbu)); + if (fabs (angle - trans.angle ()) > 1e-6 || mirror != trans.is_mirror () || fabs (mag - trans.mag ()) > 1e-6 || ! disp.equal (trans.disp () * dbu)) { + appl->add (new ChangeInstanceTransApplicator (angle, trans.angle (), mirror, trans.is_mirror (), mag, trans.mag (), disp, trans.disp () * dbu)); } db::CellInstArray::vector_type a_org, b_org; @@ -407,12 +508,53 @@ InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance & double rx = 0.0, ry = 0.0; unsigned long rows = 0, cols = 0; - tl::from_string (tl::to_string (column_x_le->text ()), cx); - tl::from_string (tl::to_string (column_y_le->text ()), cy); - tl::from_string (tl::to_string (row_x_le->text ()), rx); - tl::from_string (tl::to_string (row_y_le->text ()), ry); - tl::from_string (tl::to_string (rows_le->text ()), rows); - tl::from_string (tl::to_string (columns_le->text ()), cols); + try { + tl::from_string (tl::to_string (column_x_le->text ()), cx); + lay::indicate_error (column_x_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (column_x_le, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (column_y_le->text ()), cy); + lay::indicate_error (column_y_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (column_y_le, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (row_x_le->text ()), rx); + lay::indicate_error (row_x_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (row_x_le, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (row_y_le->text ()), ry); + lay::indicate_error (row_y_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (row_y_le, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (rows_le->text ()), rows); + lay::indicate_error (rows_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (rows_le, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (columns_le->text ()), cols); + lay::indicate_error (columns_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (columns_le, &ex); + has_error = true; + } db::DVector rv = db::DVector (dpoint_from_dpoint (db::DPoint (rx, ry), dbu, du, t)); db::DVector cv = db::DVector (dpoint_from_dpoint (db::DPoint (cx, cy), dbu, du, t)); @@ -432,6 +574,14 @@ InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance & } + if (has_error || has_pcell_error) { + throw tl::Exception (tl::to_string (tr ("At least one value and PCell parameter is not correct - see hightlighted entry fields or the PCell error indicator"))); + } else if (has_error) { + throw tl::Exception (tl::to_string (tr ("At least one value is not correct - see hightlighted entry fields"))); + } else if (has_pcell_error) { + throw tl::Exception (tl::to_string (tr ("At least one PCell parameter is not correct - see hightlighted entry fields or the PCell error indicator"))); + } + return appl.release (); } @@ -453,7 +603,7 @@ InstPropertiesPage::recompute_selection_ptrs (const std::vectorview ()->layer_snapshot (); unsigned int cv_index = m_selection_ptrs [m_index]->cv_index (); @@ -482,32 +632,7 @@ InstPropertiesPage::do_apply (bool current_only) bool relative_mode = false; if (! current_only && applicator->supports_relative_mode ()) { - - static bool s_relative_mode = true; - - QMessageBox mb (QMessageBox::Question, - tr ("Apply Changes To All"), - tr ("For this operation absolute or relative mode is available which affects the way parameters of the selected objects are changed:\n\n" - "In absolute mode, they will be set to the given value. In relative mode, they will be adjusted by the same amount.\n"), - QMessageBox::NoButton, this); - - mb.addButton (tr ("Cancel"), QMessageBox::RejectRole); - QPushButton *absolute = mb.addButton (tr ("Absolute"), QMessageBox::NoRole); - QPushButton *relative = mb.addButton (tr ("Relative"), QMessageBox::YesRole); - - mb.setDefaultButton (s_relative_mode ? relative : absolute); - - mb.exec (); - - if (mb.clickedButton () == absolute) { - s_relative_mode = relative_mode = false; - } else if (mb.clickedButton () == relative) { - s_relative_mode = relative_mode = true; - } else { - // Cancel pressed - return; - } - + relative_mode = relative; } // Note: using the apply-all scheme for applying a single change may look like overhead. @@ -599,7 +724,7 @@ InstPropertiesPage::do_apply (bool current_only) void InstPropertiesPage::apply () { - do_apply (true); + do_apply (true, false); } bool @@ -609,9 +734,9 @@ InstPropertiesPage::can_apply_to_all () const } void -InstPropertiesPage::apply_to_all () +InstPropertiesPage::apply_to_all (bool relative) { - do_apply (false); + do_apply (false, relative); } void @@ -636,16 +761,13 @@ InstPropertiesPage::update_pcell_parameters () std::pair pc = layout->pcell_by_name (tl::to_string (cell_name_le->text ()).c_str ()); std::pair cc = layout->cell_by_name (tl::to_string (cell_name_le->text ()).c_str ()); - // by the way, update the foreground color of the cell edit box as well (red, if not valid) - QPalette pl = cell_name_le->palette (); + // indicate an invalid cell name if (! pc.first && ! cc.first) { - pl.setColor (QPalette::Text, Qt::red); - pl.setColor (QPalette::Base, QColor (Qt::red).lighter (180)); + tl::Exception ex (tl::to_string (QObject::tr ("Not a valid cell or PCell name: %s")).c_str (), tl::to_string (cell_name_le->text ()).c_str ()); + lay::indicate_error (cell_name_le, &ex); } else { - pl.setColor (QPalette::Text, palette ().color (QPalette::Text)); - pl.setColor (QPalette::Base, palette ().color (QPalette::Base)); + lay::indicate_error (cell_name_le, 0); } - cell_name_le->setPalette (pl); if (pc.first && layout->pcell_declaration (pc.second)) { @@ -686,13 +808,14 @@ InstPropertiesPage::update_pcell_parameters () mp_pcell_parameters->deleteLater (); } - mp_pcell_parameters = new PCellParametersPage (pcell_tab, &cv->layout (), mp_service->view (), pos->cv_index (), layout->pcell_declaration (pc.second), parameters); + mp_pcell_parameters = new PCellParametersPage (pcell_tab); + connect (mp_pcell_parameters, SIGNAL (edited ()), this, SIGNAL (edited ())); + mp_pcell_parameters->setup (&cv->layout (), mp_service->view (), pos->cv_index (), layout->pcell_declaration (pc.second), parameters); pcell_tab->layout ()->addWidget (mp_pcell_parameters); } param_tab_widget->setTabEnabled (1, true); - param_tab_widget->setCurrentIndex (1); } else { @@ -704,8 +827,11 @@ InstPropertiesPage::update_pcell_parameters () mp_pcell_parameters = 0; - param_tab_widget->setCurrentIndex (0); + if (param_tab_widget->currentIndex () == 1) { + param_tab_widget->setCurrentIndex (0); + } param_tab_widget->setTabEnabled (1, false); + } } diff --git a/src/edt/edt/edtInstPropertiesPage.h b/src/edt/edt/edtInstPropertiesPage.h index f648482dc..28394f4b3 100644 --- a/src/edt/edt/edtInstPropertiesPage.h +++ b/src/edt/edt/edtInstPropertiesPage.h @@ -68,9 +68,9 @@ protected: virtual bool readonly (); virtual void apply (); - virtual void apply_to_all (); + virtual void apply_to_all (bool relative); virtual bool can_apply_to_all () const; - void do_apply (bool current_only); + void do_apply (bool current_only, bool relative); virtual ChangeApplicator *create_applicator (db::Cell &cell, const db::Instance &inst, double dbu); protected slots: diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index a5068438c..29984d719 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -30,7 +30,11 @@ #include "tlExceptions.h" #include "layLayoutView.h" #include "layDialogs.h" +#include "laySelector.h" #include "layCellSelectionForm.h" +#include "layFinder.h" +#include "layLayerProperties.h" +#include "layLayerTreeModel.h" #include "tlProgress.h" #include "edtPlugin.h" #include "edtMainService.h" @@ -39,8 +43,11 @@ #include "edtConfig.h" #include "edtDialogs.h" #include "edtEditorOptionsPages.h" +#include "edtDistribute.h" #include +#include +#include namespace edt { @@ -58,12 +65,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) @@ -94,6 +107,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 () { @@ -128,10 +150,10 @@ MainService::menu_activated (const std::string &symbol) cm_descend (); } else if (symbol == "edt::ascend") { cm_ascend (); - } else if (symbol == "edt::edit_options") { - cm_edit_options (); } else if (symbol == "edt::sel_align") { cm_align (); + } else if (symbol == "edt::sel_distribute") { + cm_distribute (); } else if (symbol == "edt::sel_tap") { cm_tap (); } else if (symbol == "edt::sel_round_corners") { @@ -1835,7 +1857,115 @@ 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_distribute_visible_layers)) { + return; + } + + if (! m_hdistribute && ! m_vdistribute) { + return; + } + + // count the items + size_t n = 0; + for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { + for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) { + ++n; + } + } + + std::vector > objects_for_service; + std::vector transformations; + + { + + std::vector org_boxes; + org_boxes.reserve (n); + + edt::distributed_placer placer; + placer.reserve (n); + + size_t i = 0; + + for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { + + objects_for_service.push_back (std::make_pair (i, i)); + + for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) { + + const db::Layout &layout = view ()->cellview (s->cv_index ())->layout (); + db::CplxTrans tr = db::CplxTrans (layout.dbu ()) * s->trans (); + + db::DBox box; + if (! s->is_cell_inst ()) { + box = tr * s->shape ().bbox (); + } else { + box = inst_bbox (tr, view (), s->cv_index (), s->back (), m_distribute_visible_layers); + } + + org_boxes.push_back (box); + placer.insert (box, i); + + ++i; + + } + + objects_for_service.back ().second = i; + + } + + int href = int (m_distribute_hmode - 2); + int vref = 2 - int (m_distribute_vmode); + + if (m_hdistribute && m_vdistribute) { + placer.distribute_matrix (href, m_distribute_hpitch, m_distribute_hspace, + vref, m_distribute_vpitch, m_distribute_vspace); + } else if (m_hdistribute) { + placer.distribute_h (href, vref, m_distribute_hpitch, m_distribute_hspace); + } else if (m_vdistribute) { + placer.distribute_v (vref, href, m_distribute_vpitch, m_distribute_vspace); + } + + transformations.resize (org_boxes.size ()); + + for (edt::distributed_placer::iterator i = placer.begin (); i != placer.end (); ++i) { + transformations[i->second] = db::DCplxTrans (i->first.p1 () - org_boxes[i->second].p1 ()); + } + + } + + { + view ()->cancel_edits (); + manager ()->transaction (tl::to_string (QObject::tr ("Distribution"))); + + // do the distribution + for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { + + size_t ie = es - edt_services.begin (); + + // create a transformation vector that describes each shape's transformation + std::vector tv (transformations.begin () + objects_for_service [ie].first, transformations.begin () + objects_for_service [ie].second); + + // use the "transform" method to transform the shapes and instances (with individual transformations) + (*es)->transform (db::DCplxTrans () /*dummy*/, &tv); + + } + + manager ()->commit (); + + } +} + +void MainService::cm_make_array () { size_t n = 0; @@ -1941,21 +2071,67 @@ MainService::cm_make_array () void MainService::cm_tap () { - tl_assert (view ()->is_editable ()); - check_no_guiding_shapes (); + if (! view ()->view_object_widget ()->mouse_in_window ()) { + return; + } - std::vector edt_services = view ()->get_plugins (); + lay::ShapeFinder finder (true /*point mode*/, false /*all hierarchy levels*/, db::ShapeIterator::All, 0); - // get (common) cellview index of the selected shapes - for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { - for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) { - const lay::CellView &cv = view ()->cellview (s->cv_index ()); - if (cv.is_valid () && ! s->is_cell_inst ()) { - view ()->set_current_layer (s->cv_index (), cv->layout ().get_properties (s->layer ())); - return; - } + // capture all objects in point mode (default: capture one only) + finder.set_catch_all (true); + + // go through all visible layers of all cellviews + db::DPoint pt = view ()->view_object_widget ()->mouse_position_um (); + finder.find (view (), db::DBox (pt, pt)); + + std::set > layers_in_selection; + + for (lay::ShapeFinder::iterator f = finder.begin (); f != finder.end (); ++f) { + // ignore guiding shapes + if (f->layer () != view ()->cellview (f->cv_index ())->layout ().guiding_shape_layer ()) { + layers_in_selection.insert (std::make_pair (f->cv_index (), f->layer ())); } } + + std::vector tapped_layers; + for (lay::LayerPropertiesConstIterator lp = view ()->begin_layers (view ()->current_layer_list ()); ! lp.at_end (); ++lp) { + const lay::LayerPropertiesNode *ln = lp.operator-> (); + if (layers_in_selection.find (std::make_pair ((unsigned int) ln->cellview_index (), (unsigned int) ln->layer_index ())) != layers_in_selection.end ()) { + tapped_layers.push_back (lp); + } + } + + if (tapped_layers.empty ()) { + return; + } + + // List the layers under the cursor as pop up a menu + + std::auto_ptr menu (new QMenu (view ())); + menu->show (); + + int icon_size = menu->style ()->pixelMetric (QStyle::PM_ButtonIconSize); + + QPoint mp = view ()->view_object_widget ()->mapToGlobal (view ()->view_object_widget ()->mouse_position ()); + + for (std::vector::const_iterator l = tapped_layers.begin (); l != tapped_layers.end (); ++l) { + QAction *a = menu->addAction (lay::LayerTreeModel::icon_for_layer (*l, view (), icon_size, icon_size, 0, true), tl::to_qstring ((*l)->source (true).to_string ())); + a->setData (int (l - tapped_layers.begin ())); + } + + QAction *action = menu->exec (mp); + if (action) { + + int index = action->data ().toInt (); + lay::LayerPropertiesConstIterator iter = tapped_layers [index]; + view ()->set_current_layer (iter); + + edt::Service *es = dynamic_cast (view ()->view_object_widget ()->active_service ()); + if (es) { + es->tap (pt); + } + + } } void @@ -1966,16 +2142,12 @@ MainService::cm_change_layer () int cv_index = -1; - std::vector edt_services = view ()->get_plugins (); - // get (common) cellview index of the selected shapes - for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { - for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) { - if (cv_index >= 0 && cv_index != int (s->cv_index ())) { - throw tl::Exception (tl::to_string (QObject::tr ("Selections originate from different layouts - cannot switch layer in this case."))); - } - cv_index = int (s->cv_index ()); + for (SelectionIterator s (view ()); ! s.at_end (); ++s) { + if (cv_index >= 0 && cv_index != int (s->cv_index ())) { + throw tl::Exception (tl::to_string (QObject::tr ("Selections originate from different layouts - cannot switch layer in this case."))); } + cv_index = int (s->cv_index ()); } if (cv_index >= 0) { @@ -2037,46 +2209,42 @@ MainService::cm_change_layer () // Insert and delete the shape. This exploits the fact, that a shape can be erased multiple times - // this is important since the selection potentially contains the same shape multiple times. - for (std::vector::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) { + for (SelectionIterator s (view ()); ! s.at_end (); ++s) { - for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) { + if (!s->is_cell_inst () && int (s->layer ()) != layer) { - if (!s->is_cell_inst () && int (s->layer ()) != layer) { + db::Cell &cell = layout.cell (s->cell_index ()); + if (cell.shapes (s->layer ()).is_valid (s->shape ())) { + cell.shapes (layer).insert (s->shape ()); + cell.shapes (s->layer ()).erase_shape (s->shape ()); + } - db::Cell &cell = layout.cell (s->cell_index ()); - if (cell.shapes (s->layer ()).is_valid (s->shape ())) { - cell.shapes (layer).insert (s->shape ()); - cell.shapes (s->layer ()).erase_shape (s->shape ()); - } + } else if (s->is_cell_inst ()) { - } else if (s->is_cell_inst ()) { + // If the selected object is a PCell instance, and there is exactly one visible, writable layer type parameter, change this one - // If the selected object is a PCell instance, and there is exactly one visible, writable layer type parameter, change this one + db::Instance inst = s->back ().inst_ptr; + db::Cell &cell = layout.cell (s->cell_index ()); - db::Instance inst = s->back ().inst_ptr; - db::Cell &cell = layout.cell (s->cell_index ()); + if (cell.is_valid (inst)) { - if (cell.is_valid (inst)) { + const db::PCellDeclaration *pcell_decl = layout.pcell_declaration_for_pcell_variant (inst.cell_index ()); + if (pcell_decl) { - const db::PCellDeclaration *pcell_decl = layout.pcell_declaration_for_pcell_variant (inst.cell_index ()); - if (pcell_decl) { - - size_t layer_par_index = 0; - int n_layer_par = 0; - for (std::vector::const_iterator d = pcell_decl->parameter_declarations ().begin (); d != pcell_decl->parameter_declarations ().end () && n_layer_par < 2; ++d) { - if (d->get_type () == db::PCellParameterDeclaration::t_layer && !d->is_hidden () && !d->is_readonly ()) { - ++n_layer_par; - layer_par_index = size_t (d - pcell_decl->parameter_declarations ().begin ()); - } - } - - if (n_layer_par == 1) { - std::vector parameters = cell.get_pcell_parameters (inst); - tl_assert (layer_par_index < parameters.size ()); - parameters [layer_par_index] = layout.get_properties (layer); - cell.change_pcell_parameters (inst, parameters); + size_t layer_par_index = 0; + int n_layer_par = 0; + for (std::vector::const_iterator d = pcell_decl->parameter_declarations ().begin (); d != pcell_decl->parameter_declarations ().end () && n_layer_par < 2; ++d) { + if (d->get_type () == db::PCellParameterDeclaration::t_layer && !d->is_hidden () && !d->is_readonly ()) { + ++n_layer_par; + layer_par_index = size_t (d - pcell_decl->parameter_declarations ().begin ()); } + } + if (n_layer_par == 1) { + std::vector parameters = cell.get_pcell_parameters (inst); + tl_assert (layer_par_index < parameters.size ()); + parameters [layer_par_index] = layout.get_properties (layer); + cell.change_pcell_parameters (inst, parameters); } } @@ -2101,12 +2269,6 @@ MainService::cm_change_layer () } -void -MainService::cm_edit_options () -{ - show_editor_options_dialog (); -} - void MainService::check_no_guiding_shapes () { diff --git a/src/edt/edt/edtMainService.h b/src/edt/edt/edtMainService.h index 743d8a9f4..c2f5c97ec 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; // ------------------------------------------------------------- @@ -93,11 +94,6 @@ public: */ void cm_ascend (); - /** - * @brief Edit object options - */ - void cm_edit_options (); - /** * @brief Change the layer of the shapes in the selection */ @@ -153,6 +149,11 @@ public: */ void cm_align (); + /** + * @brief Distribute the selected shapes and instances + */ + void cm_distribute (); + /** * @brief Flatten instances */ @@ -205,6 +206,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 +223,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 +232,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/edtPCellParametersDialog.cc b/src/edt/edt/edtPCellParametersDialog.cc deleted file mode 100644 index bdb33b47f..000000000 --- a/src/edt/edt/edtPCellParametersDialog.cc +++ /dev/null @@ -1,65 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2020 Matthias Koefferlein - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - - -#include "edtPCellParametersDialog.h" - -#include - -namespace edt -{ - -PCellParametersDialog::PCellParametersDialog (QWidget *parent) - : QDialog (parent) -{ - setupUi (this); - - connect (buttons->button (QDialogButtonBox::Apply), SIGNAL (clicked ()), this, SLOT (apply_pressed ())); -} - -void -PCellParametersDialog::apply_pressed () -{ - emit parameters_changed (); - parameters_changed_event (); -} - -std::vector -PCellParametersDialog::get_parameters () -{ - return parameters->get_parameters (); -} - -void -PCellParametersDialog::set_parameters (const std::vector &p) -{ - parameters->set_parameters (p); -} - -int -PCellParametersDialog::exec (const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type &p) -{ - parameters->setup (layout, view, cv_index, pcell_decl, p); - return QDialog::exec (); -} - -} diff --git a/src/edt/edt/edtPCellParametersDialog.h b/src/edt/edt/edtPCellParametersDialog.h deleted file mode 100644 index 2a977a4cd..000000000 --- a/src/edt/edt/edtPCellParametersDialog.h +++ /dev/null @@ -1,86 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2020 Matthias Koefferlein - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - - -#ifndef HDR_edtPCellParametersDialog -#define HDR_edtPCellParametersDialog - -#include "dbPCellDeclaration.h" -#include "ui_PCellParametersDialog.h" - -#include - -namespace lay -{ - class LayoutView; -} - -namespace edt -{ - -/** - * @brief A QScrollArea that displays and allows editing PCell parameters - */ -class PCellParametersDialog - : public QDialog, private Ui::PCellParametersDialog -{ -Q_OBJECT - -public: - /** - * @brief Constructor: create a dialog showing the given parameters - * @param parent The parent widget - */ - PCellParametersDialog (QWidget *parent); - - /** - * @brief Executes the parameter dialog - * @param layout The layout in which the PCell instance resides - * @param view The layout view from which to take layers for example - * @param cv_index The index of the cellview in "view" - * @param pcell_decl The PCell declaration - * @param parameters The parameter values to show (if empty, the default values are used) - */ - int exec (const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type &p); - - /** - * @brief Get the current parameters - */ - std::vector get_parameters (); - - /** - * @brief Sets the given parameters as values - */ - void set_parameters (const std::vector &values); - - tl::Event parameters_changed_event; - -signals: - void parameters_changed (); - -private slots: - void apply_pressed (); -}; - -} - -#endif diff --git a/src/edt/edt/edtPCellParametersPage.cc b/src/edt/edt/edtPCellParametersPage.cc index 87ab6d3e4..01b667002 100644 --- a/src/edt/edt/edtPCellParametersPage.cc +++ b/src/edt/edt/edtPCellParametersPage.cc @@ -22,7 +22,9 @@ #include "edtPCellParametersPage.h" +#include "edtPropertiesPageUtils.h" #include "layWidgets.h" +#include "layQtTools.h" #include "tlScriptError.h" #include @@ -142,15 +144,8 @@ static void set_value (const db::PCellParameterDeclaration &p, const db::Layout } } -PCellParametersPage::PCellParametersPage (QWidget *parent, const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type ¶meters) - : QFrame (parent) -{ - init (); - setup (layout, view, cv_index, pcell_decl, parameters); -} - -PCellParametersPage::PCellParametersPage (QWidget *parent) - : QFrame (parent) +PCellParametersPage::PCellParametersPage (QWidget *parent, bool dense) + : QFrame (parent), m_dense (dense), dm_parameter_changed (this, &PCellParametersPage::do_parameter_changed) { init (); } @@ -165,6 +160,8 @@ PCellParametersPage::init () mp_parameters_area = 0; QGridLayout *frame_layout = new QGridLayout (this); + // spacing and margin for tool windows + frame_layout->setMargin (0); setLayout (frame_layout); mp_error_icon = new QLabel (this); @@ -173,6 +170,7 @@ PCellParametersPage::init () frame_layout->addWidget (mp_error_icon, 1, 0, 1, 1); mp_error_label = new QLabel (this); + mp_error_label->setWordWrap (true); QPalette palette = mp_error_label->palette (); palette.setColor (QPalette::Foreground, Qt::red); mp_error_label->setPalette (palette); @@ -200,6 +198,7 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int m_widgets.clear (); mp_parameters_area = new QScrollArea (this); + mp_parameters_area->setFrameShape (QFrame::NoFrame); QGridLayout *frame_layout = dynamic_cast (QFrame::layout ()); frame_layout->addWidget (mp_parameters_area, 0, 0, 1, 2); frame_layout->setRowStretch (0, 1); @@ -211,6 +210,11 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int QGridLayout *inner_grid = new QGridLayout (inner_frame); inner_frame->setLayout (inner_grid); + if (m_dense) { + inner_grid->setMargin (4); + inner_grid->setHorizontalSpacing (6); + inner_grid->setVerticalSpacing (2); + } QWidget *main_frame = inner_frame; QGridLayout *main_grid = inner_grid; @@ -247,6 +251,11 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int main_grid->addWidget (gb, main_row, 0, 1, 2); inner_grid = new QGridLayout (gb); + if (m_dense) { + inner_grid->setMargin (4); + inner_grid->setHorizontalSpacing (6); + inner_grid->setVerticalSpacing (2); + } gb->setLayout (inner_grid); inner_frame = gb; @@ -286,11 +295,13 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int QHBoxLayout *hb = new QHBoxLayout (f); hb->setMargin (0); f->setLayout (hb); + f->setFrameShape (QFrame::NoFrame); QLineEdit *le = new QLineEdit (f); le->setEnabled (! p->is_readonly ()); hb->addWidget (le); le->setMaximumWidth (150); + le->setObjectName (tl::to_qstring (p->get_name ())); m_widgets.push_back (le); QLabel *ul = new QLabel (f); @@ -299,7 +310,7 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int inner_grid->addWidget (f, row, 1); - connect (le, SIGNAL (editingFinished ()), this, SLOT (activated ())); + connect (le, SIGNAL (editingFinished ()), this, SLOT (parameter_changed ())); } break; @@ -309,10 +320,11 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int { QLineEdit *le = new QLineEdit (inner_frame); le->setEnabled (! p->is_readonly ()); + le->setObjectName (tl::to_qstring (p->get_name ())); m_widgets.push_back (le); inner_grid->addWidget (le, row, 1); - connect (le, SIGNAL (editingFinished ()), this, SLOT (activated ())); + connect (le, SIGNAL (editingFinished ()), this, SLOT (parameter_changed ())); } break; @@ -322,19 +334,25 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int ly->setEnabled (! p->is_readonly ()); ly->set_no_layer_available (true); ly->set_view (mp_view, m_cv_index, true /*all layers*/); + ly->setObjectName (tl::to_qstring (p->get_name ())); m_widgets.push_back (ly); inner_grid->addWidget (ly, row, 1); + + connect (ly, SIGNAL (activated (int)), this, SLOT (parameter_changed ())); } break; case db::PCellParameterDeclaration::t_boolean: { QCheckBox *cbx = new QCheckBox (inner_frame); + // this makes the checkbox not stretch over the full width - better when navigating with tab + cbx->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Preferred)); cbx->setEnabled (! p->is_readonly ()); + cbx->setObjectName (tl::to_qstring (p->get_name ())); m_widgets.push_back (cbx); inner_grid->addWidget (cbx, row, 1); - connect (cbx, SIGNAL (stateChanged (int)), this, SLOT (activated ())); + connect (cbx, SIGNAL (stateChanged (int)), this, SLOT (parameter_changed ())); } break; @@ -346,6 +364,7 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int } else { QComboBox *cb = new QComboBox (inner_frame); + cb->setObjectName (tl::to_qstring (p->get_name ())); int i = 0; for (std::vector::const_iterator c = p->get_choices ().begin (); c != p->get_choices ().end (); ++c, ++i) { @@ -356,7 +375,8 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int } } - connect (cb, SIGNAL (activated (int)), this, SLOT (activated ())); + connect (cb, SIGNAL (activated (int)), this, SLOT (parameter_changed ())); + cb->setEnabled (! p->is_readonly ()); cb->setMinimumContentsLength (30); cb->setSizeAdjustPolicy (QComboBox::AdjustToMinimumContentsLengthWithIcon); @@ -377,17 +397,24 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int mp_parameters_area->setWidget (main_frame); main_frame->show (); - // does a first coerce and update - get_parameters (); + // does a first coerce and update. Ignore errors for now. + bool ok = false; + get_parameters (&ok); } PCellParametersPage::State PCellParametersPage::get_state () { State s; + s.valid = true; s.vScrollPosition = mp_parameters_area->verticalScrollBar ()->value (); s.hScrollPosition = mp_parameters_area->horizontalScrollBar ()->value (); + + if (focusWidget ()) { + s.focusWidget = focusWidget ()->objectName (); + } + return s; } @@ -395,29 +422,42 @@ void PCellParametersPage::set_state (const State &s) { if (s.valid) { + mp_parameters_area->verticalScrollBar ()->setValue (s.vScrollPosition); mp_parameters_area->horizontalScrollBar ()->setValue (s.hScrollPosition); + + if (! s.focusWidget.isEmpty ()) { + QWidget *c = findChild (s.focusWidget); + if (c) { + c->setFocus (); + } + } + } } void -PCellParametersPage::activated () +PCellParametersPage::parameter_changed () { - // does a coerce and update - get_parameters (); + dm_parameter_changed (); } -void -PCellParametersPage::clicked () +void +PCellParametersPage::do_parameter_changed () { // does a coerce and update - get_parameters (); + bool ok = false; + get_parameters (&ok); + if (ok) { + emit edited (); + } } std::vector -PCellParametersPage::get_parameters () +PCellParametersPage::get_parameters (bool *ok) { std::vector parameters; + bool edit_error = true; int r = 0; const std::vector &pcp = mp_pcell_decl->parameter_declarations (); @@ -443,9 +483,22 @@ PCellParametersPage::get_parameters () { QLineEdit *le = dynamic_cast (m_widgets [r]); if (le) { - int v = 0; - tl::from_string (tl::to_string (le->text ()), v); - parameters.back () = tl::Variant (v); + + try { + + int v = 0; + tl::from_string (tl::to_string (le->text ()), v); + + parameters.back () = tl::Variant (v); + lay::indicate_error (le, 0); + + } catch (tl::Exception &ex) { + + lay::indicate_error (le, &ex); + edit_error = false; + + } + } } break; @@ -454,9 +507,22 @@ PCellParametersPage::get_parameters () { QLineEdit *le = dynamic_cast (m_widgets [r]); if (le) { - double v = 0; - tl::from_string (tl::to_string (le->text ()), v); - parameters.back () = tl::Variant (v); + + try { + + double v = 0; + tl::from_string (tl::to_string (le->text ()), v); + + parameters.back () = tl::Variant (v); + lay::indicate_error (le, 0); + + } catch (tl::Exception &ex) { + + lay::indicate_error (le, &ex); + edit_error = false; + + } + } } break; @@ -516,22 +582,43 @@ PCellParametersPage::get_parameters () try { + if (! edit_error) { + throw tl::Exception (tl::to_string (tr ("There are errors. See the highlighted edit fields for details."))); + } + // coerce the parameters mp_pcell_decl->coerce_parameters (*mp_layout, parameters); set_parameters (parameters); + mp_error_label->hide (); + mp_error_icon->hide (); + + if (ok) { + *ok = true; + } + } catch (tl::ScriptError &ex) { - mp_error_label->setText (tl::to_qstring (ex.basic_msg ())); - mp_error_label->setToolTip (tl::to_qstring (ex.msg ())); - mp_error_icon->show (); - mp_error_label->show (); + if (ok) { + mp_error_label->setText (tl::to_qstring (ex.basic_msg ())); + mp_error_label->setToolTip (tl::to_qstring (ex.msg ())); + mp_error_icon->show (); + mp_error_label->show (); + *ok = false; + } else { + throw; + } } catch (tl::Exception &ex) { - mp_error_label->setText (tl::to_qstring (ex.msg ())); - mp_error_icon->show (); - mp_error_label->show (); + if (ok) { + mp_error_label->setText (tl::to_qstring (ex.msg ())); + mp_error_icon->show (); + mp_error_label->show (); + *ok = false; + } else { + throw; + } } @@ -539,7 +626,7 @@ PCellParametersPage::get_parameters () } void -PCellParametersPage::set_parameters (const std::vector ¶meters) +PCellParametersPage::set_parameters (const std::vector ¶meters) { // write the changed value back size_t r = 0; diff --git a/src/edt/edt/edtPCellParametersPage.h b/src/edt/edt/edtPCellParametersPage.h index a41a7c233..9786a2c8b 100644 --- a/src/edt/edt/edtPCellParametersPage.h +++ b/src/edt/edt/edtPCellParametersPage.h @@ -25,6 +25,7 @@ #define HDR_edtPCellParametersPage #include "dbPCellDeclaration.h" +#include "tlDeferredExecution.h" #include #include @@ -42,7 +43,7 @@ namespace edt * @brief A QScrollArea that displays and allows editing PCell parameters */ class PCellParametersPage - : public QFrame + : public QFrame, public tl::Object { Q_OBJECT @@ -54,31 +55,28 @@ public: bool valid; int hScrollPosition; int vScrollPosition; + QString focusWidget; }; - /** - * @brief Constructor: creates a page showing the given parameters - * - * @param parent The parent widget - * @param layout The layout in which the PCell instance resides - * @param view The layout view from which to take layers for example - * @param cv_index The index of the cellview in "view" - * @param pcell_decl The PCell declaration - * @param parameters The parameter values to show (if empty, the default values are used) - */ - PCellParametersPage (QWidget *parent, const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type ¶meters); - /** * @brief Default constructor * * Use "setup" to configure the page. + * + * @param dense Use a dense layout if true */ - PCellParametersPage (QWidget *parent); + PCellParametersPage (QWidget *parent, bool dense = false); /** - * @brief Delayed initialization + * @brief initialization * * Use this method to setup when the arguments are not available in the constructor + * + * @param layout The layout in which the PCell instance resides + * @param view The layout view from which to take layers for example + * @param cv_index The index of the cellview in "view" + * @param pcell_decl The PCell declaration + * @param parameters The parameter values to show (if empty, the default values are used) */ void setup (const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type ¶meters); @@ -94,8 +92,12 @@ public: /** * @brief Get the current parameters + * + * *ok is set to true, if there is no error. In case of an error it's set to false. + * The error is indicated in the error label in the editor page. + * If ok is null, an exception is thrown. */ - std::vector get_parameters (); + std::vector get_parameters (bool *ok = 0); /** * @brief Get the PCell declaration pointer @@ -110,10 +112,12 @@ public: */ void set_parameters (const std::vector &values); -public slots: - void activated (); - void clicked (); - +signals: + void edited (); + +private slots: + void parameter_changed (); + private: QScrollArea *mp_parameters_area; QLabel *mp_error_label; @@ -124,8 +128,11 @@ private: lay::LayoutView *mp_view; int m_cv_index; db::pcell_parameters_type m_parameters; + bool m_dense; + tl::DeferredMethod dm_parameter_changed; void init (); + void do_parameter_changed (); }; } diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index 63952ed28..dac20b299 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -32,6 +32,7 @@ #include "edtService.h" #include "edtConfig.h" #include "edtDialogs.h" +#include "edtPlugin.h" #include "edtEditorOptionsPages.h" #include @@ -1009,9 +1010,7 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, PartialService::PartialService (db::Manager *manager, lay::LayoutView *view, lay::Dispatcher *root) : QObject (), - lay::ViewService (view->view_object_widget ()), - lay::Editable (view), - lay::Plugin (view), + lay::EditorServiceBase (view), db::Object (manager), mp_view (view), mp_root (root), @@ -1069,7 +1068,7 @@ PartialService::deactivated () void PartialService::activated () { - // ... + // .. nothing yet .. } void @@ -1294,11 +1293,11 @@ PartialService::snap (const db::DVector &v) const const int sr_pixels = 8; // TODO: make variable -db::DPoint +lay::PointSnapToObjectResult PartialService::snap2 (const db::DPoint &p) const { double snap_range = widget ()->mouse_event_trans ().inverted ().ctrans (sr_pixels); - return lay::obj_snap (m_snap_to_objects ? view () : 0, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, snap_range).second; + return lay::obj_snap (m_snap_to_objects ? view () : 0, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, snap_range); } void @@ -1530,20 +1529,27 @@ PartialService::wheel_event (int /*delta*/, bool /*horizonal*/, const db::DPoint bool PartialService::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) { + clear_mouse_cursors (); + if (m_dragging) { set_cursor (lay::Cursor::size_all); m_alt_ac = ac_from_buttons (buttons); - // drag the vertex or edge/segment + lay::PointSnapToObjectResult snap_details; + + // drag the vertex or edge/segment if (is_single_point_selection ()) { - // for a single selected point, m_start is the original position and we snap the target - - // thus, we can bring the point on grid or to an object's edge or vertex - m_current = snap2 (p); + // for a single selected point, m_start is the original position and we snap the target - + // thus, we can bring the point on grid or to an object's edge or vertex + snap_details = snap2 (p); + m_current = snap_details.snapped_point; } else { m_current = m_start + snap (p - m_start); } + + mouse_cursor_from_snap_details (snap_details); selection_to_view (); m_alt_ac = lay::AC_Global; diff --git a/src/edt/edt/edtPartialService.h b/src/edt/edt/edtPartialService.h index 2c22ac21c..f38b994a0 100644 --- a/src/edt/edt/edtPartialService.h +++ b/src/edt/edt/edtPartialService.h @@ -25,10 +25,8 @@ #ifndef HDR_edtPartialService #define HDR_edtPartialService -#include "layEditable.h" +#include "layEditorServiceBase.h" #include "layObjectInstPath.h" -#include "layLayoutView.h" -#include "layPlugin.h" #include "layViewObject.h" #include "layRubberBox.h" #include "laySnap.h" @@ -140,9 +138,7 @@ struct EdgeWithIndex */ class PartialService : public QObject, - public lay::ViewService, - public lay::Editable, - public lay::Plugin, + public lay::EditorServiceBase, public db::Object { Q_OBJECT @@ -326,7 +322,7 @@ private: db::DPoint snap (const db::DPoint &p) const; db::DVector snap (const db::DVector &p) const; - db::DPoint snap2 (const db::DPoint &p) const; + lay::PointSnapToObjectResult snap2 (const db::DPoint &p) const; void enter_edge (const EdgeWithIndex &e, size_t &nmarker, partial_objects::const_iterator sel, const std::map &new_points, const std::map &new_edges, const db::ICplxTrans >, const std::vector &tv, bool transient); void enter_vertices (size_t &nmarker, partial_objects::const_iterator sel, const std::map &new_points, const std::map &new_edges, const db::ICplxTrans >, const std::vector &tv, bool transient); diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 53e7f2753..7caa8288b 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -22,6 +22,9 @@ #include "layTipDialog.h" +#include "layEditorOptionsPages.h" +#include "layDispatcher.h" +#include "layLayoutView.h" #include "edtPlugin.h" #include "edtConfig.h" #include "edtService.h" @@ -29,13 +32,27 @@ #include "edtMainService.h" #include "edtPartialService.h" #include "edtEditorOptionsPages.h" +#include "edtRecentConfigurationPage.h" #include +#include namespace edt { -static +edt::RecentConfigurationPage::ConfigurationDescriptor shape_cfg_descriptors[] = +{ + edt::RecentConfigurationPage::ConfigurationDescriptor ("", tl::to_string (tr ("Layer")), edt::RecentConfigurationPage::Layer), +}; + +static +void get_shape_editor_options_pages (std::vector &ret, lay::LayoutView *view, lay::Dispatcher *dispatcher) +{ + ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-shape-param", + &shape_cfg_descriptors[0], &shape_cfg_descriptors[sizeof (shape_cfg_descriptors) / sizeof (shape_cfg_descriptors[0])])); +} + +static void get_text_options (std::vector < std::pair > &options) { options.push_back (std::pair (cfg_edit_text_string, "ABC")); @@ -44,10 +61,21 @@ void get_text_options (std::vector < std::pair > &opti options.push_back (std::pair (cfg_edit_text_valign, "bottom")); } -static -void get_text_editor_options_pages (std::vector &ret, lay::Dispatcher *) +edt::RecentConfigurationPage::ConfigurationDescriptor text_cfg_descriptors[] = { - ret.push_back (new edt::EditorOptionsText ()); + edt::RecentConfigurationPage::ConfigurationDescriptor ("", tl::to_string (tr ("Layer")), edt::RecentConfigurationPage::Layer), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_text_string, tl::to_string (tr ("Text")), edt::RecentConfigurationPage::Text), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_text_size, tl::to_string (tr ("Size")), edt::RecentConfigurationPage::Double), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_text_halign, tl::to_string (tr ("Hor. align")), edt::RecentConfigurationPage::Text), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_text_valign, tl::to_string (tr ("Vert. align")), edt::RecentConfigurationPage::Text) +}; + +static +void get_text_editor_options_pages (std::vector &ret, lay::LayoutView *view, lay::Dispatcher *dispatcher) +{ + ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-text-param", + &text_cfg_descriptors[0], &text_cfg_descriptors[sizeof (text_cfg_descriptors) / sizeof (text_cfg_descriptors[0])])); + ret.push_back (new edt::EditorOptionsText (dispatcher)); } static @@ -59,10 +87,21 @@ void get_path_options (std::vector < std::pair > &opti options.push_back (std::pair (cfg_edit_path_ext_var_end, "0.0")); } -static -void get_path_editor_options_pages (std::vector &ret, lay::Dispatcher *) +edt::RecentConfigurationPage::ConfigurationDescriptor path_cfg_descriptors[] = { - ret.push_back (new EditorOptionsPath ()); + edt::RecentConfigurationPage::ConfigurationDescriptor ("", tl::to_string (tr ("Layer")), edt::RecentConfigurationPage::Layer), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_path_width, tl::to_string (tr ("Width")), edt::RecentConfigurationPage::Double), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_path_ext_type, tl::to_string (tr ("Ends")), edt::RecentConfigurationPage::Int), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_path_ext_var_begin, tl::to_string (tr ("Begin ext.")), edt::RecentConfigurationPage::Double), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_path_ext_var_end, tl::to_string (tr ("End ext.")), edt::RecentConfigurationPage::Double) +}; + +static +void get_path_editor_options_pages (std::vector &ret, lay::LayoutView *view, lay::Dispatcher *dispatcher) +{ + ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-path-param", + &path_cfg_descriptors[0], &path_cfg_descriptors[sizeof (path_cfg_descriptors) / sizeof (path_cfg_descriptors[0])])); + ret.push_back (new EditorOptionsPath (dispatcher)); } static @@ -84,10 +123,30 @@ void get_inst_options (std::vector < std::pair > &opti options.push_back (std::pair (cfg_edit_show_shapes_of_instances, "true")); } -static -void get_inst_editor_options_pages (std::vector &ret, lay::Dispatcher *root) +edt::RecentConfigurationPage::ConfigurationDescriptor inst_cfg_descriptors[] = { - ret.push_back (new EditorOptionsInst (root)); + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_lib_name, tl::to_string (tr ("Library")), edt::RecentConfigurationPage::CellLibraryName), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_cell_name, tl::to_string (tr ("Cell")), edt::RecentConfigurationPage::CellDisplayName), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_angle, tl::to_string (tr ("Angle")), edt::RecentConfigurationPage::Double), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_mirror, tl::to_string (tr ("Mirror")), edt::RecentConfigurationPage::Bool), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_scale, tl::to_string (tr ("Scale")), edt::RecentConfigurationPage::Double), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_array, tl::to_string (tr ("Array")), edt::RecentConfigurationPage::ArrayFlag), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_rows, tl::to_string (tr ("Rows")), edt::RecentConfigurationPage::IntIfArray), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_row_x, tl::to_string (tr ("Row step (x)")), edt::RecentConfigurationPage::DoubleIfArray), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_row_y, tl::to_string (tr ("Row step (y)")), edt::RecentConfigurationPage::DoubleIfArray), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_columns, tl::to_string (tr ("Columns")), edt::RecentConfigurationPage::IntIfArray), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_column_x, tl::to_string (tr ("Column step (x)")), edt::RecentConfigurationPage::DoubleIfArray), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_column_y, tl::to_string (tr ("Column step (y)")), edt::RecentConfigurationPage::DoubleIfArray), + edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_pcell_parameters, tl::to_string (tr ("PCell parameters")), edt::RecentConfigurationPage::PCellParameters) +}; + +static +void get_inst_editor_options_pages (std::vector &ret, lay::LayoutView *view, lay::Dispatcher *dispatcher) +{ + ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-inst-param", + &inst_cfg_descriptors[0], &inst_cfg_descriptors[sizeof (inst_cfg_descriptors) / sizeof (inst_cfg_descriptors[0])])); + ret.push_back (new EditorOptionsInstPCellParam (dispatcher)); + ret.push_back (new EditorOptionsInst (dispatcher)); } template @@ -97,7 +156,7 @@ class PluginDeclaration public: PluginDeclaration (const std::string &title, const std::string &mouse_mode, void (*option_get_f) (std::vector < std::pair > &) = 0, - void (*pages_f) (std::vector &, lay::Dispatcher *) = 0) + void (*pages_f) (std::vector &, lay::LayoutView *, lay::Dispatcher *) = 0) : m_title (title), m_mouse_mode (mouse_mode), mp_option_get_f (option_get_f), mp_pages_f (pages_f) { // .. nothing yet .. @@ -120,11 +179,11 @@ public: // .. nothing yet .. } - virtual void get_editor_options_pages (std::vector &pages, lay::Dispatcher *root) const + virtual void get_editor_options_pages (std::vector &pages, lay::LayoutView *view, lay::Dispatcher *root) const { if (mp_pages_f != 0) { size_t nstart = pages.size (); - (*mp_pages_f) (pages, root); + (*mp_pages_f) (pages, view, root); while (nstart < pages.size ()) { pages [nstart++]->set_plugin_declaration (this); } @@ -155,16 +214,16 @@ private: std::string m_mouse_mode; void (*mp_option_get_f) (std::vector < std::pair > &options); - void (*mp_pages_f) (std::vector &, lay::Dispatcher *); + void (*mp_pages_f) (std::vector &, lay::LayoutView *, lay::Dispatcher *); }; static tl::RegisteredClass config_decl1 ( - new edt::PluginDeclaration (tl::to_string (QObject::tr ("Polygons")), "polygon:edit_mode\t" + tl::to_string (QObject::tr ("Polygon")) + "<:polygon.png>" + tl::to_string (QObject::tr ("{Create a polygon}"))), + new edt::PluginDeclaration (tl::to_string (QObject::tr ("Polygons")), "polygon:edit_mode\t" + tl::to_string (QObject::tr ("Polygon")) + "<:polygon.png>" + tl::to_string (QObject::tr ("{Create a polygon}")), 0, &get_shape_editor_options_pages), 4010, "edt::Service(Polygons)" ); static tl::RegisteredClass config_decl2 ( - new edt::PluginDeclaration (tl::to_string (QObject::tr ("Boxes")), "box:edit_mode\t" + tl::to_string (QObject::tr ("Box")) + "\t<:box.png>" + tl::to_string (QObject::tr ("{Create a box}"))), + new edt::PluginDeclaration (tl::to_string (QObject::tr ("Boxes")), "box:edit_mode\t" + tl::to_string (QObject::tr ("Box")) + "\t<:box.png>" + tl::to_string (QObject::tr ("{Create a box}")), 0, &get_shape_editor_options_pages), 4011, "edt::Service(Boxes)" ); @@ -189,7 +248,7 @@ class MainPluginDeclaration { public: MainPluginDeclaration (const std::string &title) - : mp_root (0), m_title (title), mp_obj_prop_dialog (0) + : mp_root (0), m_title (title) { // .. nothing yet .. } @@ -218,13 +277,12 @@ public: menu_entries.push_back (lay::menu_item ("edt::descend", "descend", "zoom_menu.end", tl::to_string (QObject::tr ("Descend")) + "(Ctrl+D)")); menu_entries.push_back (lay::menu_item ("edt::ascend", "ascend", "zoom_menu.end", tl::to_string (QObject::tr ("Ascend")) + "(Ctrl+A)")); - menu_entries.push_back (lay::separator ("edit_options_group:edit_mode", "edit_menu.end")); - menu_entries.push_back (lay::menu_item ("edt::edit_options", "edit_options:edit_mode", "edit_menu.end", tl::to_string (QObject::tr ("Editor Options")) + "(F3)")); menu_entries.push_back (lay::menu_item ("edt::sel_make_array", "make_array:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Make Array")))); menu_entries.push_back (lay::separator ("selection_group:edit_mode", "edit_menu.selection_menu.end")); menu_entries.push_back (lay::menu_item ("edt::sel_change_layer", "change_layer:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Change Layer")))); 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")))); @@ -263,12 +321,19 @@ public: { return false; } - + virtual bool implements_mouse_mode (std::string & /*title*/) const { return false; } + virtual void get_editor_options_pages (std::vector &pages, lay::LayoutView * /*view*/, lay::Dispatcher *dispatcher) const + { + // NOTE: we do not set plugin_declaration which makes the page unspecific + EditorOptionsGeneric *generic_opt = new EditorOptionsGeneric (dispatcher); + pages.push_back (generic_opt); + } + virtual void initialize (lay::Dispatcher *root) { lay::Dispatcher *mp = lay::Dispatcher::instance (); @@ -278,24 +343,6 @@ public: mp_root = root; - // create the editor options dialog - m_prop_dialog_pages.push_back (new edt::EditorOptionsGeneric ()); - - for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { - const PluginDeclarationBase *pd_base = dynamic_cast (&*cls); - if (pd_base) { - pd_base->get_editor_options_pages (m_prop_dialog_pages, root); - } - } - - mp_obj_prop_dialog = new edt::EditorOptionsPages (m_prop_dialog_pages, root); - - for (std::vector::const_iterator op = m_prop_dialog_pages.begin (); op != m_prop_dialog_pages.end (); ++op) { - if ((*op)->plugin_declaration () != 0) { - (*op)->activate (false); - } - } - // add entries to the combine mode dialog mp->menu ()->insert_item ("@toolbar.combine_mode.end", "combine_mode_add", new lay::ConfigureAction (tl::to_string (QObject::tr ("Add<:/cm_add.png>{Add shapes}")), cfg_edit_combine_mode, CMConverter ().to_string (CM_Add))); mp->menu ()->insert_item ("@toolbar.combine_mode.end", "combine_mode_merge", new lay::ConfigureAction (tl::to_string (QObject::tr ("Merge<:/cm_merge.png>{Merge shapes with background}")), cfg_edit_combine_mode, CMConverter ().to_string (CM_Merge))); @@ -333,42 +380,6 @@ public: } } - virtual void uninitialize (lay::Dispatcher *) - { - if (mp_obj_prop_dialog) { - delete mp_obj_prop_dialog; - mp_obj_prop_dialog = 0; - } - } - - virtual void config_finalize () - { - if (mp_obj_prop_dialog && mp_obj_prop_dialog->isVisible ()) { - mp_obj_prop_dialog->setup (); - } - } - - void show_dialog () const - { - if (mp_obj_prop_dialog) { - if (! mp_obj_prop_dialog->isVisible ()) { - mp_obj_prop_dialog->setup (); - mp_obj_prop_dialog->show (); - } - mp_obj_prop_dialog->activateWindow (); - mp_obj_prop_dialog->raise (); - } - } - - void activate (const lay::PluginDeclaration *pd, bool active) const - { - for (std::vector::const_iterator op = m_prop_dialog_pages.begin (); op != m_prop_dialog_pages.end (); ++op) { - if ((*op)->plugin_declaration () == pd) { - (*op)->activate (active); - } - } - } - void initialized (lay::Dispatcher *root) { lay::Dispatcher *mp = lay::Dispatcher::instance (); @@ -397,40 +408,27 @@ public: private: lay::Dispatcher *mp_root; std::string m_title; - edt::EditorOptionsPages *mp_obj_prop_dialog; - std::vector m_prop_dialog_pages; }; +static tl::RegisteredClass config_decl_main (new edt::MainPluginDeclaration (tl::to_string (QObject::tr ("Instances and shapes"))), 4000, "edt::MainService"); + void -show_editor_options_dialog () +commit_recent (lay::LayoutView *view) { - // look for the plugin declaration and show the dialog - for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { - const MainPluginDeclaration *main_pd = dynamic_cast (&*cls); - if (main_pd) { - main_pd->show_dialog (); - break; + lay::EditorOptionsPages *eo_pages = view->editor_options_pages ();; + if (!eo_pages) { + return; + } + + for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { + if ((*op)->active ()) { + (*op)->commit_recent (view); } } } -void -activate_service (const lay::PluginDeclaration *pd, bool active) -{ - // look for the plugin declaration and show the dialog - for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { - const MainPluginDeclaration *main_pd = dynamic_cast (&*cls); - if (main_pd) { - main_pd->activate (pd, active); - break; - } - } -} - -static tl::RegisteredClass config_decl20 (new edt::MainPluginDeclaration (tl::to_string (QObject::tr ("Instances and shapes"))), 4000, "edt::MainService"); - class PartialPluginDeclaration - : public lay::PluginDeclaration + : public PluginDeclarationBase { public: PartialPluginDeclaration (const std::string &title, const std::string &mouse_mode) @@ -444,6 +442,11 @@ public: // .. nothing yet .. } + virtual void get_editor_options_pages (std::vector & /*pages*/, lay::LayoutView * /*view*/, lay::Dispatcher * /*root*/) const + { + // .. no specific ones .. + } + virtual lay::Plugin *create_plugin (db::Manager *manager, lay::Dispatcher *root, lay::LayoutView *view) const { return new edt::PartialService (manager, view, root); diff --git a/src/edt/edt/edtPlugin.h b/src/edt/edt/edtPlugin.h index f75dec8dd..17dcd2389 100644 --- a/src/edt/edt/edtPlugin.h +++ b/src/edt/edt/edtPlugin.h @@ -31,35 +31,24 @@ namespace lay { class Dispatcher; + class EditorOptionsPage; } namespace edt { - class EditorOptionsPage; - /** * @brief A helper class for plugin declarations for editor services */ class PluginDeclarationBase : public lay::PluginDeclaration { - public: - virtual void get_editor_options_pages (std::vector &, lay::Dispatcher *) const = 0; + // .. nothing yet .. }; /** - * @brief Show the editor options dialog - * - * This dialog is a global resource which is managed by the main plugin declaration + * @brief Commits the current configuration for the recently used configuration list */ - void show_editor_options_dialog (); - - /** - * @brief Activate or deactivate a certain service - * - * This will show or hide the editor properties pages for the respective service. - */ - void activate_service (const lay::PluginDeclaration *pd, bool active); + void commit_recent (lay::LayoutView *view); } #endif diff --git a/src/edt/edt/edtPropertiesPageUtils.cc b/src/edt/edt/edtPropertiesPageUtils.cc index 1bb1c9477..62fe988dc 100644 --- a/src/edt/edt/edtPropertiesPageUtils.cc +++ b/src/edt/edt/edtPropertiesPageUtils.cc @@ -26,6 +26,8 @@ #include "dbShapes.h" #include "dbLayout.h" +#include + namespace edt { diff --git a/src/edt/edt/edtPropertiesPageUtils.h b/src/edt/edt/edtPropertiesPageUtils.h index fc43c71ae..1e71ff5cd 100644 --- a/src/edt/edt/edtPropertiesPageUtils.h +++ b/src/edt/edt/edtPropertiesPageUtils.h @@ -33,6 +33,8 @@ #include +class QLineEdit; + namespace edt { diff --git a/src/edt/edt/edtPropertiesPages.cc b/src/edt/edt/edtPropertiesPages.cc index 8250241bb..ca645bcec 100644 --- a/src/edt/edt/edtPropertiesPages.cc +++ b/src/edt/edt/edtPropertiesPages.cc @@ -24,9 +24,11 @@ #include "edtPropertiesPages.h" #include "edtPropertiesPageUtils.h" #include "edtDialogs.h" +#include "edtPropertiesPageUtils.h" #include "layDialogs.h" #include "layObjectInstPath.h" #include "layLayoutView.h" +#include "layQtTools.h" #include "tlExceptions.h" #include "tlString.h" @@ -174,7 +176,7 @@ ShapePropertiesPage::recompute_selection_ptrs (const std::vector applicator; @@ -203,32 +205,7 @@ ShapePropertiesPage::do_apply (bool current_only) // Ask whether to use relative or absolute mode bool relative_mode = false; if (! current_only && applicator->supports_relative_mode ()) { - - static bool s_relative_mode = true; - - QMessageBox mb (QMessageBox::Question, - tr ("Apply Changes To All"), - tr ("For this operation absolute or relative mode is available which affects the way parameters of the selected objects are changed:\n\n" - "In absolute mode, they will be set to the given value. In relative mode, they will be adjusted by the same amount.\n"), - QMessageBox::NoButton, this); - - mb.addButton (tr ("Cancel"), QMessageBox::RejectRole); - QPushButton *absolute = mb.addButton (tr ("Absolute"), QMessageBox::NoRole); - QPushButton *relative = mb.addButton (tr ("Relative"), QMessageBox::YesRole); - - mb.setDefaultButton (s_relative_mode ? relative : absolute); - - mb.exec (); - - if (mb.clickedButton () == absolute) { - s_relative_mode = relative_mode = false; - } else if (mb.clickedButton () == relative) { - s_relative_mode = relative_mode = true; - } else { - // Cancel pressed - return; - } - + relative_mode = relative; } // Note: using the apply-all scheme for applying a single change may look like overhead. @@ -333,7 +310,7 @@ ShapePropertiesPage::do_apply (bool current_only) void ShapePropertiesPage::apply () { - do_apply (true); + do_apply (true, false); } bool @@ -343,9 +320,9 @@ ShapePropertiesPage::can_apply_to_all () const } void -ShapePropertiesPage::apply_to_all () +ShapePropertiesPage::apply_to_all (bool relative) { - do_apply (false); + do_apply (false, relative); } void @@ -391,7 +368,9 @@ void ShapePropertiesPage::show_props () { lay::UserPropertiesForm props_form (this); - props_form.show (mp_service->view (), m_selection_ptrs [m_index]->cv_index (), m_prop_id); + if (props_form.show (mp_service->view (), m_selection_ptrs [m_index]->cv_index (), m_prop_id)) { + emit edited (); + } } bool @@ -404,13 +383,19 @@ ShapePropertiesPage::readonly () // PolygonPropertiesPage implementation PolygonPropertiesPage::PolygonPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent) - : ShapePropertiesPage (service, manager, parent) + : ShapePropertiesPage (service, manager, parent), m_in_text_changed (false) { setupUi (this); setup (); connect (inst_pb, SIGNAL (clicked ()), this, SLOT (show_inst ())); connect (prop_pb, SIGNAL (clicked ()), this, SLOT (show_props ())); + + if (! readonly ()) { + connect (pointListEdit, SIGNAL (textChanged ()), this, SLOT (text_changed ())); + } else { + pointListEdit->setReadOnly (true); + } } void @@ -448,61 +433,87 @@ PolygonPropertiesPage::do_update (const db::Shape &shape, double dbu, const std: } - pointListEdit->setText (tl::to_qstring (ptlist)); + if (! m_in_text_changed) { + pointListEdit->blockSignals (true); + pointListEdit->setText (tl::to_qstring (ptlist)); + pointListEdit->blockSignals (false); + } + pointCountLabel->setText (tl::to_qstring (tl::sprintf (tl::to_string (QObject::tr ("(%lu points)")), poly.vertices ()))); } +void +PolygonPropertiesPage::text_changed () +{ + m_in_text_changed = true; + try { + emit edited (); + } catch (tl::Exception &) { + // ignore exceptions + } + m_in_text_changed = false; +} + ChangeApplicator * PolygonPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape &shape, double dbu) { - std::string text (tl::to_string (pointListEdit->toPlainText ())); - tl::Extractor ex (text.c_str ()); - - db::VCplxTrans t = db::CplxTrans (trans ()).inverted (); - bool du = dbu_units (); - db::Polygon poly; - if (*ex.skip () == '(') { + try { - db::DPolygon dp; - ex.read (dp); + std::string text (tl::to_string (pointListEdit->toPlainText ())); + tl::Extractor ex (text.c_str ()); - poly = db::Polygon (dp.transformed (db::DCplxTrans (t) * db::DCplxTrans (du ? 1.0 : 1.0 / dbu))); + db::VCplxTrans t = db::CplxTrans (trans ()).inverted (); + bool du = dbu_units (); - } else { + if (*ex.skip () == '(') { - unsigned int h = 0; - while (! ex.at_end ()) { + db::DPolygon dp; + ex.read (dp); - std::vector points; + poly = db::Polygon (dp.transformed (db::DCplxTrans (t) * db::DCplxTrans (du ? 1.0 : 1.0 / dbu))); - while (! ex.at_end () && ! ex.test ("/")) { + } else { - double dx = 0.0, dy = 0.0; - ex.read (dx); - ex.test (","); - ex.read (dy); - ex.test (";"); + unsigned int h = 0; + while (! ex.at_end ()) { - points.push_back (point_from_dpoint (db::DPoint (dx, dy), dbu, du, t)); + std::vector points; + + while (! ex.at_end () && ! ex.test ("/")) { + + double dx = 0.0, dy = 0.0; + ex.read (dx); + ex.test (","); + ex.read (dy); + ex.test (";"); + + points.push_back (point_from_dpoint (db::DPoint (dx, dy), dbu, du, t)); + + } + + if (points.size () < 3) { + throw tl::Exception (tl::to_string (QObject::tr ("Polygon must have at least three points"))); + } + + if (h == 0) { + poly.assign_hull (points.begin (), points.end (), false /*not compressed*/); + } else { + poly.insert_hole (points.begin (), points.end (), false /*not compressed*/); + } + + ++h; } - if (points.size () < 3) { - throw tl::Exception (tl::to_string (QObject::tr ("Polygon must have at least three points"))); - } - - if (h == 0) { - poly.assign_hull (points.begin (), points.end (), false /*not compressed*/); - } else { - poly.insert_hole (points.begin (), points.end (), false /*not compressed*/); - } - - ++h; - } + lay::indicate_error (pointListEdit, 0); + + } catch (tl::Exception &ex) { + lay::indicate_error (pointListEdit, &ex); + throw; } db::Polygon org_poly; @@ -525,15 +536,32 @@ BoxPropertiesPage::BoxPropertiesPage (edt::Service *service, db::Manager *manage setup (); mode_tab->setCurrentIndex (s_coordinateMode ? 0 : 1); - connect (mode_tab, SIGNAL (currentChanged (int)), this, SLOT (changed ())); - connect (x1_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ())); - connect (y1_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ())); - connect (x2_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ())); - connect (y2_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ())); - connect (w_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ())); - connect (h_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ())); - connect (cx_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ())); - connect (cy_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ())); + + if (! readonly ()) { + + connect (mode_tab, SIGNAL (currentChanged (int)), this, SLOT (changed ())); + connect (x1_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ())); + connect (y1_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ())); + connect (x2_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ())); + connect (y2_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ())); + connect (w_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ())); + connect (h_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ())); + connect (cx_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ())); + connect (cy_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ())); + + } else { + + x1_le_1->setReadOnly (true); + y1_le_1->setReadOnly (true); + x2_le_1->setReadOnly (true); + y2_le_1->setReadOnly (true); + w_le_2->setReadOnly (true); + h_le_2->setReadOnly (true); + cx_le_2->setReadOnly (true); + cy_le_2->setReadOnly (true); + + } + connect (inst_pb, SIGNAL (clicked ()), this, SLOT (show_inst ())); connect (prop_pb, SIGNAL (clicked ()), this, SLOT (show_props ())); } @@ -576,11 +604,44 @@ BoxPropertiesPage::get_box (int mode) const { if (mode == 0) { + bool has_error = false; double x1 = 0.0, y1 = 0.0, x2 = 0.0, y2 = 0.0; - tl::from_string (tl::to_string (x1_le_1->text ()), x1); - tl::from_string (tl::to_string (y1_le_1->text ()), y1); - tl::from_string (tl::to_string (x2_le_1->text ()), x2); - tl::from_string (tl::to_string (y2_le_1->text ()), y2); + + try { + tl::from_string (tl::to_string (x1_le_1->text ()), x1); + lay::indicate_error (x1_le_1, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (x1_le_1, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (y1_le_1->text ()), y1); + lay::indicate_error (y1_le_1, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (y1_le_1, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (x2_le_1->text ()), x2); + lay::indicate_error (x2_le_1, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (x2_le_1, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (y2_le_1->text ()), y2); + lay::indicate_error (y2_le_1, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (y2_le_1, &ex); + has_error = true; + } + + if (has_error) { + throw tl::Exception (tl::to_string (tr ("Invalid values - see highlighted entry boxes"))); + } if (m_lr_swapped) { std::swap (x1, x2); @@ -603,11 +664,44 @@ BoxPropertiesPage::get_box (int mode) const } else { + bool has_error = false; double cx = 0.0, cy = 0.0, w = 0.0, h = 0.0; - tl::from_string (tl::to_string (cx_le_2->text ()), cx); - tl::from_string (tl::to_string (cy_le_2->text ()), cy); - tl::from_string (tl::to_string (w_le_2->text ()), w); - tl::from_string (tl::to_string (h_le_2->text ()), h); + + try { + tl::from_string (tl::to_string (cx_le_2->text ()), cx); + lay::indicate_error (cx_le_2, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (cx_le_2, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (cy_le_2->text ()), cy); + lay::indicate_error (cy_le_2, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (cy_le_2, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (w_le_2->text ()), w); + lay::indicate_error (w_le_2, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (w_le_2, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (h_le_2->text ()), h); + lay::indicate_error (h_le_2, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (h_le_2, &ex); + has_error = true; + } + + if (has_error) { + throw tl::Exception (tl::to_string (tr ("Invalid values - see highlighted entry boxes"))); + } db::VCplxTrans t = db::VCplxTrans (trans ().inverted ()); bool du = dbu_units (); @@ -664,6 +758,8 @@ BoxPropertiesPage::changed () set_box (get_box (m_tab_index)); } catch (...) { } + + emit edited (); } // ------------------------------------------------------------------------- @@ -677,6 +773,28 @@ TextPropertiesPage::TextPropertiesPage (edt::Service *service, db::Manager *mana connect (inst_pb, SIGNAL (clicked ()), this, SLOT (show_inst ())); connect (prop_pb, SIGNAL (clicked ()), this, SLOT (show_props ())); + + if (! readonly ()) { + + connect (text_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (size_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (orient_cbx, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (halign_cbx, SIGNAL (activated (int)), this, SIGNAL (edited ())); + connect (valign_cbx, SIGNAL (activated (int)), this, SIGNAL (edited ())); + + } else { + + text_le->setReadOnly (true); + x_le->setReadOnly (true); + y_le->setReadOnly (true); + size_le->setReadOnly (true); + orient_cbx->setEnabled (false); + halign_cbx->setEnabled (false); + valign_cbx->setEnabled (false); + + } } void @@ -707,12 +825,28 @@ TextPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::st ChangeApplicator * TextPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape &shape, double dbu) { + bool has_error = false; + db::VCplxTrans t = db::CplxTrans (trans ()).inverted (); bool du = dbu_units (); double x = 0.0, y = 0.0; - tl::from_string (tl::to_string (x_le->text ()), x); - tl::from_string (tl::to_string (y_le->text ()), y); + + try { + tl::from_string (tl::to_string (x_le->text ()), x); + lay::indicate_error (x_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (x_le, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (y_le->text ()), y); + lay::indicate_error (y_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (y_le, &ex); + has_error = true; + } db::Vector tp = db::Vector (point_from_dpoint (db::DPoint (x, y), dbu, du, t)); db::Trans tt (orient_cbx->currentIndex (), tp); @@ -739,7 +873,13 @@ TextPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape db::Coord size = 0; if (! size_le->text ().isEmpty ()) { - size = coord_from_string (tl::to_string (size_le->text ()).c_str (), dbu, du, t); + try { + size = coord_from_string (tl::to_string (size_le->text ()).c_str (), dbu, du, t); + lay::indicate_error (size_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (size_le, &ex); + has_error = true; + } } if (size != org_text.size ()) { appl->add (new TextSizeChangeApplicator (size)); @@ -749,6 +889,10 @@ TextPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape appl->add (new TextStringChangeApplicator (str)); } + if (has_error) { + throw tl::Exception (tl::to_string (tr ("Invalid values - see highlighted entry boxes"))); + } + return appl.release (); } @@ -756,16 +900,22 @@ TextPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape // PathPropertiesPage implementation PathPropertiesPage::PathPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent) - : ShapePropertiesPage (service, manager, parent) + : ShapePropertiesPage (service, manager, parent), m_in_text_changed (false) { setupUi (this); setup (); connect (inst_pb, SIGNAL (clicked ()), this, SLOT (show_inst ())); connect (prop_pb, SIGNAL (clicked ()), this, SLOT (show_props ())); + + ptlist_le->setReadOnly (true); + width_le->setReadOnly (true); + start_ext_le->setReadOnly (true); + end_ext_le->setReadOnly (true); + round_cb->setEnabled (false); } -void +void PathPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::string &lname) { layer_lbl->setText (tl::to_qstring (lname)); @@ -788,7 +938,12 @@ PathPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::st } ptlist += coords_to_string (t * *pt, dbu, du); } - ptlist_le->setText (tl::to_qstring (ptlist)); + + if (! m_in_text_changed) { + ptlist_le->blockSignals (true); + ptlist_le->setText (tl::to_qstring (ptlist)); + ptlist_le->blockSignals (false); + } width_le->setText (tl::to_qstring (coord_to_string (t.ctrans (path.width ()), dbu, du))); start_ext_le->setText (tl::to_qstring (coord_to_string (t.ctrans (path.extensions ().first), dbu, du))); @@ -797,68 +952,16 @@ PathPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::st } ChangeApplicator * -PathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape &shape, double dbu) +PathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape & /*shape*/, double /*dbu*/) { - db::VCplxTrans t = db::CplxTrans (trans ()).inverted (); - bool du = dbu_units (); - - std::string text (tl::to_string (ptlist_le->toPlainText ())); - tl::Extractor ex (text.c_str ()); - - std::vector points; - - while (! ex.at_end ()) { - - double dx = 0.0, dy = 0.0; - ex.read (dx); - ex.read (dy); - - points.push_back (point_from_dpoint (db::DPoint (dx, dy), dbu, du, t)); - - } - - if (points.size () < 1) { - throw tl::Exception (tl::to_string (QObject::tr ("The path must have at least one point"))); - } - - db::Coord w = coord_from_string (tl::to_string (width_le->text ()).c_str (), dbu, du, t); - db::Coord se = coord_from_string (tl::to_string (start_ext_le->text ()).c_str (), dbu, du, t); - db::Coord ee = coord_from_string (tl::to_string (end_ext_le->text ()).c_str (), dbu, du, t); - bool round = round_cb->isChecked (); - - std::auto_ptr appl; - - db::Path org_path; - shape.path (org_path); - std::vector org_points; - for (db::Path::iterator p = org_path.begin (); p != org_path.end (); ++p) { - org_points.push_back (*p); - } - - if (org_points != points) { - appl->add (new PathPointsChangeApplicator (points, org_points)); - } - if (w != org_path.width ()) { - appl->add (new PathWidthChangeApplicator (w, org_path.width ())); - } - if (se != org_path.extensions ().first) { - appl->add (new PathStartExtensionChangeApplicator (se)); - } - if (ee != org_path.extensions ().second) { - appl->add (new PathEndExtensionChangeApplicator (ee)); - } - if (round != org_path.round ()) { - appl->add (new PathRoundEndChangeApplicator (round)); - } - - return appl.release (); + return 0; } // ------------------------------------------------------------------------- // EditablePathPropertiesPage implementation EditablePathPropertiesPage::EditablePathPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent) - : ShapePropertiesPage (service, manager, parent) + : ShapePropertiesPage (service, manager, parent), m_in_text_changed (false) { setupUi (this); setup (); @@ -866,6 +969,12 @@ EditablePathPropertiesPage::EditablePathPropertiesPage (edt::Service *service, d connect (inst_pb, SIGNAL (clicked ()), this, SLOT (show_inst ())); connect (prop_pb, SIGNAL (clicked ()), this, SLOT (show_props ())); connect (type_cb, SIGNAL (currentIndexChanged (int)), this, SLOT (type_selected (int))); + + connect (ptlist_le, SIGNAL (textChanged ()), this, SLOT (text_changed ())); + connect (width_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (start_ext_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (end_ext_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (type_cb, SIGNAL (activated (int)), this, SIGNAL (edited ())); } static int @@ -886,7 +995,19 @@ path_type_choice (const db::Path &path) } } -void +void +EditablePathPropertiesPage::text_changed () +{ + m_in_text_changed = true; + try { + emit edited (); + } catch (tl::Exception &) { + // ignore exceptions + } + m_in_text_changed = false; +} + +void EditablePathPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::string &lname) { layer_lbl->setText (tl::to_qstring (lname)); @@ -909,7 +1030,12 @@ EditablePathPropertiesPage::do_update (const db::Shape &shape, double dbu, const } ptlist += coords_to_string (t * *pt, dbu, du); } - ptlist_le->setText (tl::to_qstring (ptlist)); + + if (! m_in_text_changed) { + ptlist_le->blockSignals (true); + ptlist_le->setText (tl::to_qstring (ptlist)); + ptlist_le->blockSignals (false); + } db::Coord w = path.width (); db::Coord se = path.extensions ().first; @@ -921,6 +1047,10 @@ EditablePathPropertiesPage::do_update (const db::Shape &shape, double dbu, const end_ext_le->setText (tl::to_qstring (coord_to_string (t.ctrans (ee), dbu, du))); int type_choice = path_type_choice (path); + if (type_cb->currentIndex () == 2) { + // keep "variable" mode, otherwise if's difficult to switch to it + type_choice = 2; + } type_cb->setCurrentIndex (type_choice); type_selected (type_choice); } @@ -928,6 +1058,8 @@ EditablePathPropertiesPage::do_update (const db::Shape &shape, double dbu, const ChangeApplicator * EditablePathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape &shape, double dbu) { + bool has_error = false; + db::VCplxTrans t = db::CplxTrans (trans ()).inverted (); bool du = dbu_units (); @@ -936,22 +1068,38 @@ EditablePathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db std::vector points; - while (! ex.at_end ()) { + try { - double dx = 0.0, dy = 0.0; - ex.read (dx); - ex.read (dy); + while (! ex.at_end ()) { - points.push_back (point_from_dpoint (db::DPoint (dx, dy), dbu, du, t)); + double dx = 0.0, dy = 0.0; + ex.read (dx); + ex.read (dy); + points.push_back (point_from_dpoint (db::DPoint (dx, dy), dbu, du, t)); + + } + + if (points.size () < 1) { + throw tl::Exception (tl::to_string (QObject::tr ("The path must have at least one point"))); + } + + lay::indicate_error (ptlist_le, 0); + + } catch (tl::Exception &ex) { + lay::indicate_error (ptlist_le, &ex); + has_error = true; } - if (points.size () < 1) { - throw tl::Exception (tl::to_string (QObject::tr ("The path must have at least one point"))); + db::Coord w = 0; + try { + w = coord_from_string (tl::to_string (width_le->text ()).c_str (), dbu, du, t); + lay::indicate_error (width_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (width_le, &ex); + has_error = true; } - db::Coord w = coord_from_string (tl::to_string (width_le->text ()).c_str (), dbu, du, t); - db::Coord se = 0, ee = 0; switch (type_cb->currentIndex ()) { case 0: // flush @@ -961,8 +1109,20 @@ EditablePathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db se = ee = std::numeric_limits ::min (); // force to half width break; case 2: // variable - se = coord_from_string (tl::to_string (start_ext_le->text ()).c_str (), dbu, du, t); - ee = coord_from_string (tl::to_string (end_ext_le->text ()).c_str (), dbu, du, t); + try { + se = coord_from_string (tl::to_string (start_ext_le->text ()).c_str (), dbu, du, t); + lay::indicate_error (start_ext_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (start_ext_le, &ex); + has_error = true; + } + try { + ee = coord_from_string (tl::to_string (end_ext_le->text ()).c_str (), dbu, du, t); + lay::indicate_error (end_ext_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (end_ext_le, &ex); + has_error = true; + } break; } @@ -989,6 +1149,10 @@ EditablePathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db appl->add (new PathRoundEndChangeApplicator (type_cb->currentIndex () == 3)); } + if (has_error) { + throw tl::Exception (tl::to_string (tr ("Invalid values - see highlighted entry boxes"))); + } + return appl.release (); } diff --git a/src/edt/edt/edtPropertiesPages.h b/src/edt/edt/edtPropertiesPages.h index 5b612326c..42e283cc2 100644 --- a/src/edt/edt/edtPropertiesPages.h +++ b/src/edt/edt/edtPropertiesPages.h @@ -56,13 +56,15 @@ public: virtual void operator++ (); virtual void leave (); +protected: + virtual bool readonly (); + private: virtual void update (); virtual void apply (); - virtual void apply_to_all (); + virtual void apply_to_all (bool relative); virtual bool can_apply_to_all () const; - virtual void do_apply (bool current_only); - virtual bool readonly (); + virtual void do_apply (bool current_only, bool relative); void recompute_selection_ptrs (const std::vector &new_sel); protected: @@ -104,6 +106,12 @@ public: protected: virtual QCheckBox *dbu_checkbox () const { return dbu_cb; } virtual QCheckBox *abs_checkbox () const { return abs_cb; } + +public slots: + void text_changed (); + +private: + bool m_in_text_changed; }; class BoxPropertiesPage @@ -167,6 +175,9 @@ public: protected: virtual QCheckBox *dbu_checkbox () const { return dbu_cb; } virtual QCheckBox *abs_checkbox () const { return abs_cb; } + +private: + bool m_in_text_changed; }; class EditablePathPropertiesPage @@ -187,6 +198,10 @@ protected: public slots: void type_selected (int); + void text_changed (); + +private: + bool m_in_text_changed; }; } diff --git a/src/edt/edt/edtRecentConfigurationPage.cc b/src/edt/edt/edtRecentConfigurationPage.cc new file mode 100644 index 000000000..554f85be0 --- /dev/null +++ b/src/edt/edt/edtRecentConfigurationPage.cc @@ -0,0 +1,394 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "edtRecentConfigurationPage.h" +#include "edtUtils.h" +#include "layDispatcher.h" +#include "layLayoutView.h" +#include "layLayerTreeModel.h" +#include "dbLibraryManager.h" +#include "dbLibrary.h" + +#include +#include +#include + +namespace edt +{ + +static const size_t max_entries = 100; + +void +RecentConfigurationPage::init () +{ + QVBoxLayout *ly = new QVBoxLayout (this); + ly->setMargin (0); + + QLabel *label = new QLabel (this); + label->setText (tr ("Click to select a recent configuration")); + ly->addWidget (label); + + mp_tree_widget = new QTreeWidget (this); + mp_tree_widget->setRootIsDecorated (false); + mp_tree_widget->setUniformRowHeights (true); + mp_tree_widget->setSelectionMode (QAbstractItemView::NoSelection); + mp_tree_widget->setAllColumnsShowFocus (true); + ly->addWidget (mp_tree_widget); + + connect (mp_tree_widget, SIGNAL (itemClicked (QTreeWidgetItem *, int)), this, SLOT (item_clicked (QTreeWidgetItem *))); + mp_view->layer_list_changed_event.add (this, &RecentConfigurationPage::layers_changed); + + mp_tree_widget->setColumnCount (int (m_cfg.size ())); + + QStringList column_labels; + for (std::list::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c) { + column_labels << tl::to_qstring (c->title); + } + mp_tree_widget->setHeaderLabels (column_labels); + + update_list (get_stored_values ()); +} + +RecentConfigurationPage::~RecentConfigurationPage () +{ + // .. nothing yet .. +} + +std::string RecentConfigurationPage::title () const +{ + return tl::to_string (tr ("Recent")); +} + +int RecentConfigurationPage::order () const +{ + return 100; +} + +std::list > +RecentConfigurationPage::get_stored_values () const +{ + std::string serialized_list = dispatcher ()->config_get (m_recent_cfg_name); + + std::list > values; + tl::Extractor ex (serialized_list.c_str ()); + while (! ex.at_end ()) { + + values.push_back (std::vector ()); + while (! ex.at_end () && ! ex.test (";")) { + values.back ().push_back (std::string ()); + ex.read_word_or_quoted (values.back ().back ()); + ex.test (","); + } + + } + + return values; +} + +void +RecentConfigurationPage::set_stored_values (const std::list > &values) const +{ + std::string serialized_list; + for (std::list >::const_iterator v = values.begin (); v != values.end (); ++v) { + if (v != values.begin ()) { + serialized_list += ";"; + } + for (std::vector::const_iterator s = v->begin (); s != v->end (); ++s) { + serialized_list += tl::to_word_or_quoted_string (*s); + serialized_list += ","; + } + } + + dispatcher ()->config_set (m_recent_cfg_name, serialized_list); +} + +static lay::LayerPropertiesConstIterator +lp_iter_from_string (lay::LayoutView *view, const std::string &s) +{ + // parse the layer spec ([@]) + db::LayerProperties lp; + tl::Extractor ex (s.c_str ()); + lp.read (ex); + int cv_index = 0; + if (ex.test ("@")) { + ex.read (cv_index); + } + + // rename the ones that got shifted. + lay::LayerPropertiesConstIterator l = view->begin_layers (); + while (! l.at_end ()) { + if (l->source (true).cv_index () == int (cv_index) && l->source (true).layer_props ().log_equal (lp)) { + return l; + } + ++l; + } + + return l; +} + + +void +RecentConfigurationPage::render_to (QTreeWidgetItem *item, int column, const std::vector &values, RecentConfigurationPage::ConfigurationRendering rendering) +{ + // store original value + item->setData (column, Qt::UserRole, tl::to_qstring (values [column])); + + switch (rendering) { + + case RecentConfigurationPage::ArrayFlag: + case RecentConfigurationPage::Bool: + { + bool f = false; + tl::from_string (values [column], f); + static QString checkmark = QString::fromUtf8 ("\xe2\x9c\x93"); + item->setText (column, f ? checkmark : QString ()); // "checkmark" + } + break; + + case RecentConfigurationPage::Layer: + { + int icon_size = mp_view->style ()->pixelMetric (QStyle::PM_ButtonIconSize); + lay::LayerPropertiesConstIterator l = lp_iter_from_string (mp_view, values [column]); + if (! l.is_null () && ! l.at_end ()) { + item->setIcon (column, lay::LayerTreeModel::icon_for_layer (l, mp_view, icon_size, icon_size, 0, true)); + item->setText (column, tl::to_qstring (values [column])); + } else { + item->setIcon (column, QIcon ()); + item->setText (column, tl::to_qstring ("(" + values [column] + ")")); + } + } + break; + + case RecentConfigurationPage::Int: + case RecentConfigurationPage::Double: + case RecentConfigurationPage::Text: + item->setText (column, tl::to_qstring (values [column])); + break; + + case RecentConfigurationPage::CellLibraryName: + if (values [column].empty ()) { + item->setText (column, tr ("(local)")); + } else { + item->setText (column, tl::to_qstring (values [column])); + } + break; + + case RecentConfigurationPage::IntIfArray: + case RecentConfigurationPage::DoubleIfArray: + { + bool is_array = false; + int flag_column = 0; + for (std::list::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c, ++flag_column) { + if (c->rendering == RecentConfigurationPage::ArrayFlag) { + tl::from_string (values [flag_column], is_array); + break; + } + } + + if (is_array) { + item->setText (column, tl::to_qstring (values [column])); + } else { + item->setText (column, QString ()); + } + } + break; + + case RecentConfigurationPage::CellDisplayName: + { + // search for a libname + int libname_column = 0; + const db::Library *lib = 0; + for (std::list::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c, ++libname_column) { + if (c->rendering == RecentConfigurationPage::CellLibraryName) { + lib = db::LibraryManager::instance ().lib_ptr_by_name (values [libname_column]); + break; + } + } + + if (lib) { + + // search for a PCell parameters + int pcp_column = 0; + std::map pcp; + for (std::list::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c, ++pcp_column) { + if (c->rendering == RecentConfigurationPage::PCellParameters) { + pcp = pcell_parameters_from_string (values [pcp_column]); + break; + } + } + + std::pair pcid = lib->layout ().pcell_by_name (values [column].c_str ()); + if (pcid.first) { + const db::PCellDeclaration *pc_decl = lib->layout ().pcell_declaration (pcid.second); + if (pc_decl) { + item->setText (column, tl::to_qstring (pc_decl->get_display_name (pc_decl->map_parameters (pcp)))); + break; + } + } + + } + + item->setText (column, tl::to_qstring (values [column])); + } + break; + + case RecentConfigurationPage::PCellParameters: + { + std::map pcp; + pcp = pcell_parameters_from_string (values [column]); + std::string r; + for (std::map::const_iterator p = pcp.begin (); p != pcp.end (); ++p) { + if (p != pcp.begin ()) { + r += ","; + } + r += p->first; + r += "="; + r += p->second.to_string (); + } + + item->setText (column, tl::to_qstring (r)); + } + break; + } + +} + +void +RecentConfigurationPage::layers_changed (int) +{ + update_list (get_stored_values ()); +} + +void +RecentConfigurationPage::update_list (const std::list > &stored_values) +{ + int row = 0; + for (std::list >::const_iterator v = stored_values.begin (); v != stored_values.end (); ++v, ++row) { + + QTreeWidgetItem *item = 0; + if (row < mp_tree_widget->topLevelItemCount ()) { + item = mp_tree_widget->topLevelItem (row); + } else { + item = new QTreeWidgetItem (mp_tree_widget); + mp_tree_widget->addTopLevelItem (item); + } + + int column = 0; + for (std::list::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c, ++column) { + if (column < int (v->size ())) { + render_to (item, column, *v, c->rendering); + } + } + + } + + while (mp_tree_widget->topLevelItemCount () > row) { + delete mp_tree_widget->takeTopLevelItem (row); + } + + mp_tree_widget->header ()->resizeSections (QHeaderView::ResizeToContents); +} + +void +RecentConfigurationPage::item_clicked (QTreeWidgetItem *item) +{ + int column = 0; + for (std::list::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c, ++column) { + + std::string v = tl::to_string (item->data (column, Qt::UserRole).toString ()); + + if (c->rendering == Layer) { + + // "getting" a layer means making it current + db::LayerProperties lp; + tl::Extractor ex (v.c_str ()); + lp.read (ex); + int cv_index = 0; + if (ex.test ("@")) { + ex.read (cv_index); + } + + mp_view->set_or_request_current_layer (cv_index, lp); + + } else { + dispatcher ()->config_set (c->cfg_name, v); + } + + } + + dispatcher ()->config_end (); +} + +void +RecentConfigurationPage::commit_recent (lay::Dispatcher *root) +{ + std::vector values; + values.reserve (m_cfg.size ()); + for (std::list::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c) { + + if (c->rendering == Layer) { + + std::string s; + + if (!(mp_view->current_layer ().is_null () || mp_view->current_layer ().at_end ()) && mp_view->current_layer ()->is_visual ()) { + + int cv_index = mp_view->current_layer ()->cellview_index (); + const lay::CellView &cv = mp_view->cellview (cv_index); + int li = mp_view->current_layer ()->layer_index (); + if (cv.is_valid () && cv->layout ().is_valid_layer (li)) { + s = cv->layout ().get_properties (li).to_string (); + if (cv_index > 0) { + s += "@" + tl::to_string (cv_index); + } + } + + } + + values.push_back (s); + + } else { + values.push_back (root->config_get (c->cfg_name)); + } + + } + + std::list > stored_values = get_stored_values (); + + for (std::list >::iterator v = stored_values.begin (); v != stored_values.end (); ++v) { + if (*v == values) { + stored_values.erase (v); + break; + } + } + + stored_values.push_front (values); + + while (stored_values.size () > max_entries) { + stored_values.erase (--stored_values.end ()); + } + + set_stored_values (stored_values); + + update_list (stored_values); +} + +} diff --git a/src/edt/edt/edtRecentConfigurationPage.h b/src/edt/edt/edtRecentConfigurationPage.h new file mode 100644 index 000000000..a177be7fb --- /dev/null +++ b/src/edt/edt/edtRecentConfigurationPage.h @@ -0,0 +1,115 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_edtRecentConfigurationPage +#define HDR_edtRecentConfigurationPage + +#include "layEditorOptionsPage.h" +#include "tlObject.h" + +#include +#include + +namespace lay +{ + class LayoutView; +} + +namespace edt +{ + +class PCellParametersPage; + +class EditorOptionsPages; + +/** + * @brief The base class for a object properties page + */ +class RecentConfigurationPage + : public lay::EditorOptionsPage, + public tl::Object +{ +Q_OBJECT + +public: + enum ConfigurationRendering + { + Text = 0, + Bool = 1, + Double = 2, + Int = 3, + Layer = 4, + PCellParameters = 5, + CellLibraryName = 6, + CellDisplayName = 7, + ArrayFlag = 8, + DoubleIfArray = 9, + IntIfArray = 10 + }; + + struct ConfigurationDescriptor + { + ConfigurationDescriptor (const std::string &_cfg_name, const std::string &_title, ConfigurationRendering _rendering) + : cfg_name (_cfg_name), title (_title), rendering (_rendering) + { } + + std::string cfg_name, title; + ConfigurationRendering rendering; + }; + + template + RecentConfigurationPage (lay::LayoutView *view, lay::Dispatcher *dispatcher, const std::string &recent_cfg_name, Iter begin_cfg, Iter end_cfg) + : EditorOptionsPage (dispatcher), mp_view (view), m_recent_cfg_name (recent_cfg_name), m_cfg (begin_cfg, end_cfg) + { + init (); + } + + virtual ~RecentConfigurationPage (); + + virtual std::string title () const; + virtual int order () const; + virtual void apply (lay::Dispatcher * /*root*/) { } + virtual void setup (lay::Dispatcher * /*root*/) { } + virtual void commit_recent (lay::Dispatcher *root); + +private slots: + void item_clicked (QTreeWidgetItem *item); + +private: + lay::LayoutView *mp_view; + std::string m_recent_cfg_name; + std::list m_cfg; + QTreeWidget *mp_tree_widget; + + void init (); + void update_list (const std::list > &stored_values); + std::list > get_stored_values () const; + void set_stored_values (const std::list > &values) const; + void render_to (QTreeWidgetItem *item, int column, const std::vector &values, RecentConfigurationPage::ConfigurationRendering rendering); + void layers_changed (int); +}; + +} + +#endif + diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index 0e5986b5c..904c91574 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -60,49 +60,8 @@ ac_from_buttons (unsigned int buttons) // ------------------------------------------------------------- -std::string pcell_parameters_to_string (const std::map ¶meters) -{ - std::string param; - - param = "!"; // flags PCells - for (std::map::const_iterator p = parameters.begin (); p != parameters.end (); ++p) { - param += tl::to_word_or_quoted_string (p->first); - param += ":"; - param += p->second.to_parsable_string (); - param += ";"; - } - - return param; -} - -std::map pcell_parameters_from_string (const std::string &s) -{ - tl::Extractor ex (s.c_str ()); - std::map pm; - - ex.test ("!"); - - try { - while (! ex.at_end ()) { - std::string n; - ex.read_word_or_quoted (n); - ex.test (":"); - ex.read (pm.insert (std::make_pair (n, tl::Variant ())).first->second); - ex.test (";"); - } - } catch (...) { - // ignore errors - } - - return pm; -} - -// ------------------------------------------------------------- - Service::Service (db::Manager *manager, lay::LayoutView *view, db::ShapeIterator::flags_type flags) - : lay::ViewService (view->view_object_widget ()), - lay::Editable (view), - lay::Plugin (view), + : lay::EditorServiceBase (view), db::Object (manager), mp_view (view), mp_transient_marker (0), @@ -122,9 +81,7 @@ Service::Service (db::Manager *manager, lay::LayoutView *view, db::ShapeIterator } Service::Service (db::Manager *manager, lay::LayoutView *view) - : lay::ViewService (view->view_object_widget ()), - lay::Editable (view), - lay::Plugin (view), + : lay::EditorServiceBase (view), db::Object (manager), mp_view (view), mp_transient_marker (0), @@ -217,18 +174,30 @@ Service::snap (const db::DPoint &p, const db::DPoint &plast, bool connect) const const int sr_pixels = 8; // TODO: make variable -db::DPoint -Service::snap2 (const db::DPoint &p) const +lay::PointSnapToObjectResult +Service::snap2_details (const db::DPoint &p) const { double snap_range = widget ()->mouse_event_trans ().inverted ().ctrans (sr_pixels); - return lay::obj_snap (m_snap_to_objects ? view () : 0, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, snap_range).second; + return lay::obj_snap (m_snap_to_objects ? view () : 0, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, snap_range); +} + +db::DPoint +Service::snap2 (const db::DPoint &p) const +{ + return snap2_details (p).snapped_point; } db::DPoint Service::snap2 (const db::DPoint &p, const db::DPoint &plast, bool connect) const { double snap_range = widget ()->mouse_event_trans ().inverted ().ctrans (sr_pixels); - return lay::obj_snap (m_snap_to_objects ? view () : 0, plast, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, connect ? connect_ac () : move_ac (), snap_range).second; + return lay::obj_snap (m_snap_to_objects ? view () : 0, plast, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, connect ? connect_ac () : move_ac (), snap_range).snapped_point; +} + +void +Service::service_configuration_changed () +{ + // The base class implementation does nothing } bool @@ -238,27 +207,60 @@ Service::configure (const std::string &name, const std::string &value) edt::ACConverter acc; if (name == cfg_edit_global_grid) { + egc.from_string (value, m_global_grid); + service_configuration_changed (); + } else if (name == cfg_edit_show_shapes_of_instances) { + tl::from_string (value, m_show_shapes_of_instances); + service_configuration_changed (); + } else if (name == cfg_edit_max_shapes_of_instances) { + tl::from_string (value, m_max_shapes_of_instances); + service_configuration_changed (); + } else if (name == cfg_edit_grid) { + egc.from_string (value, m_edit_grid); + service_configuration_changed (); + return true; // taken + } else if (name == cfg_edit_snap_to_objects) { + tl::from_string (value, m_snap_to_objects); + service_configuration_changed (); + return true; // taken + } else if (name == cfg_edit_move_angle_mode) { + acc.from_string (value, m_move_ac); + service_configuration_changed (); + return true; // taken + } else if (name == cfg_edit_connect_angle_mode) { + acc.from_string (value, m_connect_ac); + service_configuration_changed (); + return true; // taken + } else if (name == cfg_edit_top_level_selection) { + tl::from_string (value, m_top_level_sel); + service_configuration_changed (); + } else if (name == cfg_edit_hier_copy_mode) { + tl::from_string (value, m_hier_copy_mode); + service_configuration_changed (); + + } else { + lay::EditorServiceBase::configure (name, value); } return false; // not taken @@ -710,14 +712,15 @@ Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) // in this mode, ignore exceptions here since it is rather annoying to have messages popping // up then. try { - do_begin_edit (p); - m_editing = true; + begin_edit (p); } catch (...) { set_edit_marker (0); } } if (m_editing) { do_mouse_move (p); + } else { + do_mouse_move_inactive (p); } m_alt_ac = lay::AC_Global; @@ -744,8 +747,7 @@ Service::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio view ()->cancel (); // cancel any pending edit operations and clear the selection set_edit_marker (0); - do_begin_edit (p); - m_editing = true; + begin_edit (p); } else { if (do_mouse_click (p)) { @@ -797,22 +799,21 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio void Service::activated () { - // make all editor option pages visible - activate_service (plugin_declaration (), true); - if (view ()->is_editable ()) { + view ()->cancel (); // cancel any pending edit operations and clear the selection set_edit_marker (0); + m_immediate = do_activated (); m_editing = false; + } } void Service::deactivated () { - // make all editor option pages visible - activate_service (plugin_declaration (), false); + lay::EditorServiceBase::deactivated (); edit_cancel (); @@ -1432,6 +1433,19 @@ Service::move_markers (const db::DTrans &t) } } +void +Service::begin_edit (const db::DPoint &p) +{ + do_begin_edit (p); + m_editing = true; +} + +void +Service::tap (const db::DPoint & /*initial*/) +{ + // .. nothing here .. +} + void Service::selection_to_view () { diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h index cb4812f79..b24871cf6 100644 --- a/src/edt/edt/edtService.h +++ b/src/edt/edt/edtService.h @@ -27,9 +27,8 @@ #include "edtCommon.h" -#include "layEditable.h" +#include "layEditorServiceBase.h" #include "layPlugin.h" -#include "layViewObject.h" #include "layMarker.h" #include "laySnap.h" #include "layObjectInstPath.h" @@ -44,12 +43,14 @@ #include #include +namespace lay { + class LayerPropertiesConstIterator; +} + namespace edt { class Service; class PluginDeclarationBase; -class EditorOptionsPages; -class EditorOptionsPage; // ------------------------------------------------------------- @@ -70,9 +71,7 @@ std::map pcell_parameters_from_string (const std::stri // ------------------------------------------------------------- class EDT_PUBLIC Service - : public lay::ViewService, - public lay::Editable, - public lay::Plugin, + : public lay::EditorServiceBase, public db::Object { public: @@ -216,22 +215,6 @@ public: return m_color; } - /** - * @brief Obtain the lay::ViewService interface - */ - lay::ViewService *view_service_interface () - { - return this; - } - - /** - * @brief Obtain the lay::Editable interface - */ - lay::Editable *editable_interface () - { - return this; - } - /** * @brief Get the selection container */ @@ -344,6 +327,11 @@ public: */ virtual void edit_cancel (); + /** + * @brief Triggered by tap - gives the new layer and if required the initial point + */ + virtual void tap (const db::DPoint &initial); + /** * @brief Delete the selected rulers * @@ -383,6 +371,11 @@ protected: */ void selection_to_view (); + /** + * @brief starts editing at the given point. + */ + void begin_edit (const db::DPoint &p); + /** * @brief Reimplemented by the specific implementation of the shape editors * @@ -441,6 +434,11 @@ protected: */ virtual void do_cancel_edit () { } + /** + * @brief Called when a configuration parameter provided by the service base class has changed + */ + virtual void service_configuration_changed (); + /** * @brief Install a marker for representing the edited object * @@ -528,6 +526,16 @@ protected: return m_max_shapes_of_instances; } + bool editing () const + { + return m_editing; + } + + /** + * @brief Point snapping with detailed return value + */ + lay::PointSnapToObjectResult snap2_details (const db::DPoint &p) const; + private: // The layout view that the editor service is attached to lay::LayoutView *mp_view; diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index 691bff357..c15bc6106 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -25,8 +25,8 @@ #include "edtServiceImpl.h" #include "edtPropertiesPages.h" #include "edtInstPropertiesPage.h" -#include "edtPCellParametersDialog.h" #include "edtService.h" +#include "edtPlugin.h" #include "dbEdge.h" #include "dbLibrary.h" #include "dbLibraryManager.h" @@ -51,7 +51,7 @@ ShapeEditService::ShapeEditService (db::Manager *manager, lay::LayoutView *view, : edt::Service (manager, view, shape_types), m_layer (0), m_cv_index (0), mp_cell (0), mp_layout (0), m_combine_mode (CM_Add) { - // .. nothing yet .. + view->current_layer_changed_event.add (this, &ShapeEditService::update_edit_layer); } bool @@ -69,17 +69,11 @@ void ShapeEditService::get_edit_layer () { lay::LayerPropertiesConstIterator cl = view ()->current_layer (); + if (cl.is_null ()) { throw tl::Exception (tl::to_string (QObject::tr ("Please select a layer first"))); } - if (! cl->visible (true)) { - lay::TipDialog td (QApplication::activeWindow (), - tl::to_string (QObject::tr ("You are about to draw on a hidden layer. The result won't be visible.")), - "drawing-on-invisible-layer"); - td.exec_dialog (); - } - int cv_index = cl->cellview_index (); const lay::CellView &cv = view ()->cellview (cv_index); int layer = cl->layer_index (); @@ -88,6 +82,13 @@ ShapeEditService::get_edit_layer () throw tl::Exception (tl::to_string (QObject::tr ("Please select a cell first"))); } + if (! cl->visible (true)) { + lay::TipDialog td (QApplication::activeWindow (), + tl::to_string (QObject::tr ("You are about to draw on a hidden layer. The result won't be visible.")), + "drawing-on-invisible-layer"); + td.exec_dialog (); + } + if (layer < 0 || ! cv->layout ().is_valid_layer ((unsigned int) layer)) { if (cl->has_children ()) { @@ -128,6 +129,79 @@ ShapeEditService::get_edit_layer () } } +void +ShapeEditService::update_edit_layer (const lay::LayerPropertiesConstIterator &cl) +{ + if (! editing ()) { + return; + } + + if (cl.is_null () || cl->has_children ()) { + return; + } + + int cv_index = cl->cellview_index (); + const lay::CellView &cv = view ()->cellview (cv_index); + int layer = cl->layer_index (); + + if (cv_index < 0 || ! cv.is_valid ()) { + return; + } + + if (cv->layout ().cell (cv.cell_index ()).is_proxy ()) { + return; + } + + if (! cl->visible (true)) { + lay::TipDialog td (QApplication::activeWindow (), + tl::to_string (QObject::tr ("You are now drawing on a hidden layer. The result won't be visible.")), + "drawing-on-invisible-layer"); + td.exec_dialog (); + } + + if (layer < 0 || ! cv->layout ().is_valid_layer ((unsigned int) layer)) { + + // create this layer now + const lay::ParsedLayerSource &source = cl->source (true /*real*/); + + db::LayerProperties db_lp; + if (source.has_name ()) { + db_lp.name = source.name (); + } + db_lp.layer = source.layer (); + db_lp.datatype = source.datatype (); + + cv->layout ().insert_layer (db_lp); + + // update the layer index inside the layer view + cl->realize_source (); + + // Hint: we could have taken the new index from insert_layer, but this + // is a nice test: + layer = cl->layer_index (); + tl_assert (layer >= 0); + + } + + m_layer = (unsigned int) layer; + m_cv_index = (unsigned int) cv_index; + m_trans = (cl->trans ().front () * db::CplxTrans (cv->layout ().dbu ()) * cv.context_trans ()).inverted (); + mp_layout = &(cv->layout ()); + mp_cell = &(mp_layout->cell (cv.cell_index ())); + + current_layer_changed (); +} + +void +ShapeEditService::tap (const db::DPoint &initial) +{ + if (editing ()) { + get_edit_layer (); + } else { + begin_edit (initial); + } +} + /** * @brief Deliver a good interpolation between two points m and p * @@ -339,9 +413,18 @@ PolygonService::set_last_point (const db::DPoint &p) } } -void +void +PolygonService::do_mouse_move_inactive (const db::DPoint &p) +{ + lay::PointSnapToObjectResult snap_details = snap2_details (p); + mouse_cursor_from_snap_details (snap_details); +} + +void PolygonService::do_mouse_move (const db::DPoint &p) { + do_mouse_move_inactive (p); + set_cursor (lay::Cursor::cross); if (m_points.size () >= 2) { set_last_point (p); @@ -366,6 +449,7 @@ void PolygonService::do_finish_edit () { deliver_shape (get_polygon ()); + commit_recent (view ()); } db::Polygon @@ -644,9 +728,18 @@ BoxService::update_marker () } } -void +void +BoxService::do_mouse_move_inactive (const db::DPoint &p) +{ + lay::PointSnapToObjectResult snap_details = snap2_details (p); + mouse_cursor_from_snap_details (snap_details); +} + +void BoxService::do_mouse_move (const db::DPoint &p) { + do_mouse_move_inactive (p); + set_cursor (lay::Cursor::cross); m_p2 = snap2 (p); update_marker (); @@ -663,6 +756,7 @@ void BoxService::do_finish_edit () { deliver_shape (get_box ()); + commit_recent (view ()); } void @@ -738,18 +832,21 @@ TextService::do_activated () { m_rot = 0; - // Show editor options dialog to allow entering of width - std::vector edt_main_services = view ()->get_plugins (); - if (edt_main_services.size () > 0) { - edt_main_services [0]->cm_edit_options (); - } - return true; // start editing immediately } -void +void +TextService::do_mouse_move_inactive (const db::DPoint &p) +{ + lay::PointSnapToObjectResult snap_details = snap2_details (p); + mouse_cursor_from_snap_details (snap_details); +} + +void TextService::do_mouse_move (const db::DPoint &p) { + do_mouse_move_inactive (p); + set_cursor (lay::Cursor::cross); m_text.trans (db::DTrans (m_rot, snap2 (p) - db::DPoint ())); update_marker (); @@ -786,6 +883,8 @@ TextService::do_finish_edit () cell ().shapes (layer ()).insert (get_text ()); manager ()->commit (); + commit_recent (view ()); + if (! view ()->text_visible ()) { lay::TipDialog td (QApplication::activeWindow (), @@ -904,12 +1003,6 @@ PathService::do_begin_edit (const db::DPoint &p) bool PathService::do_activated () { - // Show editor options dialog to allow entering of width - std::vector edt_main_services = view ()->get_plugins (); - if (edt_main_services.size () > 0) { - edt_main_services [0]->cm_edit_options (); - } - return false; // don't start editing immediately } @@ -935,9 +1028,18 @@ PathService::set_last_point (const db::DPoint &p) } } -void +void +PathService::do_mouse_move_inactive (const db::DPoint &p) +{ + lay::PointSnapToObjectResult snap_details = snap2_details (p); + mouse_cursor_from_snap_details (snap_details); +} + +void PathService::do_mouse_move (const db::DPoint &p) { + do_mouse_move_inactive (p); + set_cursor (lay::Cursor::cross); if (m_points.size () >= 2) { set_last_point (p); @@ -966,6 +1068,8 @@ PathService::do_finish_edit () m_points.pop_back (); deliver_shape (get_path ()); + + commit_recent (view ()); } void @@ -1094,7 +1198,7 @@ InstService::InstService (db::Manager *manager, lay::LayoutView *view) m_array (false), m_rows (1), m_columns (1), m_row_x (0.0), m_row_y (0.0), m_column_x (0.0), m_column_y (0.0), m_place_origin (false), m_reference_transaction_id (0), - m_needs_update (true), m_has_valid_cell (false), m_in_drag_drop (false), + m_needs_update (true), m_parameters_changed (false), m_has_valid_cell (false), m_in_drag_drop (false), m_current_cell (0), mp_current_layout (0), mp_pcell_decl (0), m_cv_index (-1) { // .. nothing yet .. @@ -1109,12 +1213,6 @@ InstService::properties_page (db::Manager *manager, QWidget *parent) bool InstService::do_activated () { - // Show editor options dialog to allow entering of parameters - std::vector edt_main_services = view ()->get_plugins (); - if (edt_main_services.size () > 0) { - edt_main_services [0]->cm_edit_options (); - } - m_cv_index = view ()->active_cellview_index (); m_has_valid_cell = false; @@ -1137,63 +1235,69 @@ InstService::get_default_layer_for_pcell () bool InstService::drag_enter_event (const db::DPoint &p, const lay::DragDropDataBase *data) -{ +{ const lay::CellDragDropData *cd = dynamic_cast (data); if (view ()->is_editable () && cd && (cd->layout () == & view ()->active_cellview ()->layout () || cd->library ())) { view ()->cancel (); - set_edit_marker (0); - m_cv_index = view ()->active_cellview_index (); - m_in_drag_drop = true; + bool switch_parameters = true; + // configure from the drag/drop data if (cd->library ()) { + + // Reject drag & drop if the target technology does not match + if (cd->library ()->for_technologies () && view ()->cellview (view ()->active_cellview_index ()).is_valid ()) { + if (! cd->library ()->is_for_technology (view ()->cellview (view ()->active_cellview_index ())->tech_name ())) { + return false; + } + } + if (m_lib_name != cd->library ()->get_name ()) { m_lib_name = cd->library ()->get_name (); - m_pcell_parameters.clear (); } + } else { m_lib_name.clear (); } - m_is_pcell = false; - if (cd->is_pcell ()) { const db::PCellDeclaration *pcell_decl = cd->layout ()->pcell_declaration (cd->cell_index ()); - if (pcell_decl) { + if (! pcell_decl) { + return false; + } - if (m_cell_or_pcell_name != pcell_decl->name ()) { - m_cell_or_pcell_name = pcell_decl->name (); - m_pcell_parameters.clear (); - } - - m_is_pcell = true; - - // NOTE: we reuse previous parameters for convenience unless PCell or library has changed - const std::vector &pd = pcell_decl->parameter_declarations(); - for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { - if (i->get_type () == db::PCellParameterDeclaration::t_layer && !i->is_hidden () && !i->is_readonly () && i->get_default ().is_nil ()) { - m_pcell_parameters.insert (std::make_pair (i->get_name (), get_default_layer_for_pcell ())); - } else { - m_pcell_parameters.insert (std::make_pair (i->get_name (), i->get_default ())); - } - } - - do_begin_edit (p); - return true; + if (m_cell_or_pcell_name != pcell_decl->name ()) { + m_cell_or_pcell_name = pcell_decl->name (); + } + if (! cd->pcell_params ().empty ()) { + m_pcell_parameters = pcell_decl->named_parameters (cd->pcell_params ()); + switch_parameters = false; } } else if (cd->layout ()->is_valid_cell_index (cd->cell_index ())) { m_cell_or_pcell_name = cd->layout ()->cell_name (cd->cell_index ()); - do_begin_edit (p); - return true; + } else { + return false; } + switch_cell_or_pcell (switch_parameters); + + sync_to_config (); + m_in_drag_drop = true; + + view ()->switch_mode (plugin_declaration ()->id ()); + + do_begin_edit (p); + + // action taken. + return true; + } return false; @@ -1210,7 +1314,7 @@ InstService::drag_move_event (const db::DPoint &p, const lay::DragDropDataBase * } } -void +void InstService::drag_leave_event () { if (m_in_drag_drop) { @@ -1219,7 +1323,7 @@ InstService::drag_leave_event () } } -bool +bool InstService::selection_applies (const lay::ObjectInstPath &sel) const { return sel.is_cell_inst (); @@ -1228,53 +1332,8 @@ InstService::selection_applies (const lay::ObjectInstPath &sel) const bool InstService::drop_event (const db::DPoint & /*p*/, const lay::DragDropDataBase * /*data*/) { - if (m_in_drag_drop) { - - const lay::CellView &cv = view ()->cellview (m_cv_index); - if (! cv.is_valid ()) { - return false; - } - - make_cell (cv); - - bool accepted = true; - - if (m_has_valid_cell && mp_pcell_decl) { - - std::vector pv = mp_pcell_decl->map_parameters (m_pcell_parameters); - - // Turn off the drag cursor for the modal dialog - QApplication::restoreOverrideCursor (); - - // for PCells dragged show the parameter dialog for a chance to edit the initial parameters - if (! mp_pcell_parameters_dialog.get ()) { - mp_pcell_parameters_dialog.reset (new edt::PCellParametersDialog (view ())); - mp_pcell_parameters_dialog->parameters_changed_event.add (this, &InstService::apply_edits); - } - - if (! mp_pcell_parameters_dialog->exec (mp_current_layout, view (), m_cv_index, mp_pcell_decl, pv)) { - accepted = false; - } else { - m_has_valid_cell = false; - m_pcell_parameters = mp_pcell_decl->named_parameters (mp_pcell_parameters_dialog->get_parameters ()); - } - - } - - set_edit_marker (0); - - if (accepted) { - do_finish_edit (); - } else { - do_cancel_edit (); - } - - sync_to_config (); - return true; - - } else { - return false; - } + m_in_drag_drop = false; + return false; } void @@ -1337,10 +1396,6 @@ InstService::do_begin_edit (const db::DPoint &p) m_trans = db::VCplxTrans (1.0 / cv->layout ().dbu ()) * tv [0] * db::CplxTrans (cv->layout ().dbu ()) * cv.context_trans (); } - lay::Marker *marker = new lay::Marker (view (), m_cv_index, ! show_shapes_of_instances (), show_shapes_of_instances () ? max_shapes_of_instances () : 0); - marker->set_vertex_shape (lay::ViewOp::Cross); - marker->set_vertex_size (9 /*cross vertex size*/); - set_edit_marker (marker); update_marker (); } @@ -1426,9 +1481,18 @@ InstService::make_cell (const lay::CellView &cv) return std::pair (true, inst_cell_index); } -void +void +InstService::do_mouse_move_inactive (const db::DPoint &p) +{ + clear_mouse_cursors (); + add_mouse_cursor (snap (p)); +} + +void InstService::do_mouse_move (const db::DPoint &p) { + do_mouse_move_inactive (p); + set_cursor (lay::Cursor::cross); const lay::CellView &cv = view ()->cellview (m_cv_index); @@ -1470,6 +1534,14 @@ InstService::do_mouse_transform (const db::DPoint &p, db::DFTrans trans) m_column_x = c.x (); m_column_y = c.y (); + dispatcher ()->config_set (cfg_edit_inst_angle, m_angle); + dispatcher ()->config_set (cfg_edit_inst_mirror, m_mirror); + dispatcher ()->config_set (cfg_edit_inst_row_x, m_row_x); + dispatcher ()->config_set (cfg_edit_inst_row_y, m_row_y); + dispatcher ()->config_set (cfg_edit_inst_column_x, m_column_x); + dispatcher ()->config_set (cfg_edit_inst_column_y, m_column_y); + dispatcher ()->config_end (); + // honour the new transformation do_mouse_move (p); } @@ -1510,6 +1582,8 @@ InstService::do_finish_edit () cv->layout ().cleanup (); manager ()->commit (); + commit_recent (view ()); + if (m_in_drag_drop) { lay::ObjectInstPath sel; @@ -1545,6 +1619,8 @@ InstService::do_cancel_edit () m_has_valid_cell = false; m_in_drag_drop = false; + set_edit_marker (0); + // clean up any proxy cells created so far const lay::CellView &cv = view ()->cellview (m_cv_index); if (cv.is_valid ()) { @@ -1552,132 +1628,307 @@ InstService::do_cancel_edit () } } +void +InstService::service_configuration_changed () +{ + m_needs_update = true; +} + bool InstService::configure (const std::string &name, const std::string &value) { if (name == cfg_edit_inst_cell_name) { - m_cell_or_pcell_name = value; - m_needs_update = true; + + if (value != m_cell_or_pcell_name) { + m_cell_or_pcell_name = value; + m_needs_update = true; + } + return true; // taken } if (name == cfg_edit_inst_lib_name) { - m_lib_name = value; - m_needs_update = true; + + if (value != m_lib_name) { + m_lib_name_previous = m_lib_name; + m_lib_name = value; + m_needs_update = true; + } + return true; // taken } if (name == cfg_edit_inst_pcell_parameters) { - m_pcell_parameters = pcell_parameters_from_string (value); - m_is_pcell = ! value.empty (); + std::map pcp = pcell_parameters_from_string (value); + if (pcp != m_pcell_parameters) { + + m_pcell_parameters = pcp; + m_is_pcell = ! value.empty (); + + m_needs_update = true; + m_parameters_changed = true; + + } - m_needs_update = true; return true; // taken } if (name == cfg_edit_inst_place_origin) { - tl::from_string (value, m_place_origin); - m_needs_update = true; + + bool f; + tl::from_string (value, f); + + if (f != m_place_origin) { + m_place_origin = f; + m_needs_update = true; + } + return true; // taken + } if (name == cfg_edit_inst_scale) { - tl::from_string (value, m_scale); - m_needs_update = true; + + double s; + tl::from_string (value, s); + + if (fabs (s - m_scale) > 1e-10) { + m_scale = s; + m_needs_update = true; + } + return true; // taken + } if (name == cfg_edit_inst_angle) { - tl::from_string (value, m_angle); - m_needs_update = true; + + double a; + tl::from_string (value, a); + + if (fabs (a - m_angle) > 1e-10) { + m_angle = a; + m_needs_update = true; + } + return true; // taken } if (name == cfg_edit_inst_mirror) { - tl::from_string (value, m_mirror); - m_needs_update = true; + + bool f; + tl::from_string (value, f); + + if (f != m_mirror) { + m_mirror = f; + m_needs_update = true; + } + return true; // taken + } if (name == cfg_edit_inst_array) { - tl::from_string (value, m_array); - m_needs_update = true; + + bool f; + tl::from_string (value, f); + + if (f != m_array) { + m_array = f; + m_needs_update = true; + } + return true; // taken + } if (name == cfg_edit_inst_rows) { - tl::from_string (value, m_rows); - m_needs_update = true; + + unsigned int v; + tl::from_string (value, v); + + if (v != m_rows) { + m_rows = v; + m_needs_update = true; + } + return true; // taken + } if (name == cfg_edit_inst_row_x) { - tl::from_string (value, m_row_x); - m_needs_update = true; + + double v; + tl::from_string (value, v); + + if (! db::coord_traits::equal (m_row_x, v)) { + m_row_x = v; + m_needs_update = true; + } + return true; // taken + } if (name == cfg_edit_inst_row_y) { - tl::from_string (value, m_row_y); - m_needs_update = true; + + double v; + tl::from_string (value, v); + + if (! db::coord_traits::equal (m_row_y, v)) { + m_row_y = v; + m_needs_update = true; + } + return true; // taken + } if (name == cfg_edit_inst_columns) { - tl::from_string (value, m_columns); - m_needs_update = true; + + unsigned int v; + tl::from_string (value, v); + + if (v != m_columns) { + m_columns = v; + m_needs_update = true; + } + return true; // taken + } if (name == cfg_edit_inst_column_x) { - tl::from_string (value, m_column_x); - m_needs_update = true; + + double v; + tl::from_string (value, v); + + if (! db::coord_traits::equal (m_column_x, v)) { + m_column_x = v; + m_needs_update = true; + } + return true; // taken + } if (name == cfg_edit_inst_column_y) { - tl::from_string (value, m_column_y); - m_needs_update = true; + + double v; + tl::from_string (value, v); + + if (! db::coord_traits::equal (m_column_y, v)) { + m_column_y = v; + m_needs_update = true; + } + return true; // taken + } return edt::Service::configure (name, value); } +void +InstService::switch_cell_or_pcell (bool switch_parameters) +{ + // if the library or cell name has changed, store the current pcell parameters and try to reuse + // an existing parameter set + if (! m_cell_or_pcell_name_previous.empty () && (m_cell_or_pcell_name_previous != m_cell_or_pcell_name || m_lib_name_previous != m_lib_name)) { + + m_stored_pcell_parameters[std::make_pair (m_cell_or_pcell_name_previous, m_lib_name_previous)] = m_pcell_parameters; + + if (switch_parameters) { + + std::map, std::map >::const_iterator p = m_stored_pcell_parameters.find (std::make_pair (m_cell_or_pcell_name, m_lib_name)); + if (p != m_stored_pcell_parameters.end ()) { + m_pcell_parameters = p->second; + } else { + m_pcell_parameters.clear (); + } + + } + + } + + db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name); + const lay::CellView &cv = view ()->cellview (m_cv_index); + + // find the layout the cell has to be looked up: that is either the layout of the current instance or + // the library selected + const db::Layout *layout = 0; + if (lib) { + layout = &lib->layout (); + } else if (cv.is_valid ()) { + layout = &cv->layout (); + } + + if (layout) { + m_is_pcell = layout->pcell_by_name (m_cell_or_pcell_name.c_str ()).first; + } else { + m_is_pcell = false; + } + + // remember the current cell or library name + m_cell_or_pcell_name_previous = m_cell_or_pcell_name; + m_lib_name_previous = m_lib_name; +} + void InstService::config_finalize () { if (m_needs_update) { + + // don't switch parameters if they have been updated explicitly + // since the last "config_finalize". This means the sender of the configuration events + // wants the parameters to be set in a specific way. Don't interfere. + bool switch_parameters = ! m_parameters_changed; + + switch_cell_or_pcell (switch_parameters); + m_has_valid_cell = false; update_marker (); - m_needs_update = false; + + if (switch_parameters) { + // Reflects any changes in PCell parameters in the configuration + // TODO: it's somewhat questionable to do this inside "config_finalize" as this method is supposed + // to reflect changes rather than induce some. + if (m_is_pcell) { + dispatcher ()->config_set (cfg_edit_inst_pcell_parameters, pcell_parameters_to_string (m_pcell_parameters)); + } else { + dispatcher ()->config_set (cfg_edit_inst_pcell_parameters, std::string ()); + } + } + } + m_needs_update = false; + m_parameters_changed = false; + edt::Service::config_finalize (); } -void -InstService::apply_edits() -{ - if (mp_pcell_decl && mp_pcell_parameters_dialog.get ()) { - m_pcell_parameters = mp_pcell_decl->named_parameters (mp_pcell_parameters_dialog->get_parameters ()); - } - - sync_to_config (); -} - void InstService::update_marker () { - lay::Marker *marker = dynamic_cast (edit_marker ()); - if (marker) { - marker->set (); + if (editing ()) { + + lay::Marker *marker = new lay::Marker (view (), m_cv_index, ! show_shapes_of_instances (), show_shapes_of_instances () ? max_shapes_of_instances () : 0); + marker->set_vertex_shape (lay::ViewOp::Cross); + marker->set_vertex_size (9 /*cross vertex size*/); + set_edit_marker (marker); + db::CellInstArray inst; if (get_inst (inst)) { marker->set (inst, m_trans); + } else { + marker->set (); } + + } else { + set_edit_marker (0); } } @@ -1692,14 +1943,9 @@ InstService::get_inst (db::CellInstArray &inst) // compute the instance's transformation db::VCplxTrans pt = (db::CplxTrans (cv->layout ().dbu ()) * m_trans).inverted (); - db::ICplxTrans trans; - if (m_in_drag_drop) { - trans = db::ICplxTrans (1.0, 0.0, false, pt * m_disp - db::Point ()); - } else { - trans = db::ICplxTrans (m_scale, m_angle, m_mirror, pt * m_disp - db::Point ()); - } + db::ICplxTrans trans = db::ICplxTrans (m_scale, m_angle, m_mirror, pt * m_disp - db::Point ()); - if (! m_in_drag_drop && m_array && m_rows > 0 && m_columns > 0) { + if (m_array && m_rows > 0 && m_columns > 0) { db::Vector row = db::Vector (pt * db::DVector (m_row_x, m_row_y)); db::Vector column = db::Vector (pt * db::DVector (m_column_x, m_column_y)); inst = db::CellInstArray (db::CellInst (ci.second), trans, row, column, m_rows, m_columns); diff --git a/src/edt/edt/edtServiceImpl.h b/src/edt/edt/edtServiceImpl.h index 60955035b..f10c82a30 100644 --- a/src/edt/edt/edtServiceImpl.h +++ b/src/edt/edt/edtServiceImpl.h @@ -32,13 +32,12 @@ namespace lay { class CellView; + class LayerPropertiesConstIterator; } namespace edt { -class PCellParametersDialog; - /** * @brief Implementation of the edt::Service for generic shape editing */ @@ -57,15 +56,17 @@ protected: db::Cell &cell () const { return *mp_cell; } db::Layout &layout () const { return *mp_layout; } - void do_mouse_move_inactive (const db::DPoint &p); + virtual void do_mouse_move_inactive (const db::DPoint &p); + virtual void tap (const db::DPoint &initial); + + virtual bool configure (const std::string &name, const std::string &value); - bool configure (const std::string &name, const std::string &value); - protected: std::pair interpolate (const db::DPoint &m, const db::DPoint &o, const db::DPoint &p) const; void deliver_shape (const db::Polygon &poly); void deliver_shape (const db::Path &path); void deliver_shape (const db::Box &box); + virtual void current_layer_changed () { } private: db::VCplxTrans m_trans; @@ -74,6 +75,8 @@ private: db::Cell *mp_cell; db::Layout *mp_layout; combine_mode_type m_combine_mode; + + void update_edit_layer (const lay::LayerPropertiesConstIterator &iter); }; /** @@ -88,6 +91,7 @@ public: virtual lay::PropertiesPage *properties_page (db::Manager *manager, QWidget *parent); virtual void do_begin_edit (const db::DPoint &p); virtual void do_mouse_move (const db::DPoint &p); + virtual void do_mouse_move_inactive (const db::DPoint &p); virtual bool do_mouse_click (const db::DPoint &p); virtual void do_finish_edit (); virtual void do_cancel_edit (); @@ -117,6 +121,7 @@ public: virtual lay::PropertiesPage *properties_page (db::Manager *manager, QWidget *parent); virtual void do_begin_edit (const db::DPoint &p); virtual void do_mouse_move (const db::DPoint &p); + virtual void do_mouse_move_inactive (const db::DPoint &p); virtual bool do_mouse_click (const db::DPoint &p); virtual void do_finish_edit (); virtual void do_cancel_edit (); @@ -143,6 +148,7 @@ public: virtual void do_begin_edit (const db::DPoint &p); virtual void do_mouse_transform (const db::DPoint &p, db::DFTrans trans); virtual void do_mouse_move (const db::DPoint &p); + virtual void do_mouse_move_inactive (const db::DPoint &p); virtual bool do_mouse_click (const db::DPoint &p); virtual void do_finish_edit (); virtual void do_cancel_edit (); @@ -174,6 +180,7 @@ public: virtual void do_begin_edit (const db::DPoint &p); virtual void do_mouse_move (const db::DPoint &p); virtual bool do_mouse_click (const db::DPoint &p); + virtual void do_mouse_move_inactive (const db::DPoint &p); virtual void do_finish_edit (); virtual void do_cancel_edit (); virtual bool do_activated (); @@ -206,6 +213,7 @@ public: virtual lay::PropertiesPage *properties_page (db::Manager *manager, QWidget *parent); virtual void do_begin_edit (const db::DPoint &p); + virtual void do_mouse_move_inactive (const db::DPoint &p); virtual void do_mouse_move (const db::DPoint &p); virtual bool do_mouse_click (const db::DPoint &p); virtual void do_mouse_transform (const db::DPoint &p, db::DFTrans trans); @@ -220,6 +228,8 @@ public: protected: bool configure (const std::string &name, const std::string &value); + void service_configuration_changed (); + void config_finalize (); private: @@ -228,14 +238,16 @@ private: bool m_mirror; db::DPoint m_disp; std::string m_cell_or_pcell_name, m_lib_name; + std::string m_cell_or_pcell_name_previous, m_lib_name_previous; std::map m_pcell_parameters; + std::map, std::map > m_stored_pcell_parameters; bool m_is_pcell; bool m_array; unsigned int m_rows, m_columns; double m_row_x, m_row_y, m_column_x, m_column_y; bool m_place_origin; db::Manager::transaction_id_t m_reference_transaction_id; - bool m_needs_update; + bool m_needs_update, m_parameters_changed; bool m_has_valid_cell; bool m_in_drag_drop; db::cell_index_type m_current_cell; @@ -243,14 +255,13 @@ private: const db::PCellDeclaration *mp_pcell_decl; int m_cv_index; db::ICplxTrans m_trans; - std::auto_ptr mp_pcell_parameters_dialog; void update_marker (); - void apply_edits (); bool get_inst (db::CellInstArray &inst); std::pair make_cell (const lay::CellView &cv); tl::Variant get_default_layer_for_pcell (); void sync_to_config (); + void switch_cell_or_pcell (bool switch_parameters); }; } diff --git a/src/edt/edt/edtUtils.cc b/src/edt/edt/edtUtils.cc index d99f85ab5..a4833a0a1 100644 --- a/src/edt/edt/edtUtils.cc +++ b/src/edt/edt/edtUtils.cc @@ -26,12 +26,121 @@ #include "dbLibrary.h" #include "edtUtils.h" +#include "edtService.h" + #include "layCellView.h" #include "layLayoutView.h" +#include "layEditable.h" #include "tlException.h" namespace edt { +// ------------------------------------------------------------- + +std::string pcell_parameters_to_string (const std::map ¶meters) +{ + std::string param; + + param = "!"; // flags PCells + for (std::map::const_iterator p = parameters.begin (); p != parameters.end (); ++p) { + param += tl::to_word_or_quoted_string (p->first); + param += ":"; + param += p->second.to_parsable_string (); + param += ";"; + } + + return param; +} + +std::map pcell_parameters_from_string (const std::string &s) +{ + tl::Extractor ex (s.c_str ()); + std::map pm; + + ex.test ("!"); + + try { + while (! ex.at_end ()) { + std::string n; + ex.read_word_or_quoted (n); + ex.test (":"); + ex.read (pm.insert (std::make_pair (n, tl::Variant ())).first->second); + ex.test (";"); + } + } catch (...) { + // ignore errors + } + + return pm; +} + +// ------------------------------------------------------------- +// SelectionIterator implementation + +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..460f9a12e 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,18 @@ namespace lay namespace edt { +class Service; + +/** + * @brief Serializes PCell parameters to a string + */ +std::string pcell_parameters_to_string (const std::map ¶meters); + +/** + * @brief Deerializes PCell parameters from a string + */ +std::map pcell_parameters_from_string (const std::string &s); + /** * @brief Fetch PCell parameters from a cell and merge the guiding shapes into them * @@ -79,6 +94,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 diff --git a/src/edt/unit_tests/edtDistributeTests.cc b/src/edt/unit_tests/edtDistributeTests.cc new file mode 100644 index 000000000..1a3288f61 --- /dev/null +++ b/src/edt/unit_tests/edtDistributeTests.cc @@ -0,0 +1,159 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "tlUnitTest.h" + +#include "edtDistribute.h" + +template +static std::string plc2string (const edt::distributed_placer &plc) +{ + std::string s; + for (typename edt::distributed_placer::iterator i = plc.begin (); i != plc.end (); ++i) { + if (! s.empty ()) { + s += ","; + } + s += tl::to_string (i->first); + s += "["; + s += tl::to_string (i->second); + s += "]"; + } + return s; +} + +TEST(1) +{ + edt::distributed_placer placer; + + placer.insert (db::Box (1000, 0, 1100, 200), 0); + placer.insert (db::Box (2000, 0, 2100, 500), 1); + placer.insert (db::Box (0, -100, 100, 100), 2); + placer.insert (db::Box (1000, 100, 1050, 250), 3); + placer.insert (db::Box (1050, -50, 1100, 150), 4); + + edt::distributed_placer p; + + p = placer; + p.distribute_h (-1, 2, 0, 100); + + EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(200,0;300,200)[0],(400,100;450,250)[3],(550,-50;600,150)[4],(700,0;800,500)[1]"); + + p = placer; + p.distribute_h (-1, -1, 0, 100); + + EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(200,-100;300,100)[0],(400,-100;450,50)[3],(550,-100;600,100)[4],(700,-100;800,400)[1]"); + + p = placer; + p.distribute_h (-1, 0, 0, 100); + + EXPECT_EQ (plc2string (p), "(0,100;100,300)[2],(200,100;300,300)[0],(400,125;450,275)[3],(550,100;600,300)[4],(700,-50;800,450)[1]"); + + p = placer; + p.distribute_h (-1, 1, 0, 100); + + EXPECT_EQ (plc2string (p), "(0,300;100,500)[2],(200,300;300,500)[0],(400,350;450,500)[3],(550,300;600,500)[4],(700,0;800,500)[1]"); + + p = placer; + p.distribute_h (-1, 2, 100, 0); + + EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(100,0;200,200)[0],(200,100;250,250)[3],(300,-50;350,150)[4],(400,0;500,500)[1]"); + + p = placer; + p.distribute_h (-1, 2, 0, 0); + + EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(100,0;200,200)[0],(200,100;250,250)[3],(250,-50;300,150)[4],(300,0;400,500)[1]"); + + p = placer; + p.distribute_h (1, 2, 0, 100); + + EXPECT_EQ (plc2string (p), "(1300,-100;1400,100)[2],(1500,100;1550,250)[3],(1650,-50;1700,150)[4],(1800,0;1900,200)[0],(2000,0;2100,500)[1]"); + + p = placer; + p.distribute_v (-1, 2, 0, 100); + + EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(1050,200;1100,400)[4],(1000,500;1100,700)[0],(2000,800;2100,1300)[1],(1000,1400;1050,1550)[3]"); +} + + +TEST(2) +{ + edt::distributed_placer placer; + + placer.insert (db::Box (-5, 1, 95, 101), 0); + placer.insert (db::Box (1, 95, 101, 195), 1); + placer.insert (db::Box (110, 105, 210, 205), 2); + placer.insert (db::Box (101, 0, 201, 100), 3); + + edt::distributed_placer p; + + p = placer; + p.distribute_matrix (-1, 0, 0, -1, 0, 0); + + EXPECT_EQ (plc2string (p), "(-5,0;95,100)[0],(-5,100;95,200)[1],(95,100;195,200)[2],(95,0;195,100)[3]"); +} + +TEST(3) +{ + edt::distributed_placer placer; + + placer.insert (db::Box (0, 20, 1, 23), 0); + placer.insert (db::Box (3, 8, 8, 19), 1); + placer.insert (db::Box (6, 0, 12, 5), 2); + placer.insert (db::Box (13, 1, 19, 6), 3); + placer.insert (db::Box (10, 16, 11, 17), 4); + + edt::distributed_placer p; + + p = placer; + p.distribute_matrix (-1, 0, 0, -1, 0, 0); + + EXPECT_EQ (plc2string (p), "(0,17;1,20)[0],(1,5;6,16)[1],(6,0;12,5)[2],(13,0;19,5)[3],(12,16;13,17)[4]"); +} + +TEST(4) +{ + edt::distributed_placer placer; + + placer.insert (db::Box (0, 16, 1, 20), 0); + placer.insert (db::Box (0, 8, 5, 19), 1); + placer.insert (db::Box (0, 0, 12, 5), 2); + placer.insert (db::Box (12, 1, 19, 6), 3); + placer.insert (db::Box (0, 18, 1, 19), 4); + + edt::distributed_placer p; + + p = placer; + p.distribute_matrix (-1, 0, 0, 1, 0, 0); + + EXPECT_EQ (plc2string (p), "(6,9;7,13)[0],(1,9;6,20)[1],(0,4;12,9)[2],(12,4;19,9)[3],(0,9;1,10)[4]"); + + p = placer; + p.distribute_matrix (1, 10, 0, -1, 10, 0); + + EXPECT_EQ (plc2string (p), "(-38,30;-37,34)[0],(-18,10;-13,21)[1],(-8,0;4,5)[2],(12,0;19,5)[3],(-28,30;-27,31)[4]"); + + p = placer; + p.distribute_matrix (1, 0, 1, 1, 0, 1); + + EXPECT_EQ (plc2string (p), "(-9,16;-8,20)[0],(-7,9;-2,20)[1],(-1,3;11,8)[2],(12,3;19,8)[3],(-11,19;-10,20)[4]"); +} + diff --git a/src/edt/unit_tests/unit_tests.pro b/src/edt/unit_tests/unit_tests.pro index 630f1cd9b..14be20d9e 100644 --- a/src/edt/unit_tests/unit_tests.pro +++ b/src/edt/unit_tests/unit_tests.pro @@ -8,6 +8,7 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ edtBasicTests.cc \ + edtDistributeTests.cc INCLUDEPATH += $$EDT_INC $$TL_INC $$LAYBASIC_INC $$DB_INC $$GSI_INC DEPENDPATH += $$EDT_INC $$TL_INC $$LAYBASIC_INC $$DB_INC $$GSI_INC diff --git a/src/img/img/ImagePropertiesPage.ui b/src/img/img/ImagePropertiesPage.ui index d0453605b..2ea522bdd 100644 --- a/src/img/img/ImagePropertiesPage.ui +++ b/src/img/img/ImagePropertiesPage.ui @@ -1058,16 +1058,6 @@ p, li { white-space: pre-wrap; } 0 - - - - Preview (Auto apply) - - - true - - - @@ -1141,7 +1131,6 @@ p, li { white-space: pre-wrap; } g_sb b_slider b_sb - preview_cbx reset_pb contrast_sb gamma_sb diff --git a/src/img/img/imgPropertiesPage.cc b/src/img/img/imgPropertiesPage.cc index f53c0a100..e16d1291d 100644 --- a/src/img/img/imgPropertiesPage.cc +++ b/src/img/img/imgPropertiesPage.cc @@ -26,6 +26,7 @@ #include "imgStream.h" #include "layLayoutView.h" #include "layFileDialog.h" +#include "layQtTools.h" #include "tlExceptions.h" #include "tlFileUtils.h" @@ -73,6 +74,7 @@ void PropertiesPage::init () { m_no_signals = false; + m_in_color_mapping_signal = false; setupUi (this); @@ -108,6 +110,7 @@ PropertiesPage::init () connect (browse_pb, SIGNAL (clicked ()), this, SLOT (browse ())); connect (colors, SIGNAL (color_changed (std::pair)), false_color_control, SLOT (set_current_color (std::pair))); connect (false_color_control, SIGNAL (selection_changed (std::pair)), colors, SLOT (set_color (std::pair))); + connect (false_color_control, SIGNAL (color_mapping_changed ()), this, SLOT (color_mapping_changed ())); connect (brightness_slider, SIGNAL (valueChanged (int)), this, SLOT (brightness_slider_changed (int))); connect (brightness_sb, SIGNAL (valueChanged (int)), this, SLOT (brightness_spinbox_changed (int))); @@ -122,15 +125,22 @@ PropertiesPage::init () connect (b_slider, SIGNAL (valueChanged (int)), this, SLOT (blue_slider_changed (int))); connect (b_sb, SIGNAL (valueChanged (double)), this, SLOT (blue_spinbox_changed (double))); - connect (false_color_control, SIGNAL (color_mapping_changed ()), this, SLOT (color_mapping_changed ())); - connect (false_color_control, SIGNAL (selection_changed ()), this, SLOT (color_mapping_changed ())); - connect (from_le, SIGNAL (returnPressed ()), this, SLOT (min_max_return_pressed ())); - connect (to_le, SIGNAL (returnPressed ()), this, SLOT (min_max_return_pressed ())); - connect (value_le, SIGNAL (returnPressed ()), this, SLOT (value_return_pressed ())); + connect (from_le, SIGNAL (editingFinished ()), this, SLOT (min_max_value_changed ())); + connect (to_le, SIGNAL (editingFinished ()), this, SLOT (min_max_value_changed ())); + connect (value_le, SIGNAL (editingFinished ()), this, SLOT (value_changed ())); + connect (width_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (height_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (x_offset_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (y_offset_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (angle_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (shear_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (persp_tx_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + connect (persp_ty_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ())); + + connect (mirror_cbx, SIGNAL (clicked ()), this, SIGNAL (edited ())); connect (reset_pb, SIGNAL (clicked ()), this, SLOT (reset_pressed ())); connect (save_pb, SIGNAL (clicked ()), this, SLOT (save_pressed ())); - connect (preview_cbx, SIGNAL (clicked ()), this, SLOT (preview_checked ())); connect (define_landmarks_pb, SIGNAL (clicked ()), this, SLOT (define_landmarks_pressed ())); } @@ -202,11 +212,42 @@ PropertiesPage::readonly () return false; } -void -PropertiesPage::min_max_return_pressed () +void +PropertiesPage::get_xmin_xmax (double &xmin, double &xmax, bool &has_error_out) { -BEGIN_PROTECTED + bool has_error = false; + try { + tl::from_string (tl::to_string (from_le->text ()), xmin); + lay::indicate_error (from_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (from_le, &ex); + has_error = true; + } + + try { + tl::from_string (tl::to_string (to_le->text ()), xmax); + lay::indicate_error (to_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (to_le, &ex); + has_error = true; + } + + if (! has_error && xmin >= xmax) { + tl::Exception ex (tl::to_string (QObject::tr ("Invalid data value range (min. value must be less than max. value)"))); + lay::indicate_error (from_le, &ex); + lay::indicate_error (to_le, &ex); + has_error = true; + } + + if (has_error) { + has_error_out = true; + } +} + +void +PropertiesPage::min_max_value_changed () +{ value_le->setText (QString ()); value_le->setEnabled (false); @@ -214,10 +255,11 @@ BEGIN_PROTECTED colors->set_single_mode (false); double xmin, xmax; - tl::from_string (tl::to_string (from_le->text ()), xmin); - tl::from_string (tl::to_string (to_le->text ()), xmax); - if (xmin >= xmax) { - throw tl::Exception (tl::to_string (QObject::tr ("Invalid data value range (min. value must be less than max. value)"))); + bool has_error = false; + get_xmin_xmax (xmin, xmax, has_error); + + if (has_error) { + return; } if (false_color_control->has_selection () && false_color_control->selected_node () > 0 && false_color_control->selected_node () < int (false_color_control->nodes ().size ()) - 1) { @@ -236,9 +278,7 @@ BEGIN_PROTECTED recompute_histogram (); - preview (); - -END_PROTECTED + emit edited (); } void @@ -246,22 +286,20 @@ PropertiesPage::color_mapping_changed () { if (! m_no_signals) { + bool has_error = false; + value_le->setText (QString ()); value_le->setEnabled (false); colors->setEnabled (false_color_control->has_selection ()); colors->set_single_mode (false); - try { + if (false_color_control->has_selection () && false_color_control->selected_node () > 0 && false_color_control->selected_node () < int (false_color_control->nodes ().size ()) - 1) { - if (false_color_control->has_selection () && false_color_control->selected_node () > 0 && false_color_control->selected_node () < int (false_color_control->nodes ().size ()) - 1) { + double xmin, xmax; + get_xmin_xmax (xmin, xmax, has_error); - double xmin, xmax; - tl::from_string (tl::to_string (from_le->text ()), xmin); - tl::from_string (tl::to_string (to_le->text ()), xmax); - if (xmin >= xmax) { - throw tl::Exception (""); - } + if (! has_error) { double x = false_color_control->nodes () [false_color_control->selected_node ()].first; double xx = x * (xmax - xmin) + xmin; @@ -269,46 +307,57 @@ PropertiesPage::color_mapping_changed () value_le->setText (tl::to_qstring (tl::sprintf ("%.4g", xx))); value_le->setEnabled (true); - } else if (false_color_control->has_selection ()) { - - colors->set_single_mode (true); - } - } catch (...) { } + } else if (false_color_control->has_selection ()) { - preview (); + colors->set_single_mode (true); + + } + + if (! has_error) { + m_in_color_mapping_signal = true; + emit edited (); + m_in_color_mapping_signal = false; + } } } void -PropertiesPage::value_return_pressed () +PropertiesPage::value_changed () { -BEGIN_PROTECTED + double xx = 0; + bool has_error = false; double xmin, xmax; - tl::from_string (tl::to_string (from_le->text ()), xmin); - tl::from_string (tl::to_string (to_le->text ()), xmax); - if (xmin >= xmax) { - throw tl::Exception (tl::to_string (QObject::tr ("Invalid data value range (min. value must be less than max. value)"))); - } + get_xmin_xmax (xmin, xmax, has_error); double x = 0.0; - tl::from_string (tl::to_string (value_le->text ()), x); - - double xx = (x - xmin) / (xmax - xmin); - if (xx < 0 || xx > 1.0) { - throw tl::Exception (tl::to_string (QObject::tr ("The position entered (%g) must be between the minimum (%g) and maximum (%g) value")), x, xmin, xmax); + try { + tl::from_string (tl::to_string (value_le->text ()), x); + lay::indicate_error (value_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (value_le, &ex); + has_error = true; } - m_no_signals = true; - false_color_control->set_current_position (xx); - m_no_signals = false; + xx = (x - xmin) / (xmax - xmin); + if (! has_error && (xx < 0 || xx > 1.0)) { + tl::Exception ex (tl::to_string (QObject::tr ("The position entered (%g) must be between the minimum (%g) and maximum (%g) value")), x, xmin, xmax); + lay::indicate_error (value_le, &ex); + has_error = true; + } - preview (); + if (! has_error) { -END_PROTECTED + m_no_signals = true; + false_color_control->set_current_position (xx); + m_no_signals = false; + + emit edited (); + + } } inline double @@ -320,6 +369,10 @@ round_to_zero (double x) void PropertiesPage::update () { + if (m_in_color_mapping_signal) { + return; + } + m_no_signals = true; if (mp_service) { @@ -498,7 +551,7 @@ PropertiesPage::brightness_slider_changed (int value) m_no_signals = true; brightness_sb->setValue (value); - preview (); + emit edited (); m_no_signals = false; } @@ -511,7 +564,7 @@ PropertiesPage::brightness_spinbox_changed (int value) m_no_signals = true; brightness_slider->setValue (value); - preview (); + emit edited (); m_no_signals = false; } @@ -524,7 +577,7 @@ PropertiesPage::contrast_slider_changed (int value) m_no_signals = true; contrast_sb->setValue (value); - preview (); + emit edited (); m_no_signals = false; } @@ -537,7 +590,7 @@ PropertiesPage::contrast_spinbox_changed (int value) m_no_signals = true; contrast_slider->setValue (value); - preview (); + emit edited (); m_no_signals = false; } @@ -556,7 +609,7 @@ PropertiesPage::gamma_spinbox_changed (double value) gamma_slider->setValue (50 + int (0.5 + (value - 1.0) / (max_gamma - 1.0) * 50.0)); } - preview (); + emit edited (); m_no_signals = false; } @@ -578,7 +631,7 @@ PropertiesPage::gamma_slider_changed (int value) } gamma_sb->setValue (gamma); - preview (); + emit edited (); m_no_signals = false; } @@ -595,7 +648,7 @@ PropertiesPage::red_slider_changed (int value) double gain = value * 0.02; r_sb->setValue (gain); - preview (); + emit edited (); m_no_signals = false; } @@ -610,7 +663,7 @@ PropertiesPage::red_spinbox_changed (double value) m_no_signals = true; r_slider->setValue (int (0.5 + value * 50.0)); - preview (); + emit edited (); m_no_signals = false; } @@ -627,7 +680,7 @@ PropertiesPage::green_slider_changed (int value) double gain = value * 0.02; g_sb->setValue (gain); - preview (); + emit edited (); m_no_signals = false; } @@ -642,7 +695,7 @@ PropertiesPage::green_spinbox_changed (double value) m_no_signals = true; g_slider->setValue (int (0.5 + value * 50.0)); - preview (); + emit edited (); m_no_signals = false; } @@ -659,7 +712,7 @@ PropertiesPage::blue_slider_changed (int value) double gain = value * 0.02; b_sb->setValue (gain); - preview (); + emit edited (); m_no_signals = false; } @@ -674,9 +727,9 @@ PropertiesPage::blue_spinbox_changed (double value) m_no_signals = true; b_slider->setValue (int (0.5 + value * 50.0)); - preview (); + emit edited (); - m_no_signals = false; + m_no_signals = false; } void @@ -686,6 +739,7 @@ PropertiesPage::black_to_white () nodes.push_back (std::make_pair (0.0, std::make_pair (QColor (0, 0, 0), QColor (0, 0, 0)))); nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (255, 255, 255), QColor (255, 255, 255)))); false_color_control->set_nodes (nodes); + emit edited (); } void @@ -695,6 +749,7 @@ PropertiesPage::white_to_black () nodes.push_back (std::make_pair (0.0, std::make_pair (QColor (255, 255, 255), QColor (255, 255, 255)))); nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (0, 0, 0), QColor (0, 0, 0)))); false_color_control->set_nodes (nodes); + emit edited (); } void @@ -704,7 +759,7 @@ PropertiesPage::red_to_blue () nodes.push_back (std::make_pair (0.0, std::make_pair (QColor (255, 0, 0), QColor (255, 0, 0)))); nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (0, 0, 255), QColor (0, 0, 255)))); false_color_control->set_nodes (nodes); - + emit edited (); } void @@ -714,6 +769,7 @@ PropertiesPage::blue_to_red () nodes.push_back (std::make_pair (0.0, std::make_pair (QColor (0, 0, 255), QColor (0, 0, 255)))); nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (255, 0, 0), QColor (255, 0, 0)))); false_color_control->set_nodes (nodes); + emit edited (); } void @@ -725,11 +781,14 @@ PropertiesPage::reverse_color_order () std::swap (nodes [i].second.first, nodes [nodes.size () - 1 - i].second.second); } false_color_control->set_nodes (nodes); + emit edited (); } void PropertiesPage::apply () { + bool has_error = false; + db::Matrix3d matrix = mp_direct_image->matrix (); // The observer distance for perspective distortion is the average of the images width and height. @@ -741,45 +800,89 @@ PropertiesPage::apply () double w = matrix.mag_x (), h = matrix.mag_y (), x = matrix.disp ().x (), y = matrix.disp ().y (), a = matrix.angle (), sa = matrix.shear_angle (), tx = matrix.perspective_tilt_x (z), ty = matrix.perspective_tilt_y (z); - bool mirror; - - if (width_le->text () != tl::to_qstring (tl::micron_to_string (matrix.mag_x ()))) { + try { tl::from_string (tl::to_string (width_le->text ()), w); + if (w <= 0.0 || h <= 0.0) { + throw tl::Exception (tl::to_string (QObject::tr ("Pixel width or height must be positive, non-null values"))); + } + lay::indicate_error (width_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (width_le, &ex); + has_error = true; } - if (height_le->text () != tl::to_qstring (tl::micron_to_string (matrix.mag_y ()))) { + + try { tl::from_string (tl::to_string (height_le->text ()), h); + lay::indicate_error (height_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (height_le, &ex); + has_error = true; } - if (x_offset_le->text () != tl::to_qstring (tl::micron_to_string (round_to_zero (matrix.disp ().x ())))) { + + try { tl::from_string (tl::to_string (x_offset_le->text ()), x); + lay::indicate_error (x_offset_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (x_offset_le, &ex); + has_error = true; } - if (y_offset_le->text () != tl::to_qstring (tl::micron_to_string (round_to_zero (matrix.disp ().y ())))) { + + try { tl::from_string (tl::to_string (y_offset_le->text ()), y); + lay::indicate_error (y_offset_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (y_offset_le, &ex); + has_error = true; } - if (angle_le->text () != tl::to_qstring (tl::to_string (round_to_zero (matrix.angle ())))) { + + try { tl::from_string (tl::to_string (angle_le->text ()), a); + lay::indicate_error (angle_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (angle_le, &ex); + has_error = true; } - if (shear_le->text () != tl::to_qstring (tl::to_string (round_to_zero (matrix.shear_angle ())))) { + + try { tl::from_string (tl::to_string (shear_le->text ()), sa); + if (sa <= -45 || sa >= 45) { + throw tl::Exception (tl::to_string (QObject::tr ("The shear angle must be larger than -45 and less than 45 degree"))); + } + lay::indicate_error (shear_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (shear_le, &ex); + has_error = true; } - if (persp_tx_le->text () != tl::to_qstring (tl::to_string (round_to_zero (matrix.perspective_tilt_x (z))))) { + + try { tl::from_string (tl::to_string (persp_tx_le->text ()), tx); + if (tx <= -90 || tx >= 90) { + throw tl::Exception (tl::to_string (QObject::tr ("The perspective tilt angles must be larger than -90 and less than 90 degree"))); + } + lay::indicate_error (persp_tx_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (persp_tx_le, &ex); + has_error = true; } - if (persp_ty_le->text () != tl::to_qstring (tl::to_string (round_to_zero (matrix.perspective_tilt_y (z))))) { + + try { tl::from_string (tl::to_string (persp_ty_le->text ()), ty); + if (ty <= -90 || ty >= 90) { + throw tl::Exception (tl::to_string (QObject::tr ("The perspective tilt angles must be larger than -90 and less than 90 degree"))); + } + lay::indicate_error (persp_ty_le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (persp_ty_le, &ex); + has_error = true; } - mirror = mirror_cbx->isChecked (); + bool mirror = mirror_cbx->isChecked (); - if (w <= 0.0 || h <= 0.0) { - throw tl::Exception (tl::to_string (QObject::tr ("Pixel width or height must be positive, non-null values"))); - } + double xmin, xmax; + get_xmin_xmax (xmin, xmax, has_error); - if (sa <= -45 || sa >= 45) { - throw tl::Exception (tl::to_string (QObject::tr ("The shear angle must be larger than -45 and less than 45 degree"))); - } - - if (tx <= -90 || tx >= 90 || ty <= -90 || ty >= 90) { - throw tl::Exception (tl::to_string (QObject::tr ("The perspective tilt angles must be larger than -90 and less than 90 degree"))); + if (has_error) { + throw tl::Exception (tl::to_string (tr ("At least one value is invalid - see highlighted entry fields"))); } // Compute the new observer distance @@ -790,13 +893,6 @@ PropertiesPage::apply () matrix = db::Matrix3d::disp (db::DVector (x, y)) * db::Matrix3d::perspective (tx, ty, z) * db::Matrix3d::rotation (a) * db::Matrix3d::shear (sa) * db::Matrix3d::mag (w, h) * db::Matrix3d::mirror (mirror); mp_direct_image->set_matrix (matrix); - double xmin, xmax; - tl::from_string (tl::to_string (from_le->text ()), xmin); - tl::from_string (tl::to_string (to_le->text ()), xmax); - if (xmin >= xmax) { - throw tl::Exception (tl::to_string (QObject::tr ("Invalid data value range (min. value must be less than max. value)"))); - } - mp_direct_image->set_min_value (xmin); mp_direct_image->set_max_value (xmax); @@ -891,30 +987,7 @@ PropertiesPage::reset_pressed () m_no_signals = false; - preview (); -} - -void -PropertiesPage::preview_checked () -{ - preview (); -} - -void -PropertiesPage::preview () -{ - if (preview_cbx->isChecked ()) { - - BEGIN_PROTECTED_CLEANUP - - apply (); // this is a HACK, because it changes the current object - - END_PROTECTED_CLEANUP - { - preview_cbx->setChecked (false); - } - - } + emit edited (); } void @@ -922,7 +995,9 @@ PropertiesPage::define_landmarks_pressed () { if (mp_direct_image) { img::LandmarksDialog dialog (this, *mp_direct_image); - dialog.exec (); + if (dialog.exec ()) { + emit edited (); + } } } diff --git a/src/img/img/imgPropertiesPage.h b/src/img/img/imgPropertiesPage.h index 1de6a3f88..8a45bd186 100644 --- a/src/img/img/imgPropertiesPage.h +++ b/src/img/img/imgPropertiesPage.h @@ -64,7 +64,7 @@ public: private slots: void browse (); - void value_return_pressed (); + void value_changed (); void color_mapping_changed (); void brightness_slider_changed (int value); void brightness_spinbox_changed (int value); @@ -83,8 +83,7 @@ private slots: void red_to_blue (); void blue_to_red (); void reverse_color_order (); - void min_max_return_pressed (); - void preview_checked (); + void min_max_value_changed (); void reset_pressed (); void save_pressed (); void define_landmarks_pressed (); @@ -95,11 +94,12 @@ private: img::Service *mp_service; img::Object *mp_direct_image; bool m_no_signals; + bool m_in_color_mapping_signal; void recompute_histogram (); void invalidate (); void init (); - void preview (); + void get_xmin_xmax (double &xmin, double &xmax, bool &has_error_out); }; } diff --git a/src/lay/lay/HelpDialog.ui b/src/lay/lay/HelpDialog.ui new file mode 100644 index 000000000..19c3f1e66 --- /dev/null +++ b/src/lay/lay/HelpDialog.ui @@ -0,0 +1,75 @@ + + + HelpDialog + + + + 0 + 0 + 900 + 500 + + + + Assistant + + + + 9 + + + 4 + + + 9 + + + 9 + + + + + + 0 + 1 + + + + + + + + QDialogButtonBox::Close + + + + + + + + lay::BrowserPanel + QWidget +
layBrowserPanel.h
+ 1 +
+
+ + + + button_frame + rejected() + HelpDialog + accept() + + + 436 + 487 + + + 309 + 498 + + + + +
diff --git a/src/lay/lay/images/back.png b/src/lay/lay/images/back.png index bfd1a9f3a..f106b46a5 100644 Binary files a/src/lay/lay/images/back.png and b/src/lay/lay/images/back.png differ diff --git a/src/lay/lay/images/back_16.png b/src/lay/lay/images/back_16.png index b7d8e2918..b5c926f50 100644 Binary files a/src/lay/lay/images/back_16.png and b/src/lay/lay/images/back_16.png differ diff --git a/src/lay/lay/images/bookmark.png b/src/lay/lay/images/bookmark.png new file mode 100644 index 000000000..c315058ce Binary files /dev/null and b/src/lay/lay/images/bookmark.png differ diff --git a/src/lay/lay/images/bookmark_16.png b/src/lay/lay/images/bookmark_16.png new file mode 100644 index 000000000..59767c43a Binary files /dev/null and b/src/lay/lay/images/bookmark_16.png differ diff --git a/src/lay/lay/images/forward.png b/src/lay/lay/images/forward.png index 19b9eb896..6dbc76455 100644 Binary files a/src/lay/lay/images/forward.png and b/src/lay/lay/images/forward.png differ diff --git a/src/lay/lay/images/forward_16.png b/src/lay/lay/images/forward_16.png index 23cbccb8a..fa9e7a261 100644 Binary files a/src/lay/lay/images/forward_16.png and b/src/lay/lay/images/forward_16.png differ diff --git a/src/lay/lay/images/home.png b/src/lay/lay/images/home.png index eb50c31ea..7cee0be19 100644 Binary files a/src/lay/lay/images/home.png and b/src/lay/lay/images/home.png differ diff --git a/src/lay/lay/images/next_topic.png b/src/lay/lay/images/next_topic.png index 63e9f391f..2ba1acba3 100644 Binary files a/src/lay/lay/images/next_topic.png and b/src/lay/lay/images/next_topic.png differ diff --git a/src/lay/lay/images/prev_topic.png b/src/lay/lay/images/prev_topic.png index 46a833c67..141bc71f8 100644 Binary files a/src/lay/lay/images/prev_topic.png and b/src/lay/lay/images/prev_topic.png differ diff --git a/src/lay/lay/lay.pro b/src/lay/lay/lay.pro index 0fd1f6a7b..f2ba59502 100644 --- a/src/lay/lay/lay.pro +++ b/src/lay/lay/lay.pro @@ -9,9 +9,11 @@ DEFINES += MAKE_LAY_LIBRARY HEADERS = \ layApplication.h \ layClipDialog.h \ + layControlWidgetStack.h \ layCrashMessage.h \ layFillDialog.h \ layGSIHelpProvider.h \ + layHelpAboutDialog.h \ layHelpDialog.h \ layHelpProvider.h \ layHelpSource.h \ @@ -25,6 +27,7 @@ HEADERS = \ layMainWindow.h \ layNavigator.h \ layProgress.h \ + layProgressDialog.h \ layProgressWidget.h \ layResourceHelpProvider.h \ layRuntimeErrorForm.h \ @@ -35,6 +38,7 @@ HEADERS = \ laySettingsForm.h \ layTechSetupDialog.h \ layTextProgress.h \ + layTextProgressDelegate.h \ layVersion.h \ layCommon.h \ layConfig.h \ @@ -57,7 +61,8 @@ HEADERS = \ layMacroEditorSetupPage.h \ layPasswordDialog.h \ layForceLink.h \ - layInit.h + layInit.h \ + layViewWidgetStack.h FORMS = \ ClipDialog.ui \ @@ -66,6 +71,7 @@ FORMS = \ DeleteModeDialog.ui \ FillDialog.ui \ HelpAboutDialog.ui \ + HelpDialog.ui \ LogViewerDialog.ui \ MacroEditorDialog.ui \ MacroPropertiesDialog.ui \ @@ -112,9 +118,11 @@ SOURCES = \ gsiDeclLayMainWindow.cc \ layApplication.cc \ layClipDialog.cc \ + layControlWidgetStack.cc \ layCrashMessage.cc \ layFillDialog.cc \ layGSIHelpProvider.cc \ + layHelpAboutDialog.cc \ layHelpDialog.cc \ layHelpProvider.cc \ layHelpSource.cc \ @@ -128,6 +136,7 @@ SOURCES = \ layMainWindow.cc \ layNavigator.cc \ layProgress.cc \ + layProgressDialog.cc \ layProgressWidget.cc \ layResourceHelpProvider.cc \ layRuntimeErrorForm.cc \ @@ -139,6 +148,7 @@ SOURCES = \ laySettingsForm.cc \ layTechSetupDialog.cc \ layTextProgress.cc \ + layTextProgressDelegate.cc \ layVersion.cc \ layMacroController.cc \ layTechnologyController.cc \ @@ -159,7 +169,8 @@ SOURCES = \ layMacroEditorSetupPage.cc \ layPasswordDialog.cc \ layForceLink.cc \ - layInit.cc + layInit.cc \ + layViewWidgetStack.cc RESOURCES = layBuildInMacros.qrc \ layHelpResources.qrc \ diff --git a/src/lay/lay/layConfig.h b/src/lay/lay/layConfig.h index 9c5e9d58a..541d9f4ef 100644 --- a/src/lay/lay/layConfig.h +++ b/src/lay/lay/layConfig.h @@ -40,6 +40,9 @@ static const std::string cfg_synchronized_views ("synchronized-views"); static const std::string cfg_edit_mode ("edit-mode"); static const std::string cfg_custom_macro_paths ("custom-macro-paths"); static const std::string cfg_mru ("mru"); +static const std::string cfg_mru_layer_properties ("mru-layer-properties"); +static const std::string cfg_mru_sessions ("mru-sessions"); +static const std::string cfg_mru_bookmarks ("mru-bookmarks"); static const std::string cfg_technologies ("technology-data"); static const std::string cfg_key_bindings ("key-bindings"); static const std::string cfg_menu_items_hidden ("menu-items-hidden"); @@ -57,6 +60,7 @@ static const std::string cfg_layout_file_watcher_enabled ("layout-file-watcher-e static const std::string cfg_window_geometry ("window-geometry"); static const std::string cfg_micron_digits ("digits-micron"); static const std::string cfg_dbu_digits ("digits-dbu"); +static const std::string cfg_assistant_bookmarks ("assistant-bookmarks"); } diff --git a/src/lay/lay/layControlWidgetStack.cc b/src/lay/lay/layControlWidgetStack.cc new file mode 100644 index 000000000..a4806323e --- /dev/null +++ b/src/lay/lay/layControlWidgetStack.cc @@ -0,0 +1,141 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "layControlWidgetStack.h" + +#include + +namespace lay +{ + +ControlWidgetStack::ControlWidgetStack(QWidget *parent, const char *name) + : QFrame (parent), mp_current_widget (0) +{ + setObjectName (QString::fromUtf8 (name)); + + // Background ist a simple label without a text currently + mp_bglabel = new QLabel (this); + mp_bglabel->setAutoFillBackground (true); + mp_bglabel->setAlignment (Qt::AlignVCenter | Qt::AlignHCenter); + mp_bglabel->show (); +} + +void ControlWidgetStack::focusInEvent(QFocusEvent *) +{ + for (size_t i = 0; i < m_widgets.size (); ++i) { + if (m_widgets [i]->isVisible ()) { + m_widgets [i]->setFocus (); + break; + } + } +} + +void ControlWidgetStack::add_widget(QWidget *w) +{ + m_widgets.push_back (w); + w->setParent (this); + resize_children (); + raise_widget (m_widgets.size () - 1); + + int mw = 0; + for (size_t i = 0; i < m_widgets.size (); ++i) { + mw = std::max (m_widgets [i]->sizeHint ().width (), mw); + mw = std::max (m_widgets [i]->minimumWidth (), mw); + } + + if (mw > minimumWidth ()) { + setMinimumWidth (mw); + resize (minimumWidth (), height ()); + } +} + +QSize ControlWidgetStack::sizeHint() const +{ + int w = 0; + for (size_t i = 0; i < m_widgets.size (); ++i) { + w = std::max (m_widgets [i]->sizeHint ().width (), w); + } + return QSize (w, 0); +} + +void ControlWidgetStack::remove_widget(size_t index) +{ + if (index < m_widgets.size ()) { + if (mp_current_widget == m_widgets [index]) { + mp_current_widget = 0; + } + m_widgets.erase (m_widgets.begin () + index); + } + if (m_widgets.size () == 0) { + mp_bglabel->show (); + } +} + +void ControlWidgetStack::raise_widget(size_t index) +{ + mp_current_widget = 0; + bool any_visible = false; + for (size_t i = 0; i < m_widgets.size (); ++i) { + if (m_widgets [i]) { + if (i == index) { + m_widgets [i]->show (); + mp_current_widget = m_widgets [i]; + any_visible = true; + } else { + m_widgets [i]->hide (); + } + } + } + + if (! any_visible) { + mp_bglabel->show (); + } else { + mp_bglabel->hide (); + } +} + +QWidget *ControlWidgetStack::widget(size_t index) +{ + if (index < m_widgets.size ()) { + return m_widgets [index]; + } else { + return 0; + } +} + +QWidget *ControlWidgetStack::background_widget() +{ + return mp_bglabel; +} + +void ControlWidgetStack::resize_children() +{ + // set the geometry of all children + for (std::vector ::iterator child = m_widgets.begin (); child != m_widgets.end (); ++child) { + if (*child) { + (*child)->setGeometry (0, 0, width (), height ()); + } + } + mp_bglabel->setGeometry (0, 0, width (), height ()); +} + +} diff --git a/src/lay/lay/layControlWidgetStack.h b/src/lay/lay/layControlWidgetStack.h new file mode 100644 index 000000000..29b436a68 --- /dev/null +++ b/src/lay/lay/layControlWidgetStack.h @@ -0,0 +1,77 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_layControlWidgetStack +#define HDR_layControlWidgetStack + +#include "layCommon.h" + +#include + +class QLabel; + +namespace lay +{ + +class ControlWidgetStack + : public QFrame +{ +public: + ControlWidgetStack (QWidget *parent = 0, const char *name = 0); + + void focusInEvent (QFocusEvent *); + + QSize sizeHint () const; + + void add_widget (QWidget *w); + void remove_widget (size_t index); + void raise_widget (size_t index); + QWidget *widget (size_t index); + QWidget *background_widget (); + + QWidget *currentWidget () const + { + return mp_current_widget; + } + + size_t count () const + { + return m_widgets.size (); + } + +protected: + virtual void resizeEvent (QResizeEvent *) + { + resize_children (); + } + + void resize_children (); + + std::vector m_widgets; + QWidget *mp_current_widget; + QLabel *mp_bglabel; +}; + +} + +#endif diff --git a/src/lay/lay/layHelpAboutDialog.cc b/src/lay/lay/layHelpAboutDialog.cc new file mode 100644 index 000000000..6430d43a1 --- /dev/null +++ b/src/lay/lay/layHelpAboutDialog.cc @@ -0,0 +1,140 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "layHelpAboutDialog.h" +#include "layApplication.h" +#include "layVersion.h" +#include "layHelpSource.h" // because of escape_xml +#include "layInit.h" +#include "dbInit.h" +#include "gsiInterpreter.h" + +#include "ui_HelpAboutDialog.h" + +#include + +namespace lay +{ + +// ------------------------------------------------------------ +// Implementation of the "help about" dialog + +HelpAboutDialog::HelpAboutDialog (QWidget *parent) + : QDialog (parent) +{ + mp_ui = new Ui::HelpAboutDialog (); + mp_ui->setupUi (this); + + std::vector build_options; + if (lay::ApplicationBase::instance ()->ruby_interpreter ().available ()) { + build_options.push_back (tl::to_string (tr ("Ruby interpreter ")) + lay::ApplicationBase::instance ()->ruby_interpreter ().version ()); + } + if (lay::ApplicationBase::instance ()->python_interpreter ().available ()) { + build_options.push_back (tl::to_string (tr ("Python interpreter ")) + lay::ApplicationBase::instance ()->python_interpreter ().version ()); + } +#if defined(HAVE_QTBINDINGS) + build_options.push_back (tl::to_string (tr ("Qt bindings for scripts"))); +#endif +#if defined(HAVE_64BIT_COORD) + build_options.push_back (tl::to_string (tr ("Wide coordinates (64 bit)"))); +#endif + + std::string s; + + s = ""; + + s += "

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

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

") + escape_xml (*p) + "

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

"; + s += "

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

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

"; + s += "

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

    "; + + for (std::list::const_iterator pd = lay::plugins ().begin (); pd != lay::plugins ().end (); ++pd) { + s += "
  • "; + if (! pd->description.empty ()) { + s += escape_xml (pd->description); + } else { + s += escape_xml (pd->path); + } + if (! pd->version.empty ()) { + s += " (" + escape_xml (pd->version) + ")"; + } + s += "
  • "; + } + + for (std::list::const_iterator pd = db::plugins ().begin (); pd != db::plugins ().end (); ++pd) { + s += "
  • "; + if (! pd->description.empty ()) { + s += escape_xml (pd->description); + } else { + s += escape_xml (pd->path); + } + if (! pd->version.empty ()) { + s += " (" + escape_xml (pd->version) + ")"; + } + s += "
  • "; + } + + s += "
"; + + } + + s += ""; + + std::string t = tl::to_string (QObject::tr ("About ")) + lay::Version::name (); + + setWindowTitle (tl::to_qstring (t)); + + mp_ui->main->setWordWrap (true); + mp_ui->main->setText (tl::to_qstring (s)); +} + +HelpAboutDialog::~HelpAboutDialog () +{ + delete mp_ui; + mp_ui = 0; +} + +} diff --git a/src/lay/lay/layHelpAboutDialog.h b/src/lay/lay/layHelpAboutDialog.h new file mode 100644 index 000000000..fc98fe98e --- /dev/null +++ b/src/lay/lay/layHelpAboutDialog.h @@ -0,0 +1,55 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_layHelpAboutDialog +#define HDR_layHelpAboutDialog + +#include "layCommon.h" + +#include + +namespace Ui { + class HelpAboutDialog; +} + +namespace lay +{ + +/** + * @brief A dialog for showing the "help about" dialog + */ +class LAY_PUBLIC HelpAboutDialog + : public QDialog +{ +public: + HelpAboutDialog (QWidget *parent); + ~HelpAboutDialog (); + +private: + Ui::HelpAboutDialog *mp_ui; +}; + +} + +#endif + diff --git a/src/lay/lay/layHelpDialog.cc b/src/lay/lay/layHelpDialog.cc index 36fd04864..47f603666 100644 --- a/src/lay/lay/layHelpDialog.cc +++ b/src/lay/lay/layHelpDialog.cc @@ -24,7 +24,10 @@ #include "layHelpDialog.h" #include "layHelpSource.h" #include "layBrowserPanel.h" +#include "layDispatcher.h" +#include "layConfig.h" #include "tlStaticObjects.h" +#include "ui_HelpDialog.h" #include "tlString.h" @@ -42,35 +45,17 @@ HelpDialog::HelpDialog (QWidget *parent, bool modal) : QDialog (modal ? parent : 0 /*show as separate window*/, modal ? Qt::WindowFlags (0) : Qt::Window /*enabled minimize button*/), m_initialized (false) { + mp_ui = new Ui::HelpDialog (); + mp_ui->setupUi (this); + setModal (modal); - QVBoxLayout *help_layout = new QVBoxLayout (this); - setLayout (help_layout); - setWindowTitle (QObject::tr ("Assistant")); - mp_browser_panel = new lay::BrowserPanel (this); - help_layout->addWidget (mp_browser_panel); - - QSizePolicy sp = mp_browser_panel->sizePolicy (); - sp.setVerticalStretch (1); - mp_browser_panel->setSizePolicy (sp); - - if (modal) { - QFrame *button_frame = new QFrame (this); - help_layout->addWidget (button_frame); - QHBoxLayout *button_layout = new QHBoxLayout (button_frame); - button_layout->setMargin (0); - QPushButton *close_button = new QPushButton (button_frame); - button_layout->addStretch (1); - button_layout->addWidget (close_button); - close_button->setText (QObject::tr ("Close")); - close_button->setDefault (false); - close_button->setAutoDefault (false); - connect (close_button, SIGNAL (clicked ()), this, SLOT (accept ())); - } + mp_ui->button_frame->setVisible (modal); + mp_ui->browser_panel->set_dispatcher (lay::Dispatcher::instance (), cfg_assistant_bookmarks); m_def_title = windowTitle (); - connect (mp_browser_panel, SIGNAL (title_changed (const QString &)), this, SLOT (title_changed (const QString &))); - connect (mp_browser_panel, SIGNAL (url_changed (const QString &)), this, SLOT (title_changed (const QString &))); + connect (mp_ui->browser_panel, SIGNAL (title_changed (const QString &)), this, SLOT (title_changed (const QString &))); + connect (mp_ui->browser_panel, SIGNAL (url_changed (const QString &)), this, SLOT (title_changed (const QString &))); } HelpDialog::~HelpDialog () @@ -82,14 +67,14 @@ void HelpDialog::title_changed (const QString &) { QString wt; - QString title = tl::to_qstring (mp_browser_panel->title ()); + QString title = tl::to_qstring (mp_ui->browser_panel->title ()); if (title.isNull () || title.size () == 0) { wt = m_def_title; } else { wt = m_def_title + QString::fromUtf8 (" - ") + title; } - QString url = tl::to_qstring (mp_browser_panel->url ()); + QString url = tl::to_qstring (mp_ui->browser_panel->url ()); if (! url.isNull () && url.size () > 0) { wt += QString::fromUtf8 (" [") + url + QString::fromUtf8 ("]"); } @@ -100,13 +85,13 @@ void HelpDialog::title_changed (const QString &) void HelpDialog::load (const std::string &url) { initialize (); - mp_browser_panel->load (url); + mp_ui->browser_panel->load (url); } void HelpDialog::search (const std::string &topic) { initialize (); - mp_browser_panel->search (topic); + mp_ui->browser_panel->search (topic); } void HelpDialog::showEvent (QShowEvent *) @@ -121,13 +106,13 @@ void HelpDialog::initialize () { if (! m_initialized) { m_initialized = true; - mp_browser_panel->set_search_url ("int:/search.xml", "string"); + mp_ui->browser_panel->set_search_url ("int:/search.xml", "string"); if (! mp_help_source) { mp_help_source = new lay::HelpSource (); tl::StaticObjects::reg (&mp_help_source); } - mp_browser_panel->set_source (mp_help_source); - mp_browser_panel->set_home ("int:/index.xml"); + mp_ui->browser_panel->set_source (mp_help_source); + mp_ui->browser_panel->set_home ("int:/index.xml"); } } diff --git a/src/lay/lay/layHelpDialog.h b/src/lay/lay/layHelpDialog.h index e24c09d2e..361aed565 100644 --- a/src/lay/lay/layHelpDialog.h +++ b/src/lay/lay/layHelpDialog.h @@ -31,6 +31,11 @@ #include #include +namespace Ui +{ + class HelpDialog; +} + namespace lay { @@ -58,7 +63,7 @@ protected slots: void title_changed (const QString &t); private: - lay::BrowserPanel *mp_browser_panel; + Ui::HelpDialog *mp_ui; QRect m_geometry; static lay::HelpSource *mp_help_source; QString m_def_title; diff --git a/src/lay/lay/layHelpSource.cc b/src/lay/lay/layHelpSource.cc index e33569d8b..0b5edd062 100644 --- a/src/lay/lay/layHelpSource.cc +++ b/src/lay/lay/layHelpSource.cc @@ -554,6 +554,21 @@ HelpSource::get_outline (const std::string &u) return ol; } +void +HelpSource::search_completers (const std::string &string, std::list &completers) +{ + size_t n = 0; + const size_t max_completers = 100; + + // first produce all hits with match + for (std::vector ::const_iterator i = m_index.begin (); i < m_index.end () && n < max_completers; ++i) { + if (i->normalized_key.find (string) != std::string::npos) { + completers.push_back (i->key); + ++n; + } + } +} + std::string HelpSource::next_topic (const std::string &url) { diff --git a/src/lay/lay/layHelpSource.h b/src/lay/lay/layHelpSource.h index dc3af75c2..d355cd2b5 100644 --- a/src/lay/lay/layHelpSource.h +++ b/src/lay/lay/layHelpSource.h @@ -82,8 +82,9 @@ public: virtual QImage get_image (const std::string &url); virtual std::string get_css (const std::string &url); - virtual std::string next_topic (const std::string &url); + virtual void search_completers(const std::string &search_string, std::list &completers); + virtual std::string next_topic (const std::string &url); virtual std::string prev_topic (const std::string &url); QDomDocument get_dom (const std::string &u); diff --git a/src/lay/lay/layMainConfigPages.cc b/src/lay/lay/layMainConfigPages.cc index d906a89be..7b982788b 100644 --- a/src/lay/lay/layMainConfigPages.cc +++ b/src/lay/lay/layMainConfigPages.cc @@ -61,6 +61,9 @@ public: options.push_back (std::pair (cfg_synchronized_views, "false")); options.push_back (std::pair (cfg_default_grids, "0.01,0.005,0.001")); options.push_back (std::pair (cfg_mru, "")); + options.push_back (std::pair (cfg_mru_sessions, "")); + options.push_back (std::pair (cfg_mru_layer_properties, "")); + options.push_back (std::pair (cfg_mru_bookmarks, "")); options.push_back (std::pair (cfg_technologies, "")); options.push_back (std::pair (cfg_show_navigator, "false")); options.push_back (std::pair (cfg_navigator_all_hier_levels, "false")); @@ -80,6 +83,7 @@ public: options.push_back (std::pair (cfg_micron_digits, "5")); options.push_back (std::pair (cfg_dbu_digits, "2")); options.push_back (std::pair (cfg_reader_options_show_always, "false")); + options.push_back (std::pair (cfg_assistant_bookmarks, "")); } virtual std::vector > config_pages (QWidget *parent) const diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 2e8f23f11..425f8485f 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -69,9 +69,9 @@ #include "layConfig.h" #include "layMainWindow.h" #include "layHelpDialog.h" -#include "layHelpSource.h" // because of escape_xml #include "layNavigator.h" #include "layProgress.h" +#include "layProgressDialog.h" #include "layProgressWidget.h" #include "layStream.h" #include "layLayerControlPanel.h" // because of LabelWithBackground @@ -89,10 +89,13 @@ #include "laySaltController.h" #include "layTipDialog.h" #include "layMacroController.h" +#include "layHelpAboutDialog.h" +#include "layControlWidgetStack.h" +#include "layViewWidgetStack.h" +#include "layEditorOptionsPages.h" #include "layInit.h" #include "antObject.h" #include "antService.h" -#include "ui_HelpAboutDialog.h" #include "gsi.h" #include "gsiInterpreter.h" #include "gtf.h" @@ -104,308 +107,6 @@ const int max_dirty_files = 15; // ------------------------------------------------------------- -class ProgressDialog - : public QDialog, - public tl::Object -{ -public: - ProgressDialog (QWidget *parent, lay::ProgressReporter *pr) - : QDialog (parent), mp_pr (pr) - { - QVBoxLayout *vbl = new QVBoxLayout (this); - vbl->setMargin (0); - vbl->setSpacing (0); - - mp_progress_widget = new ProgressWidget (pr, this, true); - mp_progress_widget->setObjectName (QString::fromUtf8 ("progress")); - vbl->addWidget (mp_progress_widget); - - setWindowTitle (QObject::tr ("Progress")); - setWindowModality (Qt::WindowModal); - } - - void closeEvent (QCloseEvent * /*event*/) - { - if (mp_pr) { - // NOTE: We don't kill on close for now. This creates a too easy way to scrap results. - // mp_pr->signal_break (); - // TODO: there should be a warning saying some jobs are pending. - } - } - - void set_progress (tl::Progress *progress) - { - mp_progress_widget->set_progress (progress); - } - - void add_widget (QWidget *widget) - { - mp_progress_widget->add_widget (widget); - } - - void remove_widget () - { - mp_progress_widget->remove_widget (); - } - - QWidget *get_widget () const - { - return mp_progress_widget->get_widget (); - } - -private: - lay::ProgressWidget *mp_progress_widget; - lay::ProgressReporter *mp_pr; -}; - -// ------------------------------------------------------------- - -class ControlWidgetStack - : public QFrame -{ -public: - ControlWidgetStack (QWidget *parent = 0, const char *name = 0) - : QFrame (parent) - { - setObjectName (QString::fromUtf8 (name)); - - // Background ist a simple label without a text currently - mp_bglabel = new QLabel (this); - mp_bglabel->setAutoFillBackground (true); - mp_bglabel->setAlignment (Qt::AlignVCenter | Qt::AlignHCenter); - mp_bglabel->show (); - } - - void addWidget (QWidget *w) - { - m_widgets.push_back (w); - w->setParent (this); - resize_children (); - raiseWidget (m_widgets.size () - 1); - - int mw = 0; - for (size_t i = 0; i < m_widgets.size (); ++i) { - mw = std::max (m_widgets [i]->sizeHint ().width (), mw); - mw = std::max (m_widgets [i]->minimumWidth (), mw); - } - - if (mw > minimumWidth ()) { - setMinimumWidth (mw); - resize (minimumWidth (), height ()); - } - } - - QSize sizeHint () const - { - int w = 0; - for (size_t i = 0; i < m_widgets.size (); ++i) { - w = std::max (m_widgets [i]->sizeHint ().width (), w); - } - return QSize (w, 0); - } - - void removeWidget (size_t index) - { - if (index < m_widgets.size ()) { - m_widgets.erase (m_widgets.begin () + index); - } - if (m_widgets.size () == 0) { - mp_bglabel->show (); - } - } - - void raiseWidget (size_t index) - { - bool any_visible = false; - for (size_t i = 0; i < m_widgets.size (); ++i) { - if (m_widgets [i]) { - if (i == index) { - m_widgets [i]->show (); - any_visible = true; - } else { - m_widgets [i]->hide (); - } - } - } - - if (! any_visible) { - mp_bglabel->show (); - } else { - mp_bglabel->hide (); - } - } - - QWidget *widget (size_t index) - { - if (index < m_widgets.size ()) { - return m_widgets [index]; - } else { - return 0; - } - } - - QWidget *background_widget () - { - return mp_bglabel; - } - -protected: - virtual void resizeEvent (QResizeEvent *) - { - resize_children (); - } - - void resize_children () - { - // set the geometry of all children - for (std::vector ::iterator child = m_widgets.begin (); child != m_widgets.end (); ++child) { - if (*child) { - (*child)->setGeometry (0, 0, width (), height ()); - } - } - mp_bglabel->setGeometry (0, 0, width (), height ()); - } - - std::vector m_widgets; - QLabel *mp_bglabel; -}; - -// ------------------------------------------------------------- - -class ViewWidgetStack - : public QWidget -{ -public: - ViewWidgetStack (QWidget *parent = 0, const char *name = 0) - : QWidget (parent) - { - setObjectName (QString::fromUtf8 (name)); - - mp_bglabel = new QLabel (this); - mp_bglabel->setAutoFillBackground (true); - mp_bglabel->setText (QObject::tr ("

Use File/Open to open a layout

")); - mp_bglabel->setAlignment (Qt::AlignVCenter | Qt::AlignHCenter); - mp_bglabel->show (); - } - - void addWidget (LayoutView *w) - { - m_widgets.push_back (w); - w->setParent (this); - resize_children (); - raiseWidget (m_widgets.size () - 1); - - updateGeometry (); - } - - void removeWidget (size_t index) - { - if (index < m_widgets.size ()) { - m_widgets.erase (m_widgets.begin () + index); - } - if (m_widgets.size () == 0) { - mp_bglabel->show (); - } - } - - void raiseWidget (size_t index) - { - if (index < m_widgets.size ()) { - mp_bglabel->hide (); - m_widgets [index]->show (); - } else { - mp_bglabel->show (); - } - - size_t i = 0; - for (std::vector ::iterator child = m_widgets.begin (); child != m_widgets.end (); ++child, ++i) { - if (i != index) { - (*child)->hide (); - } - } - } - - LayoutView *widget (size_t index) - { - if (index < m_widgets.size ()) { - return m_widgets [index]; - } else { - return 0; - } - } - - QWidget *background_widget () - { - return mp_bglabel; - } - -protected: - virtual void resizeEvent (QResizeEvent *) - { - resize_children (); - } - - void resize_children () - { - // set the geometry of all children - for (std::vector ::iterator child = m_widgets.begin (); child != m_widgets.end (); ++child) { - (*child)->setGeometry (0, 0, width (), height ()); - } - mp_bglabel->setGeometry (0, 0, width (), height ()); - } - - std::vector m_widgets; - QLabel *mp_bglabel; -}; - -// ------------------------------------------------------------- - -TextProgressDelegate::TextProgressDelegate (MainWindow *mw, int verbosity) - : lay::TextProgress (verbosity), mp_mw (mw) -{ - // .. nothing yet .. -} - -void TextProgressDelegate::update_progress (tl::Progress *progress) -{ - if (!mp_mw->update_progress (progress)) { - lay::TextProgress::update_progress (progress); - } -} - -void TextProgressDelegate::show_progress_bar (bool show) -{ - if (!mp_mw->show_progress_bar (show)) { - lay::TextProgress::show_progress_bar (show); - } -} - -bool TextProgressDelegate::progress_wants_widget () const -{ - return mp_mw != 0 && mp_mw->progress_wants_widget (); -} - -void TextProgressDelegate::progress_add_widget (QWidget *widget) -{ - if (mp_mw) { - mp_mw->progress_add_widget (widget); - } -} - -QWidget *TextProgressDelegate::progress_get_widget () const -{ - return mp_mw ? mp_mw->progress_get_widget () : 0; -} - -void TextProgressDelegate::progress_remove_widget () -{ - if (mp_mw) { - mp_mw->progress_remove_widget (); - } -} - -// ------------------------------------------------------------- - static MainWindow *mw_instance = 0; MainWindow * @@ -414,7 +115,44 @@ MainWindow::instance () return mw_instance; } -// ----------------------------------- +// ------------------------------------------------------------- + +static void +show_dock_widget (QDockWidget *dock_widget, bool visible) +{ + if (visible) { + + dock_widget->show (); + dock_widget->setFocus (); + + // NOTE: this is a clumsy way to make sure the dock widget is made the current tab if it's in a tabbed dock + // TODO: is there a better way to do this? + QMainWindow *main_window = dynamic_cast (dock_widget->parent ()); + if (! main_window) { + return; + } + + // Look up all children of the main window and find the QTabBars. These are the dock tabs (we don't create others). + // Inside these, look up the right tab by checking the titles. + QList mw_children = main_window->children (); + for (QList::const_iterator i = mw_children.begin (); i != mw_children.end (); ++i) { + QTabBar *tab_bar = dynamic_cast (*i); + if (tab_bar) { + for (int j = 0; j < tab_bar->count (); ++j) { + if (tab_bar->tabText (j) == dock_widget->windowTitle ()) { + tab_bar->setCurrentIndex (j); + return; + } + } + } + } + + } else { + dock_widget->hide (); + } +} + +// ------------------------------------------------------------- MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const char *name, bool undo_enabled) : QMainWindow (0), @@ -426,7 +164,7 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha m_disable_tab_selected (false), m_exited (false), dm_do_update_menu (this, &MainWindow::do_update_menu), - dm_do_update_file_menu (this, &MainWindow::do_update_file_menu), + dm_do_update_mru_menus (this, &MainWindow::do_update_mru_menus), dm_exit (this, &MainWindow::exit), m_grid_micron (0.001), m_default_grids_updated (true), @@ -455,7 +193,7 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha init_menu (); - mp_assistant = new lay::HelpDialog (this); + mp_assistant = 0; mp_pr = new lay::ProgressReporter (); mp_pr->set_progress_bar (&m_text_progress); @@ -490,6 +228,7 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha mp_hp_dock_widget->setObjectName (QString::fromUtf8 ("hp_dock_widget")); mp_hp_stack = new ControlWidgetStack (mp_hp_dock_widget, "hp_stack"); mp_hp_dock_widget->setWidget (mp_hp_stack); + mp_hp_dock_widget->setFocusProxy (mp_hp_stack); connect (mp_hp_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); m_hp_visible = true; @@ -497,13 +236,24 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha mp_libs_dock_widget->setObjectName (QString::fromUtf8 ("libs_dock_widget")); mp_libs_stack = new ControlWidgetStack (mp_libs_dock_widget, "libs_stack"); mp_libs_dock_widget->setWidget (mp_libs_stack); + mp_libs_dock_widget->setFocusProxy (mp_libs_stack); connect (mp_libs_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); m_libs_visible = true; + mp_eo_dock_widget = new QDockWidget (QObject::tr ("Editor Options"), this); + mp_eo_dock_widget->setObjectName (QString::fromUtf8 ("eo_dock_widget")); + mp_eo_dock_widget->setMinimumHeight (150); + mp_eo_stack = new ControlWidgetStack (mp_eo_dock_widget, "eo_stack"); + mp_eo_dock_widget->setWidget (mp_eo_stack); + mp_eo_dock_widget->setFocusProxy (mp_eo_stack); + connect (mp_eo_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); + m_eo_visible = true; + mp_bm_dock_widget = new QDockWidget (QObject::tr ("Bookmarks"), this); mp_bm_dock_widget->setObjectName (QString::fromUtf8 ("bookmarks_dock_widget")); mp_bm_stack = new ControlWidgetStack (mp_bm_dock_widget, "bookmarks_stack"); mp_bm_dock_widget->setWidget (mp_bm_stack); + mp_bm_dock_widget->setFocusProxy (mp_bm_stack); connect (mp_bm_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); m_bm_visible = true; @@ -515,6 +265,7 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha mp_layer_toolbox_dock_widget->setObjectName (QString::fromUtf8 ("lt_dock_widget")); mp_layer_toolbox = new LayerToolbox (mp_layer_toolbox_dock_widget, "layer_toolbox"); mp_layer_toolbox_dock_widget->setWidget (mp_layer_toolbox); + mp_layer_toolbox_dock_widget->setFocusProxy (mp_layer_toolbox); connect (mp_layer_toolbox_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); m_layer_toolbox_visible = true; @@ -522,6 +273,7 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha mp_lp_dock_widget->setObjectName (QString::fromUtf8 ("lp_dock_widget")); mp_lp_stack = new ControlWidgetStack (mp_lp_dock_widget, "lp_stack"); mp_lp_dock_widget->setWidget (mp_lp_stack); + mp_lp_dock_widget->setFocusProxy (mp_lp_stack); connect (mp_lp_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); m_lp_visible = true; @@ -529,6 +281,7 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha mp_navigator_dock_widget->setObjectName (QString::fromUtf8 ("navigator_dock_widget")); mp_navigator = new Navigator (this); mp_navigator_dock_widget->setWidget (mp_navigator); + mp_navigator_dock_widget->setFocusProxy (mp_navigator); connect (mp_navigator_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); m_navigator_visible = true; @@ -539,6 +292,7 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha addDockWidget(Qt::LeftDockWidgetArea, mp_navigator_dock_widget); addDockWidget(Qt::LeftDockWidgetArea, mp_hp_dock_widget); addDockWidget(Qt::LeftDockWidgetArea, mp_libs_dock_widget); + addDockWidget(Qt::LeftDockWidgetArea, mp_eo_dock_widget); addDockWidget(Qt::RightDockWidgetArea, mp_bm_dock_widget); addDockWidget(Qt::RightDockWidgetArea, mp_lp_dock_widget); addDockWidget(Qt::RightDockWidgetArea, mp_layer_toolbox_dock_widget); @@ -750,7 +504,7 @@ MainWindow::init_menu () } void -MainWindow::dock_widget_visibility_changed (bool /*visible*/) +MainWindow::dock_widget_visibility_changed (bool visible) { if (sender () == mp_lp_dock_widget) { dispatcher ()->config_set (cfg_show_layer_panel, tl::to_string (!mp_lp_dock_widget->isHidden ())); @@ -764,6 +518,8 @@ MainWindow::dock_widget_visibility_changed (bool /*visible*/) dispatcher ()->config_set (cfg_show_navigator, tl::to_string (!mp_navigator_dock_widget->isHidden ())); } else if (sender () == mp_layer_toolbox_dock_widget) { dispatcher ()->config_set (cfg_show_layer_toolbox, tl::to_string (!mp_layer_toolbox_dock_widget->isHidden ())); + } else if (sender () == mp_eo_dock_widget) { + m_eo_visible = visible; } } @@ -892,6 +648,7 @@ MainWindow::show () void MainWindow::close_all () { + cancel (); mp_layer_toolbox->set_view (0); // try a smooth shutdown of the current view @@ -922,11 +679,12 @@ MainWindow::close_all () lay::LayoutView *view = mp_views.back (); mp_views.pop_back (); - mp_lp_stack->removeWidget (mp_views.size ()); - mp_hp_stack->removeWidget (mp_views.size ()); - mp_libs_stack->removeWidget (mp_views.size ()); - mp_bm_stack->removeWidget (mp_views.size ()); - mp_view_stack->removeWidget (mp_views.size ()); + mp_lp_stack->remove_widget (mp_views.size ()); + mp_hp_stack->remove_widget (mp_views.size ()); + mp_libs_stack->remove_widget (mp_views.size ()); + mp_eo_stack->remove_widget (mp_views.size ()); + mp_bm_stack->remove_widget (mp_views.size ()); + mp_view_stack->remove_widget (mp_views.size ()); delete view; @@ -1103,9 +861,18 @@ MainWindow::config_finalize () // Not set the window state: this ensures we have handled cfg_window_geometry // before we restore the state if (! m_config_window_state.empty ()) { + QByteArray state = QByteArray::fromBase64 (m_config_window_state.c_str ()); m_config_window_state.clear (); + + bool eo_visible = m_eo_visible; + restoreState (state); + + // Keep the editor options visibility state + m_eo_visible = eo_visible; + show_dock_widget (mp_eo_dock_widget, m_eo_visible); + } // Update the default grids menu if necessary @@ -1258,7 +1025,49 @@ MainWindow::configure (const std::string &name, const std::string &value) } } - dm_do_update_file_menu (); + dm_do_update_mru_menus (); + + return true; + + } else if (name == cfg_mru_sessions) { + + tl::Extractor ex (value.c_str ()); + + m_mru_sessions.clear (); + while (! ex.at_end ()) { + m_mru_sessions.push_back (value); + ex.read_quoted (m_mru_sessions.back ()); + } + + dm_do_update_mru_menus (); + + return true; + + } else if (name == cfg_mru_layer_properties) { + + tl::Extractor ex (value.c_str ()); + + m_mru_layer_properties.clear (); + while (! ex.at_end ()) { + m_mru_layer_properties.push_back (value); + ex.read_quoted (m_mru_layer_properties.back ()); + } + + dm_do_update_mru_menus (); + + return true; + + } else if (name == cfg_mru_bookmarks) { + + tl::Extractor ex (value.c_str ()); + + m_mru_bookmarks.clear (); + while (! ex.at_end ()) { + m_mru_bookmarks.push_back (value); + ex.read_quoted (m_mru_bookmarks.back ()); + } + + dm_do_update_mru_menus (); return true; @@ -1518,6 +1327,12 @@ MainWindow::update_dock_widget_state () mp_libs_dock_widget->hide (); } + if (m_eo_visible) { + mp_eo_dock_widget->show (); + } else { + mp_eo_dock_widget->hide (); + } + if (m_bm_visible) { mp_bm_dock_widget->show (); } else { @@ -1856,6 +1671,32 @@ MainWindow::select_mode (int m) } } + // if the current mode supports editing, show the editor options panel + + const lay::PluginDeclaration *pd_sel = 0; + for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { + const lay::PluginDeclaration *pd = cls.operator-> (); + if (pd->id () == m_mode) { + pd_sel = pd; + } + } + + bool eo_visible = false; + if (mp_eo_stack && pd_sel) { + eo_visible = pd_sel->editable_enabled (); + } + if (current_view () && eo_visible) { + lay::EditorOptionsPages *eo_pages = current_view ()->editor_options_pages (); + if (! eo_pages || ! eo_pages->has_content ()) { + eo_visible = false; + } + } + + if (eo_visible != m_eo_visible) { + m_eo_visible = eo_visible; + show_dock_widget (mp_eo_dock_widget, m_eo_visible); + } + } } @@ -2147,6 +1988,7 @@ MainWindow::cm_save_layer_props () std::string fn; if (mp_lprops_fdia->get_save (fn, tl::to_string (QObject::tr ("Save Layer Properties File")))) { current_view ()->save_layer_props (fn); + add_to_other_mru (fn, cfg_mru_layer_properties); } } else { throw tl::Exception (tl::to_string (QObject::tr ("No view open to save the layer properties from"))); @@ -2189,6 +2031,8 @@ MainWindow::cm_load_layer_props () load_layer_properties (fn, false /*current view only*/, false /*don't add default*/); } + add_to_other_mru (fn, cfg_mru_layer_properties); + } } else { throw tl::Exception (tl::to_string (QObject::tr ("No view open to load the layer properties for"))); @@ -2230,6 +2074,7 @@ MainWindow::cm_save_session () std::string fn = m_current_session; if (mp_session_fdia->get_save (fn, tl::to_string (QObject::tr ("Save Session File")))) { save_session (fn); + add_to_other_mru (fn, cfg_mru_sessions); } } @@ -2245,7 +2090,10 @@ MainWindow::cm_restore_session () int dirty_layouts = dirty_files (df_list); if (dirty_layouts == 0) { + restore_session (fn); + add_to_other_mru (fn, cfg_mru_sessions); + } else { QMessageBox mbox (this); @@ -2259,6 +2107,7 @@ MainWindow::cm_restore_session () if (mbox.clickedButton() == discard_button) { restore_session (fn); + add_to_other_mru (fn, cfg_mru_sessions); } } @@ -2273,6 +2122,7 @@ MainWindow::cm_save_bookmarks () std::string fn; if (mp_bookmarks_fdia->get_save (fn, tl::to_string (QObject::tr ("Save Bookmarks File")))) { current_view ()->bookmarks ().save (fn); + add_to_other_mru (fn, cfg_mru_bookmarks); } } else { throw tl::Exception (tl::to_string (QObject::tr ("No view open to save the bookmarks from"))); @@ -2288,6 +2138,7 @@ MainWindow::cm_load_bookmarks () BookmarkList bookmarks; bookmarks.load (fn); current_view ()->bookmarks (bookmarks); + add_to_other_mru (fn, cfg_mru_bookmarks); } } else { throw tl::Exception (tl::to_string (QObject::tr ("No view open to load the bookmarks for"))); @@ -2502,6 +2353,8 @@ MainWindow::select_view (int index) try { + cancel (); + tl_assert (index >= 0 && index < int (views ())); mp_tab_bar->setCurrentIndex (index); @@ -2522,11 +2375,12 @@ MainWindow::select_view (int index) current_view ()->zoom_box (box); } - mp_view_stack->raiseWidget (index); - mp_hp_stack->raiseWidget (index); - mp_lp_stack->raiseWidget (index); - mp_libs_stack->raiseWidget (index); - mp_bm_stack->raiseWidget (index); + mp_view_stack->raise_widget (index); + mp_hp_stack->raise_widget (index); + mp_lp_stack->raise_widget (index); + mp_libs_stack->raise_widget (index); + mp_eo_stack->raise_widget (index); + mp_bm_stack->raise_widget (index); mp_setup_form->setup (); } @@ -2713,11 +2567,12 @@ MainWindow::clone_current_view () mp_layer_toolbox->set_view (current_view ()); - mp_view_stack->addWidget (view); - mp_lp_stack->addWidget (view->layer_control_frame ()); - mp_hp_stack->addWidget (view->hierarchy_control_frame ()); - mp_libs_stack->addWidget (view->libraries_frame ()); - mp_bm_stack->addWidget (view->bookmarks_frame ()); + mp_view_stack->add_widget (view); + mp_lp_stack->add_widget (view->layer_control_frame ()); + mp_hp_stack->add_widget (view->hierarchy_control_frame ()); + mp_libs_stack->add_widget (view->libraries_frame ()); + mp_eo_stack->add_widget (view->editor_options_frame ()); + mp_bm_stack->add_widget (view->bookmarks_frame ()); bool f = m_disable_tab_selected; m_disable_tab_selected = true; @@ -2949,6 +2804,8 @@ MainWindow::close_view (int index) { if (view (index)) { + cancel (); + // this suppresses view_selected events that would otherwise be created bool f = m_disable_tab_selected; m_disable_tab_selected = true; @@ -2961,11 +2818,12 @@ MainWindow::close_view (int index) } mp_tab_bar->removeTab (index); - mp_view_stack->removeWidget (index); - mp_lp_stack->removeWidget (index); - mp_hp_stack->removeWidget (index); - mp_libs_stack->removeWidget (index); - mp_bm_stack->removeWidget (index); + mp_view_stack->remove_widget (index); + mp_lp_stack->remove_widget (index); + mp_hp_stack->remove_widget (index); + mp_libs_stack->remove_widget (index); + mp_eo_stack->remove_widget (index); + mp_bm_stack->remove_widget (index); view_closed_event (int (index)); @@ -3009,6 +2867,8 @@ MainWindow::add_mru (const std::string &fn_rel) add_mru (fn_rel, m_initial_technology); } +const size_t max_mru = 16; + void MainWindow::add_mru (const std::string &fn_rel, const std::string &tech) { @@ -3024,7 +2884,7 @@ MainWindow::add_mru (const std::string &fn_rel, const std::string &tech) new_mru.push_back (std::make_pair (fn, tech)); - if (new_mru.size () > 10) { + if (new_mru.size () > max_mru) { new_mru.erase (new_mru.begin ()); } @@ -3043,6 +2903,47 @@ MainWindow::add_mru (const std::string &fn_rel, const std::string &tech) dispatcher ()->config_set (cfg_mru, config_str); } +void +MainWindow::add_to_other_mru (const std::string &fn_rel, const std::string &cfg) +{ + std::vector *mru_ptr; + if (cfg == cfg_mru_sessions) { + mru_ptr = &m_mru_sessions; + } else if (cfg == cfg_mru_layer_properties) { + mru_ptr = &m_mru_layer_properties; + } else if (cfg == cfg_mru_bookmarks) { + mru_ptr = &m_mru_bookmarks; + } else { + tl_assert (false); + } + + std::vector new_mru = *mru_ptr; + std::string fn (tl::InputStream::absolute_path (fn_rel)); + + for (std::vector::iterator mru = new_mru.begin (); mru != new_mru.end (); ++mru) { + if (*mru == fn) { + new_mru.erase (mru); + break; + } + } + + new_mru.push_back (fn); + + if (new_mru.size () > max_mru) { + new_mru.erase (new_mru.begin ()); + } + + std::string config_str; + for (std::vector::const_iterator mru = new_mru.begin (); mru != new_mru.end (); ++mru) { + if (! config_str.empty ()) { + config_str += " "; + } + config_str += tl::to_quoted_string (*mru); + } + + dispatcher ()->config_set (cfg, config_str); +} + namespace { @@ -3064,10 +2965,64 @@ private: size_t m_n; }; +class OpenRecentSessionAction + : public lay::Action +{ +public: + OpenRecentSessionAction (lay::MainWindow *mw, size_t n) + : lay::Action (), mp_mw (mw), m_n (n) + { } + + void triggered () + { + mp_mw->open_recent_session (m_n); + } + +private: + lay::MainWindow *mp_mw; + size_t m_n; +}; + +class OpenRecentLayerPropertiesAction + : public lay::Action +{ +public: + OpenRecentLayerPropertiesAction (lay::MainWindow *mw, size_t n) + : lay::Action (), mp_mw (mw), m_n (n) + { } + + void triggered () + { + mp_mw->open_recent_layer_properties (m_n); + } + +private: + lay::MainWindow *mp_mw; + size_t m_n; +}; + +class OpenRecentBookmarksAction + : public lay::Action +{ +public: + OpenRecentBookmarksAction (lay::MainWindow *mw, size_t n) + : lay::Action (), mp_mw (mw), m_n (n) + { } + + void triggered () + { + mp_mw->open_recent_bookmarks (m_n); + } + +private: + lay::MainWindow *mp_mw; + size_t m_n; +}; + } void -MainWindow::do_update_file_menu () +MainWindow::do_update_mru_menus () { std::string mru_menu = "file_menu.open_recent_menu"; @@ -3094,6 +3049,84 @@ MainWindow::do_update_file_menu () } } + + mru_menu = "file_menu.open_recent_menu_sessions"; + + if (menu ()->is_valid (mru_menu)) { + + Action *open_recent_action = menu ()->action (mru_menu); + open_recent_action->set_enabled (true); + + if (m_mru_sessions.size () > 0 && edits_enabled ()) { + + // rebuild MRU menu + menu ()->clear_menu (mru_menu); + + for (std::vector::iterator mru = m_mru_sessions.end (); mru != m_mru_sessions.begin (); ) { + --mru; + size_t i = std::distance (m_mru_sessions.begin (), mru); + Action *action = new OpenRecentSessionAction (this, i); + action->set_title (*mru); + menu ()->insert_item (mru_menu + ".end", tl::sprintf ("open_recent_%d", i + 1), action); + } + + } else { + open_recent_action->set_enabled (false); + } + + } + + mru_menu = "file_menu.open_recent_menu_layer_props"; + + if (menu ()->is_valid (mru_menu)) { + + Action *open_recent_action = menu ()->action (mru_menu); + open_recent_action->set_enabled (true); + + if (m_mru_layer_properties.size () > 0 && edits_enabled ()) { + + // rebuild MRU menu + menu ()->clear_menu (mru_menu); + + for (std::vector::iterator mru = m_mru_layer_properties.end (); mru != m_mru_layer_properties.begin (); ) { + --mru; + size_t i = std::distance (m_mru_layer_properties.begin (), mru); + Action *action = new OpenRecentLayerPropertiesAction (this, i); + action->set_title (*mru); + menu ()->insert_item (mru_menu + ".end", tl::sprintf ("open_recent_%d", i + 1), action); + } + + } else { + open_recent_action->set_enabled (false); + } + + } + + mru_menu = "bookmark_menu.open_recent_menu_bookmarks"; + + if (menu ()->is_valid (mru_menu)) { + + Action *open_recent_action = menu ()->action (mru_menu); + open_recent_action->set_enabled (true); + + if (m_mru_bookmarks.size () > 0 && edits_enabled ()) { + + // rebuild MRU menu + menu ()->clear_menu (mru_menu); + + for (std::vector::iterator mru = m_mru_bookmarks.end (); mru != m_mru_bookmarks.begin (); ) { + --mru; + size_t i = std::distance (m_mru_bookmarks.begin (), mru); + Action *action = new OpenRecentBookmarksAction (this, i); + action->set_title (*mru); + menu ()->insert_item (mru_menu + ".end", tl::sprintf ("open_recent_%d", i + 1), action); + } + + } else { + open_recent_action->set_enabled (false); + } + + } } void @@ -3150,6 +3183,52 @@ MainWindow::open_recent (size_t n) END_PROTECTED } +void +MainWindow::open_recent_session (size_t n) +{ + BEGIN_PROTECTED + + if (n < m_mru_sessions.size ()) { + std::string fn = m_mru_sessions [n]; + restore_session (fn); + add_to_other_mru (fn, cfg_mru_sessions); // make it the latest + } + + END_PROTECTED +} + +void +MainWindow::open_recent_layer_properties (size_t n) +{ + BEGIN_PROTECTED + + if (n < m_mru_layer_properties.size ()) { + std::string fn = m_mru_layer_properties [n]; + load_layer_properties (fn, false /*current view only*/, false /*don't add default*/); + add_to_other_mru (fn, cfg_mru_layer_properties); // make it the latest + } + + END_PROTECTED +} + +void +MainWindow::open_recent_bookmarks (size_t n) +{ + BEGIN_PROTECTED + + if (n < m_mru_bookmarks.size ()) { + std::string fn = m_mru_bookmarks [n]; + if (current_view ()) { + BookmarkList bookmarks; + bookmarks.load (fn); + current_view ()->bookmarks (bookmarks); + add_to_other_mru (fn, cfg_mru_bookmarks); + } + } + + END_PROTECTED +} + void MainWindow::open (int mode) { @@ -3282,11 +3361,12 @@ MainWindow::create_view () mp_layer_toolbox->set_view (current_view ()); - mp_view_stack->addWidget (mp_views.back ()); - mp_lp_stack->addWidget (mp_views.back ()->layer_control_frame ()); - mp_hp_stack->addWidget (mp_views.back ()->hierarchy_control_frame ()); - mp_libs_stack->addWidget (mp_views.back ()->libraries_frame ()); - mp_bm_stack->addWidget (mp_views.back ()->bookmarks_frame ()); + mp_view_stack->add_widget (mp_views.back ()); + mp_lp_stack->add_widget (mp_views.back ()->layer_control_frame ()); + mp_hp_stack->add_widget (mp_views.back ()->hierarchy_control_frame ()); + mp_libs_stack->add_widget (mp_views.back ()->libraries_frame ()); + mp_eo_stack->add_widget (mp_views.back ()->editor_options_frame ()); + mp_bm_stack->add_widget (mp_views.back ()->bookmarks_frame ()); bool f = m_disable_tab_selected; m_disable_tab_selected = true; @@ -3345,11 +3425,12 @@ MainWindow::create_or_load_layout (const std::string *filename, const db::LoadLa mp_layer_toolbox->set_view (current_view ()); - mp_view_stack->addWidget (mp_views.back ()); - mp_lp_stack->addWidget (mp_views.back ()->layer_control_frame ()); - mp_hp_stack->addWidget (mp_views.back ()->hierarchy_control_frame ()); - mp_libs_stack->addWidget (mp_views.back ()->libraries_frame ()); - mp_bm_stack->addWidget (mp_views.back ()->bookmarks_frame ()); + mp_view_stack->add_widget (mp_views.back ()); + mp_lp_stack->add_widget (mp_views.back ()->layer_control_frame ()); + mp_hp_stack->add_widget (mp_views.back ()->hierarchy_control_frame ()); + mp_libs_stack->add_widget (mp_views.back ()->libraries_frame ()); + mp_eo_stack->add_widget (mp_views.back ()->editor_options_frame ()); + mp_bm_stack->add_widget (mp_views.back ()->bookmarks_frame ()); bool f = m_disable_tab_selected; m_disable_tab_selected = true; @@ -3641,6 +3722,10 @@ MainWindow::cm_macro_editor () void MainWindow::cm_show_assistant () { + if (! mp_assistant) { + mp_assistant = new lay::HelpDialog (this); + } + if (mp_assistant->isMinimized ()) { mp_assistant->showNormal (); } else { @@ -3676,13 +3761,7 @@ MainWindow::show_assistant_url (const std::string &url, bool modal) } else { - if (mp_assistant->isMinimized ()) { - mp_assistant->showNormal (); - } else { - mp_assistant->show (); - } - mp_assistant->activateWindow (); - mp_assistant->raise (); + cm_show_assistant (); mp_assistant->load (url); } @@ -3699,13 +3778,7 @@ MainWindow::show_assistant_topic (const std::string &s, bool modal) } else { - if (mp_assistant->isMinimized ()) { - mp_assistant->showNormal (); - } else { - mp_assistant->show (); - } - mp_assistant->activateWindow (); - mp_assistant->raise (); + cm_show_assistant (); mp_assistant->search (s); } @@ -3824,6 +3897,11 @@ MainWindow::menu_activated (const std::string &symbol) cm_help_about (); } else if (symbol == "cm_help_about_qt") { cm_help_about_qt (); + } else if (symbol == "cm_edit_options") { + + m_eo_visible = true; + show_dock_widget (mp_eo_dock_widget, m_eo_visible); + } else { // Try the plugin declarations @@ -3978,10 +4056,12 @@ MainWindow::dropEvent(QDropEvent *event) if (suffix == QString::fromUtf8 ("lyp")) { load_layer_properties (tl::to_string (path), false /*current view only*/, false /*don't add a default*/); + add_to_other_mru (tl::to_string (path), cfg_mru_layer_properties); } else if (suffix == QString::fromUtf8 ("lys")) { restore_session (tl::to_string (path)); + add_to_other_mru (tl::to_string (path), cfg_mru_sessions); } else if (suffix == QString::fromUtf8 ("lyb")) { @@ -3989,6 +4069,7 @@ MainWindow::dropEvent(QDropEvent *event) BookmarkList bookmarks; bookmarks.load (tl::to_string (path)); current_view ()->bookmarks (bookmarks); + add_to_other_mru (tl::to_string (path), cfg_mru_bookmarks); } } else { @@ -4063,108 +4144,6 @@ MainWindow::plugin_removed (lay::PluginDeclaration *cls) } } -// ------------------------------------------------------------ -// Implementation of the "help about" dialog - -HelpAboutDialog::HelpAboutDialog (QWidget *parent) - : QDialog (parent) -{ - mp_ui = new Ui::HelpAboutDialog (); - mp_ui->setupUi (this); - - std::vector build_options; - if (lay::ApplicationBase::instance ()->ruby_interpreter ().available ()) { - build_options.push_back (tl::to_string (tr ("Ruby interpreter ")) + lay::ApplicationBase::instance ()->ruby_interpreter ().version ()); - } - if (lay::ApplicationBase::instance ()->python_interpreter ().available ()) { - build_options.push_back (tl::to_string (tr ("Python interpreter ")) + lay::ApplicationBase::instance ()->python_interpreter ().version ()); - } -#if defined(HAVE_QTBINDINGS) - build_options.push_back (tl::to_string (tr ("Qt bindings for scripts"))); -#endif -#if defined(HAVE_64BIT_COORD) - build_options.push_back (tl::to_string (tr ("Wide coordinates (64 bit)"))); -#endif - - std::string s; - - s = ""; - - s += "

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

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

") + escape_xml (*p) + "

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

"; - s += "

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

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

"; - s += "

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

    "; - - for (std::list::const_iterator pd = lay::plugins ().begin (); pd != lay::plugins ().end (); ++pd) { - s += "
  • "; - if (! pd->description.empty ()) { - s += escape_xml (pd->description); - } else { - s += escape_xml (pd->path); - } - if (! pd->version.empty ()) { - s += " (" + escape_xml (pd->version) + ")"; - } - s += "
  • "; - } - - for (std::list::const_iterator pd = db::plugins ().begin (); pd != db::plugins ().end (); ++pd) { - s += "
  • "; - if (! pd->description.empty ()) { - s += escape_xml (pd->description); - } else { - s += escape_xml (pd->path); - } - if (! pd->version.empty ()) { - s += " (" + escape_xml (pd->version) + ")"; - } - s += "
  • "; - } - - s += "
"; - - } - - s += ""; - - std::string t = tl::to_string (QObject::tr ("About ")) + lay::Version::name (); - - setWindowTitle (tl::to_qstring (t)); - - mp_ui->main->setWordWrap (true); - mp_ui->main->setText (tl::to_qstring (s)); -} - -HelpAboutDialog::~HelpAboutDialog () -{ - delete mp_ui; - mp_ui = 0; -} - // ------------------------------------------------------------ // Declaration of the "plugin" for the menu entries @@ -4189,6 +4168,10 @@ public: menu_entries.push_back (lay::submenu ("@secrets", at, tl::to_string (QObject::tr ("Secret Features")))); menu_entries.push_back (lay::submenu ("@toolbar", at, std::string ())); + at = "edit_menu.end"; + menu_entries.push_back (lay::separator ("edit_options_group:edit_mode", "edit_menu.end")); + menu_entries.push_back (lay::menu_item ("cm_edit_options", "edit_options:edit_mode", "edit_menu.end", tl::to_string (QObject::tr ("Editor Options")) + "(F3)")); + at = "file_menu.end"; menu_entries.push_back (lay::menu_item ("cm_new_layout", "new_layout:edit:edit_mode", at, tl::to_string (QObject::tr ("New Layout")))); menu_entries.push_back (lay::menu_item ("cm_new_panel", "new_panel:edit:edit_mode", at, tl::to_string (QObject::tr ("New Panel")))); @@ -4220,9 +4203,11 @@ public: menu_entries.push_back (lay::separator ("layer_group", at)); menu_entries.push_back (lay::menu_item ("cm_load_layer_props", "load_layer_props:edit", at, tl::to_string (QObject::tr ("Load Layer Properties")))); menu_entries.push_back (lay::menu_item ("cm_save_layer_props", "save_layer_props:edit", at, tl::to_string (QObject::tr ("Save Layer Properties")))); + menu_entries.push_back (lay::submenu ("open_recent_menu_layer_props:edit", at, tl::to_string (QObject::tr ("Recent Layer Properties")))); menu_entries.push_back (lay::separator ("session_group", at)); menu_entries.push_back (lay::menu_item ("cm_restore_session", "restore_session:edit", at, tl::to_string (QObject::tr ("Restore Session")))); menu_entries.push_back (lay::menu_item ("cm_save_session", "save_session", at, tl::to_string (QObject::tr ("Save Session")))); + menu_entries.push_back (lay::submenu ("open_recent_menu_sessions:edit", at, tl::to_string (QObject::tr ("Recent Sessions")))); menu_entries.push_back (lay::separator ("log_group", at)); menu_entries.push_back (lay::menu_item ("cm_view_log", "view_log", at, tl::to_string (QObject::tr ("Log Viewer")))); menu_entries.push_back (lay::separator ("print_group", at)); @@ -4251,6 +4236,7 @@ public: menu_entries.push_back (lay::menu_item ("cm_reset_window_state", "reset_window_state", at, tl::to_string (QObject::tr ("Restore Window")))), menu_entries.push_back (lay::separator ("selection_group", at)); menu_entries.push_back (lay::config_menu_item ("transient_selection", at, tl::to_string (QObject::tr ("Highlight Object Under Mouse")), cfg_sel_transient_mode, "?")); + menu_entries.push_back (lay::config_menu_item ("mouse_tracking", at, tl::to_string (QObject::tr ("Mouse tracking")), cfg_tracking_cursor_enabled, "?")); at = "help_menu.end"; menu_entries.push_back (lay::menu_item ("cm_show_all_tips", "show_all_tips", at, tl::to_string (QObject::tr ("Show All Tips")))); @@ -4270,8 +4256,8 @@ public: menu_entries.push_back (lay::separator ("macros_group", at)); at = "@toolbar.end"; - menu_entries.push_back (lay::menu_item ("cm_prev_display_state", "prev_display_state", at, tl::to_string (QObject::tr ("Back(Shift+Tab)<:/back.png>")))); - menu_entries.push_back (lay::menu_item ("cm_next_display_state", "next_display_state", at, tl::to_string (QObject::tr ("Forward(Tab)<:/forward.png>")))); + menu_entries.push_back (lay::menu_item ("cm_prev_display_state", "prev_display_state", at, tl::to_string (QObject::tr ("Back<:/back.png>")))); + menu_entries.push_back (lay::menu_item ("cm_next_display_state", "next_display_state", at, tl::to_string (QObject::tr ("Forward<:/forward.png>")))); menu_entries.push_back (lay::separator ("toolbar_post_navigation_group", at)); } }; diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index 6ffa374e2..b8c8b9b97 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -20,8 +20,6 @@ */ - - #ifndef HDR_layMainWindow #define HDR_layMainWindow @@ -46,6 +44,7 @@ #include "layProgress.h" #include "layTextProgress.h" #include "layTechnology.h" +#include "layTextProgressDelegate.h" #include "tlException.h" #include "tlDeferredExecution.h" #include "tlObjectCollection.h" @@ -61,10 +60,6 @@ class QStackedWidget; class QDockWidget; class QAction; -namespace Ui { - class HelpAboutDialog; -} - namespace lay { class SettingsForm; @@ -84,38 +79,19 @@ class Navigator; class LayerToolbox; class MainWindow; class HelpDialog; +class HelpAboutDialog; +class ControlWidgetStack; +class ViewWidgetStack; +class ProgressWidget; /** - * @brief A dialog for showing the "help about" dialog + * @brief A big main window class + * + * The main window is the core UI feature of the application. + * The main window is view container, basic controller, configuration root + * and holder of many resources. + * The main window is a singleton. */ -class LAY_PUBLIC HelpAboutDialog - : public QDialog -{ -public: - HelpAboutDialog (QWidget *parent); - ~HelpAboutDialog (); - -private: - Ui::HelpAboutDialog *mp_ui; -}; - -class TextProgressDelegate - : public lay::TextProgress -{ -public: - TextProgressDelegate (MainWindow *mw, int verbosity); - - virtual void update_progress (tl::Progress *progress); - virtual void show_progress_bar (bool show); - virtual bool progress_wants_widget () const; - virtual void progress_add_widget (QWidget *widget); - virtual QWidget *progress_get_widget () const; - virtual void progress_remove_widget (); - -private: - MainWindow *mp_mw; -}; - class LAY_PUBLIC MainWindow : public QMainWindow, public lay::Dispatcher @@ -564,17 +540,22 @@ public: tl::event view_created_event; /** - * @brief Add an entry to the MRU list with the initial technology + * @brief Adds an entry to the MRU list with the initial technology */ void add_mru (const std::string &fn); /** - * @brief Add an entry to the MRU list + * @brief Adds an entry to the MRU list */ void add_mru (const std::string &fn, const std::string &tech); /** - * @brief Get the technology used for loading or creating layouts + * @brief Adds an entry to a specific MRU list given by the "cfg" configuration option + */ + void add_to_other_mru (const std::string &fn_rel, const std::string &cfg); + + /** + * @brief Gets the technology used for loading or creating layouts */ const std::string &initial_technology () { @@ -582,7 +563,7 @@ public: } /** - * @brief Set the initial technology used for loading or creating layouts + * @brief Sets the initial technology used for loading or creating layouts */ void set_initial_technology (const std::string &tech) { @@ -637,6 +618,9 @@ public slots: void close_current_view (); void tab_close_requested (int); void open_recent (size_t n); + void open_recent_session (size_t n); + void open_recent_layer_properties (size_t n); + void open_recent_bookmarks (size_t n); void view_selected (int index); void view_title_changed (); @@ -670,7 +654,7 @@ protected slots: protected: void update_content (); void do_update_menu (); - void do_update_file_menu (); + void do_update_mru_menus (); private: TextProgressDelegate m_text_progress; @@ -680,9 +664,9 @@ private: QToolBar *mp_tool_bar; QDockWidget *mp_navigator_dock_widget; lay::Navigator *mp_navigator; - QDockWidget *mp_hp_dock_widget, *mp_lp_dock_widget, *mp_libs_dock_widget, *mp_bm_dock_widget; - ControlWidgetStack *mp_hp_stack, *mp_lp_stack, *mp_libs_stack, *mp_bm_stack; - bool m_hp_visible, m_lp_visible, m_libs_visible, m_bm_visible, m_navigator_visible, m_layer_toolbox_visible; + QDockWidget *mp_hp_dock_widget, *mp_lp_dock_widget, *mp_libs_dock_widget, *mp_eo_dock_widget, *mp_bm_dock_widget; + ControlWidgetStack *mp_hp_stack, *mp_lp_stack, *mp_libs_stack, *mp_eo_stack, *mp_bm_stack; + bool m_hp_visible, m_lp_visible, m_libs_visible, m_eo_visible, m_bm_visible, m_navigator_visible, m_layer_toolbox_visible; QDockWidget *mp_layer_toolbox_dock_widget; lay::LayerToolbox *mp_layer_toolbox; ViewWidgetStack *mp_view_stack; @@ -700,6 +684,7 @@ private: std::vector mp_views; int m_open_mode; std::vector > m_mru; + std::vector m_mru_sessions, m_mru_layer_properties, m_mru_bookmarks; QStatusBar *mp_status_bar; QStackedWidget *mp_main_stack_widget; ProgressWidget *mp_progress_widget; @@ -712,7 +697,7 @@ private: bool m_disable_tab_selected; bool m_exited; tl::DeferredMethod dm_do_update_menu; - tl::DeferredMethod dm_do_update_file_menu; + tl::DeferredMethod dm_do_update_mru_menus; tl::DeferredMethod dm_exit; QTimer m_message_timer; QTimer m_file_changed_timer; diff --git a/src/lay/lay/layProgressDialog.cc b/src/lay/lay/layProgressDialog.cc new file mode 100644 index 000000000..5d74fed4f --- /dev/null +++ b/src/lay/lay/layProgressDialog.cc @@ -0,0 +1,75 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "layProgressDialog.h" +#include "layProgressWidget.h" + +#include + +namespace lay +{ + +ProgressDialog::ProgressDialog (QWidget *parent, ProgressReporter *pr) + : QDialog (parent), mp_pr (pr) +{ + QVBoxLayout *vbl = new QVBoxLayout (this); + vbl->setMargin (0); + vbl->setSpacing (0); + + mp_progress_widget = new ProgressWidget (pr, this, true); + mp_progress_widget->setObjectName (QString::fromUtf8 ("progress")); + vbl->addWidget (mp_progress_widget); + + setWindowTitle (QObject::tr ("Progress")); + setWindowModality (Qt::WindowModal); +} + +void ProgressDialog::closeEvent (QCloseEvent *) +{ + if (mp_pr) { + // NOTE: We don't kill on close for now. This creates a too easy way to scrap results. + // mp_pr->signal_break (); + // TODO: there should be a warning saying some jobs are pending. + } +} + +void ProgressDialog::set_progress (tl::Progress *progress) +{ + mp_progress_widget->set_progress (progress); +} + +void ProgressDialog::add_widget (QWidget *widget) +{ + mp_progress_widget->add_widget (widget); +} + +void ProgressDialog::remove_widget () +{ + mp_progress_widget->remove_widget (); +} + +QWidget *ProgressDialog::get_widget () const +{ + return mp_progress_widget->get_widget (); +} + +} diff --git a/src/lay/lay/layProgressDialog.h b/src/lay/lay/layProgressDialog.h new file mode 100644 index 000000000..f1036fd2d --- /dev/null +++ b/src/lay/lay/layProgressDialog.h @@ -0,0 +1,61 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_layProgressDialog +#define HDR_layProgressDialog + +#include "layCommon.h" + +#include "tlObject.h" +#include "tlProgress.h" + +#include + +namespace lay +{ + +class ProgressReporter; +class ProgressWidget; + +class ProgressDialog + : public QDialog, + public tl::Object +{ +public: + ProgressDialog (QWidget *parent, lay::ProgressReporter *pr); + + void closeEvent (QCloseEvent * /*event*/); + + void set_progress (tl::Progress *progress); + void add_widget (QWidget *widget); + void remove_widget (); + QWidget *get_widget () const; + +private: + lay::ProgressWidget *mp_progress_widget; + lay::ProgressReporter *mp_pr; +}; + +} + +#endif diff --git a/src/lay/lay/layResources.qrc b/src/lay/lay/layResources.qrc index c3a136d87..041ac55e1 100644 --- a/src/lay/lay/layResources.qrc +++ b/src/lay/lay/layResources.qrc @@ -135,6 +135,8 @@ images/fit_bottom.png images/unlocked_16.png images/locked_16.png + images/bookmark.png + images/bookmark_16.png syntax/ruby.xml diff --git a/src/lay/lay/layTextProgressDelegate.cc b/src/lay/lay/layTextProgressDelegate.cc new file mode 100644 index 000000000..d0dd0db2f --- /dev/null +++ b/src/lay/lay/layTextProgressDelegate.cc @@ -0,0 +1,73 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "layTextProgressDelegate.h" +#include "layMainWindow.h" + +namespace lay +{ + +TextProgressDelegate::TextProgressDelegate (MainWindow *mw, int verbosity) + : lay::TextProgress (verbosity), mp_mw (mw) +{ + // .. nothing yet .. +} + +void TextProgressDelegate::update_progress (tl::Progress *progress) +{ + if (!mp_mw->update_progress (progress)) { + lay::TextProgress::update_progress (progress); + } +} + +void TextProgressDelegate::show_progress_bar (bool show) +{ + if (!mp_mw->show_progress_bar (show)) { + lay::TextProgress::show_progress_bar (show); + } +} + +bool TextProgressDelegate::progress_wants_widget () const +{ + return mp_mw != 0 && mp_mw->progress_wants_widget (); +} + +void TextProgressDelegate::progress_add_widget (QWidget *widget) +{ + if (mp_mw) { + mp_mw->progress_add_widget (widget); + } +} + +QWidget *TextProgressDelegate::progress_get_widget () const +{ + return mp_mw ? mp_mw->progress_get_widget () : 0; +} + +void TextProgressDelegate::progress_remove_widget () +{ + if (mp_mw) { + mp_mw->progress_remove_widget (); + } +} + +} diff --git a/src/lay/lay/layTextProgressDelegate.h b/src/lay/lay/layTextProgressDelegate.h new file mode 100644 index 000000000..d45128861 --- /dev/null +++ b/src/lay/lay/layTextProgressDelegate.h @@ -0,0 +1,55 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_layTextProgressDelegate +#define HDR_layTextProgressDelegate + +#include "layCommon.h" + +#include "layTextProgress.h" + +namespace lay +{ + +class MainWindow; + +class TextProgressDelegate + : public lay::TextProgress +{ +public: + TextProgressDelegate (MainWindow *mw, int verbosity); + + virtual void update_progress (tl::Progress *progress); + virtual void show_progress_bar (bool show); + virtual bool progress_wants_widget () const; + virtual void progress_add_widget (QWidget *widget); + virtual QWidget *progress_get_widget () const; + virtual void progress_remove_widget (); + +private: + MainWindow *mp_mw; +}; + +} + +#endif diff --git a/src/lay/lay/layViewWidgetStack.cc b/src/lay/lay/layViewWidgetStack.cc new file mode 100644 index 000000000..7d7b51f81 --- /dev/null +++ b/src/lay/lay/layViewWidgetStack.cc @@ -0,0 +1,103 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "layViewWidgetStack.h" +#include "layLayoutView.h" + +#include + +namespace lay +{ + +ViewWidgetStack::ViewWidgetStack (QWidget *parent, const char *name) + : QWidget (parent) +{ + setObjectName (QString::fromUtf8 (name)); + + mp_bglabel = new QLabel (this); + mp_bglabel->setAutoFillBackground (true); + mp_bglabel->setText (QObject::tr ("

Use File/Open to open a layout

")); + mp_bglabel->setAlignment (Qt::AlignVCenter | Qt::AlignHCenter); + mp_bglabel->show (); +} + +void ViewWidgetStack::add_widget (LayoutView *w) +{ + m_widgets.push_back (w); + w->setParent (this); + resize_children (); + raise_widget (m_widgets.size () - 1); + + updateGeometry (); +} + +void ViewWidgetStack::remove_widget (size_t index) +{ + if (index < m_widgets.size ()) { + m_widgets.erase (m_widgets.begin () + index); + } + if (m_widgets.size () == 0) { + mp_bglabel->show (); + } +} + +void ViewWidgetStack::raise_widget (size_t index) +{ + if (index < m_widgets.size ()) { + mp_bglabel->hide (); + m_widgets [index]->show (); + } else { + mp_bglabel->show (); + } + + size_t i = 0; + for (std::vector ::iterator child = m_widgets.begin (); child != m_widgets.end (); ++child, ++i) { + if (i != index) { + (*child)->hide (); + } + } +} + +LayoutView *ViewWidgetStack::widget (size_t index) +{ + if (index < m_widgets.size ()) { + return m_widgets [index]; + } else { + return 0; + } +} + +QWidget *ViewWidgetStack::background_widget () +{ + return mp_bglabel; +} + +void ViewWidgetStack::resize_children () +{ + // set the geometry of all children + for (std::vector ::iterator child = m_widgets.begin (); child != m_widgets.end (); ++child) { + (*child)->setGeometry (0, 0, width (), height ()); + } + mp_bglabel->setGeometry (0, 0, width (), height ()); +} + +} diff --git a/src/lay/lay/layViewWidgetStack.h b/src/lay/lay/layViewWidgetStack.h new file mode 100644 index 000000000..c4e7b1c1e --- /dev/null +++ b/src/lay/lay/layViewWidgetStack.h @@ -0,0 +1,64 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_layViewWidgetStack +#define HDR_layViewWidgetStack + +#include "layCommon.h" + +#include + +class QLabel; + +namespace lay +{ + +class LayoutView; + +class ViewWidgetStack + : public QWidget +{ +public: + ViewWidgetStack (QWidget *parent = 0, const char *name = 0); + + void add_widget (LayoutView *w); + void remove_widget (size_t index); + void raise_widget (size_t index); + LayoutView *widget (size_t index); + QWidget *background_widget (); + +protected: + virtual void resizeEvent (QResizeEvent *) + { + resize_children (); + } + + void resize_children (); + + std::vector m_widgets; + QLabel *mp_bglabel; +}; + +} + +#endif diff --git a/src/laybasic/laybasic/BrowserPanel.ui b/src/laybasic/laybasic/BrowserPanel.ui index af94c7c61..d77c6c45a 100644 --- a/src/laybasic/laybasic/BrowserPanel.ui +++ b/src/laybasic/laybasic/BrowserPanel.ui @@ -29,7 +29,7 @@ 2 - + Qt::Horizontal @@ -45,30 +45,7 @@ - - - - Next Topic - - - ... - - - - :/next_topic.png:/next_topic.png - - - - 24 - 24 - - - - true - - - - + @@ -77,7 +54,7 @@ - QFrame::StyledPanel + QFrame::NoFrame QFrame::Raised @@ -100,54 +77,211 @@ Qt::Horizontal - + - + 0 0 - - - 0 - 0 - + + QFrame::NoFrame - - false + + QFrame::Raised - - true - - - false - - - - 1 + + + 0 - + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + + 0 + 0 + + + + + 0 + 0 + + + + false + + + true + + + false + + + + 1 + + + + + + false + + + + Favorites + + + + + + - + - - 4 + + 1 0 + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Find on page + + + + + + + ... + + + + :/find.png:/find.png + + + true + + + + + + + true + + + + + + + ... + + + + :/clear_edit.png:/clear_edit.png + + + true + + + + + + + - - - - - 0 - 0 - + + + + Forward + + + ... + + + + :/forward.png:/forward.png + + + + 24 + 24 + + + + true @@ -197,6 +331,22 @@ + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 6 + 20 + + + + @@ -220,17 +370,47 @@ - - + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 20 + + + + + + + + ... + + + + :/find.png:/find.png + + + true + + + + + - Forward + Next Topic ... - :/forward.png:/forward.png + :/next_topic.png:/next_topic.png @@ -243,7 +423,20 @@ - + + + + + 0 + 0 + + + + true + + + + Qt::Horizontal @@ -266,23 +459,51 @@ - - - - Qt::Horizontal + + + + Add this location to the favorites list - - QSizePolicy::Fixed + + ... - + + + :/bookmark.png:/bookmark.png + + - 6 - 20 + 24 + 24 - + + true + + + + + Find + + + Ctrl+F + + + + + Bookmark + + + Ctrl+B + + + + + Delete Entry + + @@ -292,7 +513,7 @@ - searchEdit + search_edit back_pb forward_pb home_pb @@ -300,5 +521,22 @@ - + + + search_close_button + clicked() + search_frame + hide() + + + 816 + 571 + + + 371 + 577 + + + +
diff --git a/src/laybasic/laybasic/LayoutViewConfigPage2d.ui b/src/laybasic/laybasic/LayoutViewConfigPage2d.ui new file mode 100644 index 000000000..0a9fc17f5 --- /dev/null +++ b/src/laybasic/laybasic/LayoutViewConfigPage2d.ui @@ -0,0 +1,151 @@ + + + LayoutViewConfigPage2d + + + + 0 + 0 + 608 + 318 + + + + Settings + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + Mouse tracking + + + true + + + + 9 + + + 9 + + + 9 + + + 9 + + + 6 + + + + + The color in which the rulers are drawn + + + + + + + + + + Qt::Horizontal + + + + 71 + 31 + + + + + + + + Cursor color + + + + + + + With mouse tracking enabled, a cursor will appear which indicates the snapped mouse position and whether the mouse snaps to objects. + + + true + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + lay::ColorButton + QPushButton +
layWidgets.h
+
+
+ + color_pb + + + +
diff --git a/src/laybasic/laybasic/PropertiesDialog.ui b/src/laybasic/laybasic/PropertiesDialog.ui index dc1e57646..4edb7171e 100644 --- a/src/laybasic/laybasic/PropertiesDialog.ui +++ b/src/laybasic/laybasic/PropertiesDialog.ui @@ -17,7 +17,16 @@ 6 - + + 9 + + + 9 + + + 9 + + 9 @@ -54,7 +63,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -69,7 +87,7 @@ Previous - + :/left.png:/left.png @@ -89,7 +107,7 @@ Next - + :/right.png:/right.png @@ -97,6 +115,39 @@ + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 20 + + + + + + + + Change all + + + + + + + false + + + Relative + + + @@ -120,20 +171,6 @@ - - - - Apply - - - - - - - Apply To All - - - @@ -148,7 +185,24 @@ - + - + + + apply_to_all_cbx + toggled(bool) + relative_cbx + setEnabled(bool) + + + 260 + 372 + + + 320 + 372 + + + +
diff --git a/src/laybasic/laybasic/gsiDeclLayLayoutView.cc b/src/laybasic/laybasic/gsiDeclLayLayoutView.cc index eae19ef72..a2f1d7cb7 100644 --- a/src/laybasic/laybasic/gsiDeclLayLayoutView.cc +++ b/src/laybasic/laybasic/gsiDeclLayLayoutView.cc @@ -467,6 +467,12 @@ Class decl_LayoutView (QT_EXTERNAL_BASE (QWidget) "lay", "Layou "\n" "This constant has been introduced in version 0.27.\n" ) + + gsi::constant ("LV_NoEditorOptionsPanel", (unsigned int) lay::LayoutView::LV_NoEditorOptionsPanel, + "@brief With this option, no editor options panel will be provided (see \\editor_options_frame)\n" + "Use this value with the constructor's 'options' argument.\n" + "\n" + "This constant has been introduced in version 0.27.\n" + ) + gsi::constant ("LV_NoBookmarksView", (unsigned int) lay::LayoutView::LV_NoBookmarksView, "@brief With this option, no bookmarks view will be provided (see \\bookmarks_frame)\n" "Use this value with the constructor's 'options' argument.\n" diff --git a/src/laybasic/laybasic/layBitmap.cc b/src/laybasic/laybasic/layBitmap.cc index 1c9c76336..ad857de2e 100644 --- a/src/laybasic/laybasic/layBitmap.cc +++ b/src/laybasic/laybasic/layBitmap.cc @@ -380,7 +380,38 @@ Bitmap::fill (unsigned int y, unsigned int x1, unsigned int x2) } } -struct PosCompareF +void +Bitmap::clear (unsigned int y, unsigned int x1, unsigned int x2) +{ + unsigned int b1 = x1 / 32; + + uint32_t *sl = scanline (y); + sl += b1; + + unsigned int b = x2 / 32 - b1; + if (b == 0) { + + *sl &= ~masks [x2 % 32] | masks [x1 % 32]; + + } else if (b > 0) { + + *sl++ &= masks [x1 % 32]; + while (b > 1) { + *sl++ = 0; + b--; + } + + unsigned int m = masks [x2 % 32]; + // Hint: if x2==width and width%32==0, sl must not be accessed. This is guaranteed by + // checking if m != 0. + if (m) { + *sl &= ~m; + } + + } +} + +struct PosCompareF { bool operator() (const RenderEdge &a, const RenderEdge &b) const { diff --git a/src/laybasic/laybasic/layBitmap.h b/src/laybasic/laybasic/layBitmap.h index e225134e5..07dc3bb2a 100644 --- a/src/laybasic/laybasic/layBitmap.h +++ b/src/laybasic/laybasic/layBitmap.h @@ -246,6 +246,17 @@ public: */ void fill (unsigned int y, unsigned int x1, unsigned int x2); + /** + * @brief Clears the given part of the scanline + * + * Same as fill(), but resets the bits. + * + * @param y The scanline + * @param x1 The start coordinate + * @param x2 The end coordinate + */ + void clear (unsigned int y, unsigned int x1, unsigned int x2); + /** * @brief Merges the "from" bitmap into this * diff --git a/src/laybasic/laybasic/layBrowserPanel.cc b/src/laybasic/laybasic/layBrowserPanel.cc index 1da5393a7..3dd2393e2 100644 --- a/src/laybasic/laybasic/layBrowserPanel.cc +++ b/src/laybasic/laybasic/layBrowserPanel.cc @@ -22,9 +22,11 @@ #include "layBrowserPanel.h" +#include "layDispatcher.h" #include "tlExceptions.h" #include "tlInternational.h" #include "tlException.h" +#include "tlString.h" #include "ui_BrowserPanel.h" @@ -34,6 +36,10 @@ #endif #include +#include +#include +#include +#include namespace lay { @@ -52,9 +58,44 @@ BrowserTextWidget::loadResource (int type, const QUrl &url) // ------------------------------------------------------------- +void +BookmarkItem::read (tl::Extractor &ex) +{ + while (! ex.at_end () && ! ex.test (";")) { + + std::string k, v; + ex.read_word (k); + ex.test (":"); + ex.read_word_or_quoted (v, "+-."); + ex.test (","); + + if (k == "url") { + url = v; + } else if (k == "title") { + title = v; + } else if (k == "position") { + tl::from_string (v, position); + } + + } +} + +std::string +BookmarkItem::to_string () const +{ + std::string r; + r = "url:" + tl::to_quoted_string (url) + ","; + r += "title:" + tl::to_quoted_string (title) + ","; + r += "position:" + tl::to_string (position) + ";"; + return r; +} + +// ------------------------------------------------------------- + BrowserPanel::BrowserPanel (QWidget *parent) : QWidget (parent), - m_back_dm (this, &BrowserPanel::back) + m_back_dm (this, &BrowserPanel::back), + mp_dispatcher (0) { init (); } @@ -72,21 +113,53 @@ BrowserPanel::init () mp_ui->browser->setReadOnly (true); mp_ui->browser->set_panel (this); mp_ui->browser->setWordWrapMode (QTextOption::WordWrap); + mp_ui->browser->setLineWrapMode (QTextEdit::FixedPixelWidth); + QFontMetrics fm (font ()); + int text_width = fm.boundingRect ('m').width () * 80; + mp_ui->browser->setLineWrapColumnOrWidth (text_width); + + mp_ui->browser->addAction (mp_ui->action_find); + mp_ui->browser->addAction (mp_ui->action_bookmark); + + mp_ui->browser_bookmark_view->addAction (mp_ui->action_delete_bookmark); + mp_ui->browser_bookmark_view->setContextMenuPolicy (Qt::ActionsContextMenu); connect (mp_ui->back_pb, SIGNAL (clicked ()), this, SLOT (back ())); connect (mp_ui->forward_pb, SIGNAL (clicked ()), this, SLOT (forward ())); connect (mp_ui->next_topic_pb, SIGNAL (clicked ()), this, SLOT (next ())); connect (mp_ui->prev_topic_pb, SIGNAL (clicked ()), this, SLOT (prev ())); + connect (mp_ui->bookmark_pb, SIGNAL (clicked ()), this, SLOT (bookmark ())); connect (mp_ui->home_pb, SIGNAL (clicked ()), this, SLOT (home ())); - connect (mp_ui->searchEdit, SIGNAL (returnPressed ()), this, SLOT (search_edited ())); + connect (mp_ui->search_edit, SIGNAL (textEdited (const QString &)), this, SLOT (search_text_changed (const QString &))); + connect (mp_ui->search_edit, SIGNAL (returnPressed ()), this, SLOT (search_edited ())); + connect (mp_ui->search_button, SIGNAL (clicked ()), this, SLOT (search_edited ())); connect (mp_ui->browser, SIGNAL (textChanged ()), this, SLOT (text_changed ())); connect (mp_ui->browser, SIGNAL (backwardAvailable (bool)), mp_ui->back_pb, SLOT (setEnabled (bool))); connect (mp_ui->browser, SIGNAL (forwardAvailable (bool)), mp_ui->forward_pb, SLOT (setEnabled (bool))); connect (mp_ui->outline_tree, SIGNAL (itemActivated (QTreeWidgetItem *, int)), this, SLOT (outline_item_clicked (QTreeWidgetItem *))); + connect (mp_ui->on_page_search_edit, SIGNAL (textChanged (const QString &)), this, SLOT (page_search_edited ())); + connect (mp_ui->search_close_button, SIGNAL (clicked ()), this, SLOT (page_search_edited ()), Qt::QueuedConnection); + connect (mp_ui->on_page_search_edit, SIGNAL (returnPressed ()), this, SLOT (page_search_next ())); + connect (mp_ui->on_page_search_next, SIGNAL (clicked ()), this, SLOT (page_search_next ())); + connect (mp_ui->action_find, SIGNAL (triggered ()), this, SLOT (find ())); + connect (mp_ui->action_bookmark, SIGNAL (triggered ()), this, SLOT (bookmark ())); + connect (mp_ui->action_delete_bookmark, SIGNAL (triggered ()), this, SLOT (delete_bookmark ())); + connect (mp_ui->browser_bookmark_view, SIGNAL (itemDoubleClicked (QTreeWidgetItem *, int)), this, SLOT (bookmark_item_selected (QTreeWidgetItem *))); - mp_ui->searchEdit->hide (); + mp_completer = new QCompleter (this); + mp_completer->setFilterMode (Qt::MatchStartsWith); + mp_completer->setCaseSensitivity (Qt::CaseInsensitive); + mp_completer->setCompletionMode (QCompleter::UnfilteredPopupCompletion); + mp_completer_model = new QStringListModel (mp_completer); + mp_completer->setModel (mp_completer_model); + mp_ui->search_edit->setCompleter (mp_completer); + + mp_ui->search_frame->hide (); + mp_ui->search_edit->hide (); set_label (std::string ()); + + refresh_bookmark_list (); } BrowserPanel::~BrowserPanel () @@ -98,6 +171,37 @@ BrowserPanel::~BrowserPanel () mp_ui = 0; } +void +BrowserPanel::set_dispatcher (lay::Dispatcher *dispatcher, const std::string &cfg_bookmarks) +{ + mp_dispatcher = dispatcher; + m_cfg_bookmarks = cfg_bookmarks; + + m_bookmarks.clear (); + + // load the bookmarks + try { + + if (mp_dispatcher) { + + std::string v; + mp_dispatcher->config_get (m_cfg_bookmarks, v); + + tl::Extractor ex (v.c_str ()); + while (! ex.at_end ()) { + m_bookmarks.push_back (BookmarkItem ()); + m_bookmarks.back ().read (ex); + } + + } + + } catch (...) { + // exceptions ignored here + } + + refresh_bookmark_list (); +} + std::string BrowserPanel::title () const { @@ -110,6 +214,198 @@ BrowserPanel::url () const return m_cached_url; } +void +BrowserPanel::bookmark () +{ + BookmarkItem bm; + bm.url = tl::to_string (mp_ui->browser->historyUrl (0).toString ()); + QString title = mp_ui->browser->document ()->metaInformation (QTextDocument::DocumentTitle); + bm.title = tl::to_string (title); + bm.position = mp_ui->browser->verticalScrollBar ()->value (); + + add_bookmark (bm); + refresh_bookmark_list (); + store_bookmarks (); +} + +void +BrowserPanel::store_bookmarks () +{ + if (mp_dispatcher) { + + std::string s; + for (std::list::const_iterator i = m_bookmarks.begin (); i != m_bookmarks.end (); ++i) { + s += i->to_string (); + } + + mp_dispatcher->config_set (m_cfg_bookmarks, s); + + } +} + +void +BrowserPanel::bookmark_item_selected (QTreeWidgetItem *item) +{ + int index = mp_ui->browser_bookmark_view->indexOfTopLevelItem (item); + if (index < 0 || index >= int (m_bookmarks.size ())) { + return; + } + + std::list::iterator i = m_bookmarks.begin (); + for ( ; i != m_bookmarks.end () && index > 0; --index, ++i) + ; + + if (i == m_bookmarks.end ()) { + return; + } + + BookmarkItem bm = *i; + m_bookmarks.erase (i); + m_bookmarks.push_front (bm); + + refresh_bookmark_list (); + store_bookmarks (); + load (bm.url); + + mp_ui->browser->verticalScrollBar ()->setValue (bm.position); + mp_ui->browser_bookmark_view->topLevelItem (0)->setSelected (true); +} + +void +BrowserPanel::clear_bookmarks () +{ + m_bookmarks.clear (); +} + +void +BrowserPanel::add_bookmark (const BookmarkItem &item) +{ + for (std::list::iterator i = m_bookmarks.begin (); i != m_bookmarks.end (); ) { + std::list::iterator ii = i; + ++ii; + if (*i == item) { + m_bookmarks.erase (i); + } + i = ii; + } + m_bookmarks.push_front (item); +} + +void +BrowserPanel::delete_bookmark () +{ + QTreeWidgetItem *item = mp_ui->browser_bookmark_view->currentItem (); + if (! item) { + return; + } + + int index = mp_ui->browser_bookmark_view->indexOfTopLevelItem (item); + std::list::iterator i = m_bookmarks.begin (); + for ( ; i != m_bookmarks.end () && index > 0; --index, ++i) + ; + + if (i != m_bookmarks.end ()) { + m_bookmarks.erase (i); + refresh_bookmark_list (); + store_bookmarks (); + } +} + +void +BrowserPanel::refresh_bookmark_list () +{ + mp_ui->browser_bookmark_view->setVisible (! m_bookmarks.empty ()); + + mp_ui->browser_bookmark_view->clear (); + for (std::list::const_iterator i = m_bookmarks.begin (); i != m_bookmarks.end (); ++i) { + QTreeWidgetItem *item = new QTreeWidgetItem (mp_ui->browser_bookmark_view); + item->setData (0, Qt::DisplayRole, tl::to_qstring (i->title)); + item->setData (0, Qt::ToolTipRole, tl::to_qstring (i->title)); + item->setData (0, Qt::DecorationRole, QIcon (":/bookmark_16.png")); + } +} + +void +BrowserPanel::find () +{ + mp_ui->search_frame->show (); + mp_ui->on_page_search_edit->setFocus(); +} + +void +BrowserPanel::page_search_edited () +{ + m_search_selection.clear (); + m_search_index = -1; + + if (! mp_ui->search_frame->isVisible () || mp_ui->on_page_search_edit->text ().size () < 2) { + mp_ui->browser->setExtraSelections (m_search_selection); + return; + } + + QString search_text = mp_ui->on_page_search_edit->text (); + + QTextDocument *doc = mp_ui->browser->document (); + for (QTextBlock b = doc->firstBlock (); b.isValid (); b = b.next ()) { + + int from = 0; + int index; + + QString t = b.text (); + + while ((index = t.indexOf (search_text, from, Qt::CaseInsensitive)) >= 0) { + + QTextCursor highlight (b); + highlight.movePosition (QTextCursor::NextCharacter, QTextCursor::MoveAnchor, index); + highlight.movePosition (QTextCursor::NextCharacter, QTextCursor::KeepAnchor, search_text.size ()); + + QTextEdit::ExtraSelection extra_selection; + extra_selection.cursor = highlight; + extra_selection.format.setBackground (QColor (255, 255, 160)); + m_search_selection.push_back (extra_selection); + + from = index + search_text.size (); + + } + + } + + if (! m_search_selection.empty ()) { + m_search_index = 0; + mp_ui->browser->setExtraSelections (m_search_selection); + mp_ui->browser->setTextCursor (m_search_selection [m_search_index].cursor); + } +} + +void +BrowserPanel::page_search_next () +{ + if (m_search_index >= 0) { + + ++m_search_index; + if (m_search_index >= m_search_selection.size ()) { + m_search_index = 0; + } + + mp_ui->browser->setTextCursor (m_search_selection [m_search_index].cursor); + + } +} + +void +BrowserPanel::search_text_changed (const QString &text) +{ + QList strings; + if (! text.isEmpty () && mp_source.get ()) { + std::list cl; + mp_source->search_completers (tl::to_string (text.toLower ()), cl); + for (std::list::const_iterator i = cl.begin (); i != cl.end (); ++i) { + strings.push_back (tl::to_qstring (*i)); + } + } + mp_completer_model->setStringList (strings); +} + void BrowserPanel::text_changed () { @@ -118,6 +414,9 @@ BrowserPanel::text_changed () m_current_title = title; emit title_changed (title); } + + // refresh on-page search + page_search_edited (); } void @@ -259,15 +558,15 @@ BrowserPanel::search (const std::string &s) void BrowserPanel::search_edited () { - if (mp_ui->searchEdit->text ().size () > 0) { + if (mp_ui->search_edit->text ().size () > 0) { QUrl url (tl::to_qstring (m_search_url)); #if QT_VERSION >= 0x050000 QUrlQuery qi; - qi.addQueryItem (tl::to_qstring (m_search_query_item), mp_ui->searchEdit->text ()); + qi.addQueryItem (tl::to_qstring (m_search_query_item), mp_ui->search_edit->text ()); url.setQuery (qi); #else QList > qi; - qi.push_back (QPair (tl::to_qstring (m_search_query_item), mp_ui->searchEdit->text ())); + qi.push_back (QPair (tl::to_qstring (m_search_query_item), mp_ui->search_edit->text ())); url.setQueryItems (qi); #endif load (url.toEncoded ().constData ()); @@ -279,7 +578,7 @@ BrowserPanel::set_search_url (const std::string &url, const std::string &query_i { m_search_url = url; m_search_query_item = query_item; - mp_ui->searchEdit->setVisible (! url.empty ()); + mp_ui->search_edit->setVisible (! url.empty ()); } void @@ -474,6 +773,12 @@ BrowserSource::get_outline (const std::string & /*url*/) return BrowserOutline (); } +void +BrowserSource::search_completers (const std::string & /*search_string*/, std::list & /*completers*/) +{ + // .. nothing here .. +} + std::string BrowserSource::get (const std::string & /*url*/) { diff --git a/src/laybasic/laybasic/layBrowserPanel.h b/src/laybasic/laybasic/layBrowserPanel.h index b974de658..d32366a18 100644 --- a/src/laybasic/laybasic/layBrowserPanel.h +++ b/src/laybasic/laybasic/layBrowserPanel.h @@ -30,12 +30,15 @@ #include "gsiObject.h" #include +#include #include #include #include class QTreeWidgetItem; +class QCompleter; +class QStringListModel; namespace Ui { @@ -46,6 +49,7 @@ namespace lay { class BrowserPanel; +class Dispatcher; /** * @brief Specifies the outline of the document @@ -204,6 +208,11 @@ public: */ virtual BrowserOutline get_outline (const std::string &url); + /** + * @brief Gets the search completer items for a given search string + */ + virtual void search_completers (const std::string &search_string, std::list &completers); + /** * @brief Get the image for a given "int" URL in an image */ @@ -273,6 +282,26 @@ private: BrowserPanel *mp_panel; }; +/** + * @brief A structure describing a bookmark item + */ +struct LAYBASIC_PUBLIC BookmarkItem +{ + BookmarkItem () : position (0) { } + + bool operator== (const BookmarkItem &other) const + { + return url == other.url && position == other.position; + } + + void read (tl::Extractor &ex); + std::string to_string () const; + + std::string url; + std::string title; + int position; +}; + /** * @brief A specialisation of QWidget around a TextBrowser that allows loading a specific resource */ @@ -286,14 +315,26 @@ Q_OBJECT public: /** * @brief Constructor + * + * @param p The parent widget */ - BrowserPanel (QWidget *p); + BrowserPanel (QWidget *p); /** * @brief Dtor */ ~BrowserPanel (); + /** + * @brief Connects the panel to a configuration dispatcher + * + * Doing so allows storing bookmarks and retrieving them. + * + * @param dispatcher If given, this interface will be used to retrieve and store the bookmark list + * @param cfg_bookmarks If dispatcher is given, this will be the configuration key to store the bookmarks + */ + void set_dispatcher (lay::Dispatcher *dispatcher, const std::string &cfg_bookmarks); + /** * @brief Connect to a source object * If " @@ -385,10 +426,25 @@ public slots: */ void home (); + /** + * @brief "find" activated + */ + void find (); + + /** + * @brief "bookmark" activated + */ + void bookmark(); + protected slots: + void page_search_edited (); + void page_search_next(); + void search_text_changed(const QString &text); void search_edited (); void text_changed (); void outline_item_clicked (QTreeWidgetItem *item); + void bookmark_item_selected (QTreeWidgetItem *item); + void delete_bookmark (); protected: virtual QVariant loadResource (int type, const QUrl &url); @@ -408,8 +464,19 @@ private: tl::DeferredMethod m_back_dm; std::string m_search_url, m_search_query_item; QString m_current_title; + QList m_search_selection; + int m_search_index; + QCompleter *mp_completer; + QStringListModel *mp_completer_model; + std::list m_bookmarks; + lay::Dispatcher *mp_dispatcher; + std::string m_cfg_bookmarks; void init (); + void clear_bookmarks (); + void add_bookmark (const BookmarkItem &item); + void refresh_bookmark_list (); + void store_bookmarks (); }; } diff --git a/src/laybasic/laybasic/layCellTreeModel.cc b/src/laybasic/laybasic/layCellTreeModel.cc index b1c91977e..52486cbec 100644 --- a/src/laybasic/laybasic/layCellTreeModel.cc +++ b/src/laybasic/laybasic/layCellTreeModel.cc @@ -28,6 +28,7 @@ #include "dbPCellVariant.h" #include "dbLibraryProxy.h" #include "dbLibrary.h" +#include "dbLibraryManager.h" #include #include @@ -604,14 +605,53 @@ CellTreeModel::mimeData(const QModelIndexList &indexes) const { for (QModelIndexList::const_iterator i = indexes.begin (); i != indexes.end (); ++i) { - if (i->isValid()) { + if (! i->isValid()) { + continue; + } + + if (is_pcell (*i)) { + + lay::CellDragDropData data (mp_layout, mp_library, pcell_id (*i), true); + return data.to_mime_data (); + + } else { + + const db::Cell *c = cell (*i); + if (c) { + + // resolve library proxies + const db::Layout *layout = mp_layout; + const db::Library *library = mp_library; + + const db::LibraryProxy *lib_proxy; + while (layout != 0 && (lib_proxy = dynamic_cast (c)) != 0) { + + const db::Library *lib = db::LibraryManager::instance ().lib (lib_proxy->lib_id ()); + if (! lib) { + break; + } + + library = lib; + layout = &lib->layout (); + + if (layout->is_valid_cell_index (lib_proxy->library_cell_index ())) { + c = &layout->cell (lib_proxy->library_cell_index ()); + } else { + c = 0; + } + + } + + // identify pcell variants and turn them into PCell drag targets + const db::PCellVariant *pcell_var = dynamic_cast (c); + if (pcell_var) { + lay::CellDragDropData data (layout, library, pcell_var->pcell_id (), true, pcell_var->parameters ()); + return data.to_mime_data (); + } else if (c) { + lay::CellDragDropData data (layout, library, c->cell_index (), false); + return data.to_mime_data (); + } - if (is_pcell (*i)) { - lay::CellDragDropData data (mp_layout, mp_library, pcell_id (*i), true); - return data.to_mime_data (); - } else if (cell (*i)) { - lay::CellDragDropData data (mp_layout, mp_library, cell_index (*i), false); - return data.to_mime_data (); } } diff --git a/src/laybasic/laybasic/layEditable.cc b/src/laybasic/laybasic/layEditable.cc index c2ef0fa38..0a86a8c3e 100644 --- a/src/laybasic/laybasic/layEditable.cc +++ b/src/laybasic/laybasic/layEditable.cc @@ -80,6 +80,11 @@ Editables::Editables (db::Manager *manager) Editables::~Editables () { cancel_edits (); + + if (mp_properties_dialog) { + delete mp_properties_dialog; + mp_properties_dialog = 0; + } } void @@ -444,6 +449,14 @@ Editables::select (const db::DPoint &pt, lay::Editable::SelectionMode mode) signal_selection_changed (); } +void +Editables::repeat_selection (Editable::SelectionMode mode) +{ + if (m_last_selected_point.is_point ()) { + select (m_last_selected_point, mode); + } +} + bool Editables::begin_move (const db::DPoint &p, lay::angle_constraint_type ac) { @@ -602,9 +615,8 @@ Editables::cancel_edits () { // close the property dialog if (mp_properties_dialog) { - delete mp_properties_dialog; + mp_properties_dialog->hide (); } - mp_properties_dialog = 0; // cancel any edit operations for (iterator e = begin (); e != end (); ++e) { @@ -615,7 +627,15 @@ Editables::cancel_edits () void Editables::show_properties (QWidget *parent) { - cancel_edits (); + if (selection_size () == 0) { + // try to use the transient selection for the real one + transient_to_selection (); + } + + // re-create a new properties dialog + if (mp_properties_dialog) { + delete mp_properties_dialog; + } mp_properties_dialog = new lay::PropertiesDialog (parent, manager (), this); mp_properties_dialog->show (); } diff --git a/src/laybasic/laybasic/layEditable.h b/src/laybasic/laybasic/layEditable.h index a5d6831e7..b60ed500a 100644 --- a/src/laybasic/laybasic/layEditable.h +++ b/src/laybasic/laybasic/layEditable.h @@ -493,6 +493,13 @@ public: */ void select (const db::DPoint &pt, Editable::SelectionMode mode); + /** + * @brief Repeat the previous selection + * + * This method will not do anything if there is no previous, click-at selection. + */ + void repeat_selection (Editable::SelectionMode mode); + /** * @brief Start "move" operation * diff --git a/src/laybasic/laybasic/layEditorOptionsFrame.cc b/src/laybasic/laybasic/layEditorOptionsFrame.cc new file mode 100644 index 000000000..283462ff1 --- /dev/null +++ b/src/laybasic/laybasic/layEditorOptionsFrame.cc @@ -0,0 +1,70 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "layEditorOptionsFrame.h" +#include "layEditorOptionsPage.h" +#include "layEditorOptionsPages.h" +#include "layPlugin.h" +#include "layLayoutView.h" + +#include + +namespace lay +{ + +EditorOptionsFrame::EditorOptionsFrame (QWidget *parent) + : QFrame (parent), mp_pages (0) +{ + setObjectName (QString::fromUtf8 ("editor_options_frame")); + + QVBoxLayout *left_frame_ly = new QVBoxLayout (this); + left_frame_ly->setMargin (0); + left_frame_ly->setSpacing (0); +} + +EditorOptionsFrame::~EditorOptionsFrame () +{ + // .. nothing yet .. +} + +void +EditorOptionsFrame::populate (LayoutView *view) +{ + std::vector prop_dialog_pages; + for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { + cls->get_editor_options_pages (prop_dialog_pages, view, view->dispatcher ()); + } + + for (std::vector::const_iterator op = prop_dialog_pages.begin (); op != prop_dialog_pages.end (); ++op) { + (*op)->activate (false); + } + + if (mp_pages) { + delete mp_pages; + } + + mp_pages = new lay::EditorOptionsPages (this, prop_dialog_pages, view); + layout ()->addWidget (mp_pages); + setFocusProxy (mp_pages); +} + +} diff --git a/src/laybasic/laybasic/layEditorOptionsFrame.h b/src/laybasic/laybasic/layEditorOptionsFrame.h new file mode 100644 index 000000000..5b17d3d0f --- /dev/null +++ b/src/laybasic/laybasic/layEditorOptionsFrame.h @@ -0,0 +1,55 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_layEditorOptionsFrame +#define HDR_layEditorOptionsFrame + +#include "laybasicCommon.h" +#include + +namespace lay +{ + +class EditorOptionsPages; +class LayoutView; + +class LAYBASIC_PUBLIC EditorOptionsFrame + : public QFrame +{ +public: + EditorOptionsFrame (QWidget *parent); + virtual ~EditorOptionsFrame (); + + void populate (LayoutView *view); + + EditorOptionsPages *pages_widget () const + { + return mp_pages; + } + +public: + EditorOptionsPages *mp_pages; +}; + +} + +#endif diff --git a/src/laybasic/laybasic/layEditorOptionsPage.cc b/src/laybasic/laybasic/layEditorOptionsPage.cc new file mode 100644 index 000000000..cc4a92d91 --- /dev/null +++ b/src/laybasic/laybasic/layEditorOptionsPage.cc @@ -0,0 +1,65 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "tlInternational.h" +#include "layEditorOptionsPage.h" +#include "layEditorOptionsPages.h" + +namespace lay +{ + +// ------------------------------------------------------------------ +// EditorOptionsPage implementation + +EditorOptionsPage::EditorOptionsPage (lay::Dispatcher *dispatcher) + : QWidget (0), mp_owner (0), m_active (true), mp_plugin_declaration (0), mp_dispatcher (dispatcher) +{ + // nothing yet .. +} + +EditorOptionsPage::~EditorOptionsPage () +{ + set_owner (0); +} + +void +EditorOptionsPage::set_owner (EditorOptionsPages *owner) +{ + if (mp_owner) { + mp_owner->unregister_page (this); + } + mp_owner = owner; +} + +void +EditorOptionsPage::activate (bool active) +{ + if (m_active != active) { + m_active = active; + if (mp_owner) { + mp_owner->activate_page (this); + } + } +} + +} diff --git a/src/laybasic/laybasic/layEditorOptionsPage.h b/src/laybasic/laybasic/layEditorOptionsPage.h new file mode 100644 index 000000000..3ee70b1a0 --- /dev/null +++ b/src/laybasic/laybasic/layEditorOptionsPage.h @@ -0,0 +1,84 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_layEditorOptionsPage +#define HDR_layEditorOptionsPage + +#include "laybasicCommon.h" + +#include + +namespace lay +{ + +class PluginDeclaration; +class Dispatcher; +class Plugin; +class EditorOptionsPages; + +/** + * @brief The base class for a object properties page + */ +class LAYBASIC_PUBLIC EditorOptionsPage + : public QWidget +{ +Q_OBJECT + +public: + EditorOptionsPage (lay::Dispatcher *dispatcher); + virtual ~EditorOptionsPage (); + + virtual std::string title () const = 0; + virtual int order () const = 0; + virtual void apply (lay::Dispatcher * /*root*/) { } + virtual void setup (lay::Dispatcher * /*root*/) { } + virtual void commit_recent (lay::Dispatcher * /*root*/) { } + + bool active () const { return m_active; } + void activate (bool active); + void set_owner (EditorOptionsPages *owner); + + const lay::PluginDeclaration *plugin_declaration () const { return mp_plugin_declaration; } + void set_plugin_declaration (const lay::PluginDeclaration *pd) { mp_plugin_declaration = pd; } + +protected slots: + void edited () + { + apply (dispatcher ()); + } + +protected: + lay::Dispatcher *dispatcher () const + { + return mp_dispatcher; + } + +private: + EditorOptionsPages *mp_owner; + bool m_active; + const lay::PluginDeclaration *mp_plugin_declaration; + lay::Dispatcher *mp_dispatcher; +}; + +} + +#endif diff --git a/src/laybasic/laybasic/layEditorOptionsPages.cc b/src/laybasic/laybasic/layEditorOptionsPages.cc new file mode 100644 index 000000000..0cc55fb62 --- /dev/null +++ b/src/laybasic/laybasic/layEditorOptionsPages.cc @@ -0,0 +1,215 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "tlInternational.h" +#include "layEditorOptionsPages.h" +#include "tlExceptions.h" +#include "layPlugin.h" +#include "layLayoutView.h" +#include "layQtTools.h" + +#include +#include +#include +#include +#include +#include + +namespace lay +{ + +// ------------------------------------------------------------------ +// EditorOptionsPages implementation + +struct EOPCompareOp +{ + bool operator() (lay::EditorOptionsPage *a, lay::EditorOptionsPage *b) const + { + return a->order () < b->order (); + } +}; + +EditorOptionsPages::EditorOptionsPages (QWidget *parent, const std::vector &pages, lay::Dispatcher *dispatcher) + : QFrame (parent), mp_dispatcher (dispatcher) +{ + QVBoxLayout *ly1 = new QVBoxLayout (this); + ly1->setMargin (0); + + mp_pages = new QTabWidget (this); + ly1->addWidget (mp_pages); + + m_pages = pages; + for (std::vector ::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) { + (*p)->set_owner (this); + } + + update (0); + setup (); +} + +EditorOptionsPages::~EditorOptionsPages () +{ + while (m_pages.size () > 0) { + delete m_pages.front (); + } +} + +void +EditorOptionsPages::focusInEvent (QFocusEvent * /*event*/) +{ + // Sends the focus to the current page's last focus owner + if (mp_pages->currentWidget () && mp_pages->currentWidget ()->focusWidget ()) { + mp_pages->currentWidget ()->focusWidget ()->setFocus (); + } +} + +bool EditorOptionsPages::has_content () const +{ + for (std::vector ::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) { + // NOTE: we ignore unspecific pages because they are always visible and don't contribute specific content + if ((*p)->active () && (*p)->plugin_declaration () != 0) { + return true; + } + } + return false; +} + +void +EditorOptionsPages::unregister_page (lay::EditorOptionsPage *page) +{ + std::vector pages; + for (std::vector ::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) { + if (*p != page) { + pages.push_back (*p); + } + } + m_pages = pages; + update (0); +} + +void +EditorOptionsPages::activate_page (lay::EditorOptionsPage *page) +{ + try { + if (page->active ()) { + page->setup (mp_dispatcher); + } + } catch (...) { + // catch any errors related to configuration file errors etc. + } + + update (page); +} + +void +EditorOptionsPages::update (lay::EditorOptionsPage *page) +{ + std::vector sorted_pages = m_pages; + std::sort (sorted_pages.begin (), sorted_pages.end (), EOPCompareOp ()); + + if (! page && m_pages.size () > 0) { + page = m_pages.back (); + } + + while (mp_pages->count () > 0) { + mp_pages->removeTab (0); + } + int index = -1; + for (std::vector ::iterator p = sorted_pages.begin (); p != sorted_pages.end (); ++p) { + if ((*p)->active ()) { + if ((*p) == page) { + index = mp_pages->count (); + } + mp_pages->addTab (*p, tl::to_qstring ((*p)->title ())); + } else { + (*p)->setParent (0); + } + } + if (index < 0) { + index = mp_pages->currentIndex (); + } + if (index >= int (mp_pages->count ())) { + index = mp_pages->count () - 1; + } + mp_pages->setCurrentIndex (index); + + setVisible (mp_pages->count () > 0); +} + +void +EditorOptionsPages::setup () +{ + try { + + for (std::vector ::iterator p = m_pages.begin (); p != m_pages.end (); ++p) { + if ((*p)->active ()) { + (*p)->setup (mp_dispatcher); + } + } + + // make the display consistent with the status (this is important for + // PCell parameters where the PCell may be asked to modify the parameters) + do_apply (); + + } catch (...) { + // catch any errors related to configuration file errors etc. + } +} + +void +EditorOptionsPages::do_apply () +{ + for (std::vector ::iterator p = m_pages.begin (); p != m_pages.end (); ++p) { + if ((*p)->active ()) { + // NOTE: we apply to the root dispatcher, so other dispatchers (views) get informed too. + (*p)->apply (mp_dispatcher->dispatcher ()); + } + } +} + +void +EditorOptionsPages::apply () +{ +BEGIN_PROTECTED + do_apply (); +END_PROTECTED_W (this) +} + +// ------------------------------------------------------------------ +// Indicates an error on a line edit + +template +static void configure_from_line_edit (lay::Dispatcher *dispatcher, QLineEdit *le, const std::string &cfg_name) +{ + try { + Value value = Value (0); + tl::from_string (tl::to_string (le->text ()), value); + dispatcher->config_set (cfg_name, tl::to_string (value)); + lay::indicate_error (le, 0); + } catch (tl::Exception &ex) { + lay::indicate_error (le, &ex); + } +} + +} + diff --git a/src/laybasic/laybasic/layEditorOptionsPages.h b/src/laybasic/laybasic/layEditorOptionsPages.h new file mode 100644 index 000000000..d20351e0a --- /dev/null +++ b/src/laybasic/laybasic/layEditorOptionsPages.h @@ -0,0 +1,85 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_layEditorOptionsPages +#define HDR_layEditorOptionsPages + +#include "laybasicCommon.h" +#include "layEditorOptionsPage.h" + +#include + +#include +#include +#include + +class QTabWidget; +class QLabel; + +namespace lay +{ + +class PluginDeclaration; +class Dispatcher; +class Plugin; + +/** + * @brief The object properties dialog + */ +class LAYBASIC_PUBLIC EditorOptionsPages + : public QFrame +{ +Q_OBJECT + +public: + EditorOptionsPages (QWidget *parent, const std::vector &pages, lay::Dispatcher *root); + ~EditorOptionsPages (); + + void unregister_page (lay::EditorOptionsPage *page); + void activate_page (lay::EditorOptionsPage *page); + void focusInEvent (QFocusEvent *event); + + const std::vector &pages () const + { + return m_pages; + } + + bool has_content () const; + +public slots: + void apply (); + void setup (); + +private: + std::vector m_pages; + lay::Dispatcher *mp_dispatcher; + QTabWidget *mp_pages; + + void update (lay::EditorOptionsPage *page); + void do_apply (); +}; + +} + +#endif + diff --git a/src/laybasic/laybasic/layEditorServiceBase.cc b/src/laybasic/laybasic/layEditorServiceBase.cc new file mode 100644 index 000000000..51f93429d --- /dev/null +++ b/src/laybasic/laybasic/layEditorServiceBase.cc @@ -0,0 +1,297 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "layEditorServiceBase.h" +#include "layViewport.h" +#include "layLayoutView.h" +#include "laybasicConfig.h" +#include "layConverters.h" + +namespace lay +{ + +// -------------------------------------------------------------------------------------- + +template +static void +make_circle (double r, const db::DPoint ¢er, db::DPolygon &poly, bool as_hole) +{ + db::DPoint pts [num_pts]; + for (size_t i = 0; i < num_pts; ++i) { + double x = r * cos (M_PI * 2.0 * double (i) / double (num_pts)); + double y = r * sin (M_PI * 2.0 * double (i) / double (num_pts)); + pts [i] = center + db::DVector (x, y); + } + + if (! as_hole) { + poly.assign_hull (pts, pts + num_pts); + } else { + poly.insert_hole (pts, pts + num_pts); + } +} + +class TrackingCursorBase + : public lay::ViewObject +{ +public: + TrackingCursorBase (lay::EditorServiceBase *service, lay::ViewObjectWidget *widget) + : lay::ViewObject (widget, false), mp_service (service) + { } + + uint32_t cursor_color (lay::ViewObjectCanvas &canvas) const + { + QColor color; + if (mp_service) { + color = mp_service->tracking_cursor_color (); + } + if (! color.isValid ()) { + color = canvas.foreground_color (); + } + return color.rgb (); + } + + virtual void render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas) + { + if (mp_service && mp_service->tracking_cursor_enabled ()) { + do_render (vp, canvas); + } + } + +protected: + virtual void do_render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas) = 0; + +private: + lay::EditorServiceBase *mp_service; +}; + +class MouseCursorViewObject + : public TrackingCursorBase +{ +public: + MouseCursorViewObject (lay::EditorServiceBase *service, lay::ViewObjectWidget *widget, const db::DPoint &pt, bool solid) + : TrackingCursorBase (service, widget), m_pt (pt), m_solid (solid) + { } + +protected: + virtual void do_render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas) + { + int dither_pattern = 0; // solid + int cross_dither_pattern = 6; // dotted + + int lw = int (0.5 + 1.0 / canvas.resolution ()); + + std::vector ops; + ops.resize (1); + ops[0] = lay::ViewOp (cursor_color (canvas), lay::ViewOp::Copy, 0, (unsigned int) dither_pattern, 0, lay::ViewOp::Rect, lw, 0); + lay::CanvasPlane *plane = canvas.plane (ops); + + ops[0] = lay::ViewOp (cursor_color (canvas), lay::ViewOp::Copy, 0, (unsigned int) cross_dither_pattern, 0, lay::ViewOp::Rect, lw, 0); + lay::CanvasPlane *cross_plane = canvas.plane (ops); + + lay::Renderer &r = canvas.renderer (); + + double rad = 4.0 / canvas.resolution () / vp.trans ().mag (); + + db::DPolygon c; + if (! m_solid) { + make_circle (rad, m_pt, c, false); + r.draw (c, vp.trans (), 0, plane, 0, 0); + } else { + make_circle (rad * 2, m_pt, c, false); + r.draw (c, vp.trans (), 0, plane, 0, 0); + make_circle (rad, m_pt, c, false); + r.draw (c, vp.trans (), 0, plane, 0, 0); + } + + r.draw (db::DEdge (m_pt + db::DVector (0, -rad * 4), m_pt + db::DVector (0, rad * 4)), vp.trans (), 0, cross_plane, 0, 0); + r.draw (db::DEdge (m_pt + db::DVector (-rad * 4, 0), m_pt + db::DVector (rad * 4, 0)), vp.trans (), 0, cross_plane, 0, 0); + } + +private: + db::DPoint m_pt; + bool m_solid; +}; + +class EdgeMarkerViewObject + : public TrackingCursorBase +{ +public: + EdgeMarkerViewObject (lay::EditorServiceBase *service, lay::ViewObjectWidget *widget, const db::DEdge &edge, bool solid) + : TrackingCursorBase (service, widget), m_edge (edge), m_solid (solid) + { } + +protected: + virtual void do_render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas) + { + if (m_edge.is_degenerate ()) { + return; + } + + int dashed_style = 2; + int solid_style = 0; + + int lw = int (0.5 + 1.0 / canvas.resolution ()); + + std::vector ops; + ops.resize (1); + ops[0] = lay::ViewOp (cursor_color (canvas), lay::ViewOp::Copy, solid_style, 0, 0, lay::ViewOp::Rect, lw, 0); + lay::CanvasPlane *arrow_plane = canvas.plane (ops); + + ops[0] = lay::ViewOp (cursor_color (canvas), lay::ViewOp::Copy, m_solid ? solid_style : dashed_style, 1, 0, lay::ViewOp::Rect, lw, 0); + lay::CanvasPlane *edge_plane = canvas.plane (ops); + + lay::Renderer &r = canvas.renderer (); + r.draw (m_edge, vp.trans (), 0, edge_plane, 0, 0); + + double arrow_length = 12.0 / canvas.resolution () / vp.trans ().mag (); + + double arrow_width_half = arrow_length * 0.25882; // sin(15 deg) + db::DVector n = db::DVector (m_edge.dy (), -m_edge.dx ()) * (arrow_width_half / m_edge.length ()); + db::DVector d = db::DVector (m_edge.dx (), m_edge.dy ()) * (arrow_length / m_edge.length ()); + + if (m_edge.length () < 2 * arrow_length) { + + r.draw (db::DEdge (m_edge.p1 () - n, m_edge.p1 () + n), vp.trans (), 0, arrow_plane, 0, 0); + r.draw (db::DEdge (m_edge.p2 () - n, m_edge.p2 () + n), vp.trans (), 0, arrow_plane, 0, 0); + + } else { + + db::DPoint pts[3]; + db::DPolygon p; + + pts[0] = m_edge.p1 (); + pts[1] = m_edge.p1 () + d - n; + pts[2] = m_edge.p1 () + d + n; + + p.assign_hull (pts, pts + 3); + r.draw (p, vp.trans (), 0, arrow_plane, 0, 0); + + pts[0] = m_edge.p2 (); + pts[1] = m_edge.p2 () - d + n; + pts[2] = m_edge.p2 () - d - n; + + p.assign_hull (pts, pts + 3); + r.draw (p, vp.trans (), 0, arrow_plane, 0, 0); + + } + } + +private: + db::DEdge m_edge; + bool m_solid; +}; + +// -------------------------------------------------------------------------------------- + +EditorServiceBase::EditorServiceBase (lay::LayoutView *view) + : lay::ViewService (view->view_object_widget ()), + lay::Editable (view), + lay::Plugin (view), + m_cursor_enabled (true) +{ + // .. nothing yet .. +} + +EditorServiceBase::~EditorServiceBase () +{ + clear_mouse_cursors (); +} + +void +EditorServiceBase::add_mouse_cursor (const db::DPoint &pt, bool emphasize) +{ + m_mouse_cursor_markers.push_back (new MouseCursorViewObject (this, widget (), pt, emphasize)); +} + +void +EditorServiceBase::add_edge_marker (const db::DEdge &e, bool emphasize) +{ + m_mouse_cursor_markers.push_back (new EdgeMarkerViewObject (this, widget (), e, emphasize)); +} + +void +EditorServiceBase::clear_mouse_cursors () +{ + for (std::vector::iterator r = m_mouse_cursor_markers.begin (); r != m_mouse_cursor_markers.end (); ++r) { + delete *r; + } + m_mouse_cursor_markers.clear (); +} + +void +EditorServiceBase::mouse_cursor_from_snap_details (const lay::PointSnapToObjectResult &snap_details) +{ + clear_mouse_cursors (); + + add_mouse_cursor (snap_details.snapped_point, + snap_details.object_snap == lay::PointSnapToObjectResult::ObjectVertex || + (snap_details.object_snap == lay::PointSnapToObjectResult::ObjectUnspecific && snap_details.object_ref.is_degenerate ())); + + if (snap_details.object_snap == lay::PointSnapToObjectResult::ObjectEdge || + (snap_details.object_snap == lay::PointSnapToObjectResult::ObjectUnspecific && ! snap_details.object_ref.is_degenerate ())) { + add_edge_marker (snap_details.object_ref, false); + } +} + +bool +EditorServiceBase::configure (const std::string &name, const std::string &value) +{ + bool needs_update = false; + + if (name == cfg_tracking_cursor_color) { + + QColor color; + lay::ColorConverter ().from_string (value, color); + + if (color != m_cursor_color) { + m_cursor_color = color; + needs_update = true; + } + + } else if (name == cfg_tracking_cursor_enabled) { + + bool f = m_cursor_enabled; + tl::from_string (value, f); + if (f != m_cursor_enabled) { + m_cursor_enabled = f; + needs_update = true; + } + + } + + if (needs_update) { + for (std::vector::iterator r = m_mouse_cursor_markers.begin (); r != m_mouse_cursor_markers.end (); ++r) { + (*r)->redraw (); + } + } + + // NOTE: we don't take the value as other services may be interested too. + return false; +} + +void +EditorServiceBase::deactivated () +{ + clear_mouse_cursors (); +} + +} diff --git a/src/laybasic/laybasic/layEditorServiceBase.h b/src/laybasic/laybasic/layEditorServiceBase.h new file mode 100644 index 000000000..7735b9deb --- /dev/null +++ b/src/laybasic/laybasic/layEditorServiceBase.h @@ -0,0 +1,122 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_layEditorServiceBase +#define HDR_layEditorServiceBase + +#include "laybasicCommon.h" +#include "layEditable.h" +#include "layViewObject.h" +#include "layPlugin.h" + +namespace lay +{ + +/** + * @brief A generic base class for an editor service + * + * This class offers common services such as a mouse cursor. + */ +class LAYBASIC_PUBLIC EditorServiceBase + : public lay::ViewService, + public lay::Editable, + public lay::Plugin +{ +public: + /** + * @brief Constructor + */ + EditorServiceBase (lay::LayoutView *view); + + /** + * @brief Destructor + */ + ~EditorServiceBase (); + + /** + * @brief Obtain the lay::ViewService interface + */ + lay::ViewService *view_service_interface () + { + return this; + } + + /** + * @brief Obtain the lay::Editable interface + */ + lay::Editable *editable_interface () + { + return this; + } + + /** + * @brief Adds a mouse cursor to the given point + */ + void add_mouse_cursor (const db::DPoint &pt, bool emphasize = false); + + /** + * @brief Adds an edge marker for the given edge + */ + void add_edge_marker (const db::DEdge &e, bool emphasize); + + /** + * @brief Resets the mouse cursor + */ + void clear_mouse_cursors (); + + /** + * @brief Provides a nice mouse tracking cursor from the given snap details + */ + void mouse_cursor_from_snap_details (const lay::PointSnapToObjectResult &snap_details); + + /** + * @brief Gets the tracking cursor color + */ + QColor tracking_cursor_color () const + { + return m_cursor_color; + } + + /** + * @brief Gets a value indicating whether the tracking cursor is enabled + */ + bool tracking_cursor_enabled () const + { + return m_cursor_enabled; + } + +protected: + virtual bool configure (const std::string &name, const std::string &value); + virtual void deactivated (); + +private: + // The marker representing the mouse cursor + std::vector m_mouse_cursor_markers; + QColor m_cursor_color; + bool m_cursor_enabled; +}; + +} + +#endif + diff --git a/src/laybasic/laybasic/layFinder.cc b/src/laybasic/laybasic/layFinder.cc index 378b0a8f7..62369082f 100644 --- a/src/laybasic/laybasic/layFinder.cc +++ b/src/laybasic/laybasic/layFinder.cc @@ -62,7 +62,7 @@ static int inst_point_sel_tests = 10000; Finder::Finder (bool point_mode, bool top_level_sel) : m_min_level (0), m_max_level (0), - mp_layout (0), mp_view (0), m_cv_index (0), m_point_mode (point_mode), m_top_level_sel (top_level_sel) + mp_layout (0), mp_view (0), m_cv_index (0), m_point_mode (point_mode), m_catch_all (false), m_top_level_sel (top_level_sel) { m_distance = std::numeric_limits::max (); } @@ -564,41 +564,32 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const if (match) { + lay::ObjectInstPath found; + found.set_cv_index (m_cv_index); + found.set_topcell (m_topcell); + found.assign_path (path ().begin (), path ().end ()); + found.set_layer (*l); + found.set_shape (*shape); + if (mp_excludes) { // with an exclude list first create the selection item so we can check // if it's part of the exclude set. - lay::ObjectInstPath found; - found.set_cv_index (m_cv_index); - found.set_topcell (m_topcell); - found.assign_path (path ().begin (), path ().end ()); - found.set_layer (*l); - found.set_shape (*shape); - // in point mode just store the found object that has the least "distance" and is // not in the exclude set - if (mp_excludes->find (found) == mp_excludes->end () && closer (d)) { + match = (mp_excludes->find (found) == mp_excludes->end ()); - if (m_founds.empty ()) { - m_founds.push_back (found); - } else { - m_founds.front () = found; - } - } + } - } else if (closer (d)) { + if (match && (catch_all () || closer (d))) { // in point mode just store that found that has the least "distance" - if (m_founds.empty ()) { - m_founds.push_back (lay::ObjectInstPath ()); + if (m_founds.empty () || catch_all ()) { + m_founds.push_back (found); } - m_founds.back ().set_cv_index (m_cv_index); - m_founds.back ().set_topcell (m_topcell); - m_founds.back ().assign_path (path ().begin (), path ().end ()); - m_founds.back ().set_layer (*l); - m_founds.back ().set_shape (*shape); + m_founds.back () = found; } @@ -854,52 +845,38 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d if (match) { + lay::ObjectInstPath found; + found.set_cv_index (m_cv_index); + found.set_topcell (m_topcell); + found.assign_path (path ().begin (), path ().end ()); + + // add the selected instance as the last element of the path + db::InstElement el; + el.inst_ptr = *inst; + if (! m_full_arrays) { + el.array_inst = p; + } + found.add_path (el); + if (mp_excludes) { // with an exclude list first create the selection item so we can check // if it's part of the exclude set. - lay::ObjectInstPath found; - found.set_cv_index (m_cv_index); - found.set_topcell (m_topcell); - found.assign_path (path ().begin (), path ().end ()); - - // add the selected instance as the last element of the path - db::InstElement el; - el.inst_ptr = *inst; - if (! m_full_arrays) { - el.array_inst = p; - } - found.add_path (el); - // in point mode just store the found object that has the least "distance" and is // not in the exclude set - if (mp_excludes->find (found) == mp_excludes->end () && closer (d)) { - if (m_founds.empty ()) { - m_founds.push_back (found); - } else { - m_founds.front () = found; - } - } + match = (mp_excludes->find (found) == mp_excludes->end ()); - } else if (closer (d)) { + } + + if (match && (catch_all () || closer (d))) { // in point mode just store that found that has the least "distance" - if (m_founds.empty ()) { + if (m_founds.empty () || catch_all ()) { m_founds.push_back (lay::ObjectInstPath ()); } - m_founds.back ().set_cv_index (m_cv_index); - m_founds.back ().set_topcell (m_topcell); - m_founds.back ().assign_path (path ().begin (), path ().end ()); - - // add the selected instance as the last element of the path - db::InstElement el; - el.inst_ptr = *inst; - if (! m_full_arrays) { - el.array_inst = p; - } - m_founds.back ().add_path (el); + m_founds.back () = found; } diff --git a/src/laybasic/laybasic/layFinder.h b/src/laybasic/laybasic/layFinder.h index c32a78d9e..84c4aa6a7 100644 --- a/src/laybasic/laybasic/layFinder.h +++ b/src/laybasic/laybasic/layFinder.h @@ -58,13 +58,42 @@ public: * * The point_mode is true, if the finder is supposed to operate in "point mode". * In point mode, the center of the search region is the reference point. In - * non-point mode, every relevant found inside the search region should be - * recorded. + * non-point mode, every relevant found inside the search region will be + * recorded (also see point_mode method). * The base class implementation just stores this flag and provides a read * accessor with the point_mode () method. */ Finder (bool point_mode, bool top_level_sel); + /** + * @brief Gets a flag indicating whether point mode is enabled + * If point mode is enabled in the constructor, the first will check for objects overlapping the + * point (rather than being inside the box) and by default select a single object only. + * See also "set_catch_all". + */ + bool point_mode () const + { + return m_point_mode; + } + + /** + * @brief Gets a flag indicating the capture all founds even in point mode + */ + bool catch_all () const + { + return m_catch_all; + } + + /** + * @brief Sets a flag indicating the capture all founds even in point mode + * By default, in point mode only the closest found is returned. To catch all + * founds in point mode too, set this flag to true. + */ + void set_catch_all (bool f) + { + m_catch_all = f; + } + /** * @brief Destructor (just provided to please the compiler) */ @@ -87,11 +116,6 @@ protected: return m_layers; } - bool point_mode () const - { - return m_point_mode; - } - const std::vector &path () const { return m_path; @@ -168,6 +192,7 @@ private: std::vector m_layers; double m_distance; bool m_point_mode; + bool m_catch_all; bool m_top_level_sel; db::box_convert m_box_convert; db::box_convert m_cell_box_convert; diff --git a/src/laybasic/laybasic/layLayerControlPanel.cc b/src/laybasic/laybasic/layLayerControlPanel.cc index be8eda762..b0038635d 100644 --- a/src/laybasic/laybasic/layLayerControlPanel.cc +++ b/src/laybasic/laybasic/layLayerControlPanel.cc @@ -60,36 +60,6 @@ namespace lay { -// -------------------------------------------------------------------- -// LCPTreeItemDelegate declaration & implementation - -/** - * @brief A layer tree widget helper class - * - * A specialization of the ItemDelegate that bypasses the computation - * of sizeHint and returns the pixmap's size directly for higher - * performance. - */ - -class LCPItemDelegate : public QItemDelegate -{ -public: - LCPItemDelegate (QWidget *parent) - : QItemDelegate (parent) - { } - -private: - virtual QSize - sizeHint (const QStyleOptionViewItem &style, const QModelIndex &index) const - { - if (index.column () == 0) { - return QSize (40, 16); - } else { - return QItemDelegate::sizeHint (style, index); - } - } -}; - // -------------------------------------------------------------------- // LCPTreeWidget declaration & implementation @@ -98,7 +68,6 @@ LCPTreeWidget::LCPTreeWidget (QWidget *parent, lay::LayerTreeModel *model, const { setObjectName (QString::fromUtf8 (name)); setModel (model); - setItemDelegate (new LCPItemDelegate (this)); #if QT_VERSION >= 0x040200 setAllColumnsShowFocus (true); #endif @@ -326,24 +295,21 @@ LayerControlPanel::LayerControlPanel (lay::LayoutView *view, db::Manager *manage mp_model = new lay::LayerTreeModel (this, view); mp_layer_list = new LCPTreeWidget (this, mp_model, "layer_tree"); - mp_model->set_font (mp_layer_list->font ()); - /* - * At least with Qt 4.2.x setting uniform row heights has a strange side effect: - * If a range is selected and the first selection is scrolled out of view, the - * range does not include the first element after having clicked at the second. mp_layer_list->setUniformRowHeights (true); - */ + mp_model->set_font (mp_layer_list->font ()); + mp_layer_list->setIconSize (mp_model->icon_size ()); + l->addWidget (mp_layer_list); connect (mp_layer_list, SIGNAL (double_clicked (const QModelIndex &, Qt::KeyboardModifiers)), this, SLOT (double_clicked (const QModelIndex &, Qt::KeyboardModifiers))); connect (mp_layer_list, SIGNAL (collapsed (const QModelIndex &)), this, SLOT (group_collapsed (const QModelIndex &))); connect (mp_layer_list, SIGNAL (expanded (const QModelIndex &)), this, SLOT (group_expanded (const QModelIndex &))); connect (mp_layer_list, SIGNAL (search_triggered (const QString &)), this, SLOT (search_triggered (const QString &))); + connect (mp_layer_list->selectionModel (), SIGNAL (currentChanged (const QModelIndex &, const QModelIndex &)), this, SLOT (current_index_changed (const QModelIndex &))); mp_layer_list->setContextMenuPolicy (Qt::CustomContextMenu); connect (mp_layer_list, SIGNAL(customContextMenuRequested (const QPoint &)), this, SLOT (context_menu (const QPoint &))); mp_layer_list->header ()->hide (); mp_layer_list->setSelectionMode (QTreeView::ExtendedSelection); mp_layer_list->setRootIsDecorated (false); - mp_layer_list->setIconSize (QSize (32, 16)); // Custom resize mode makes the columns as narrow as possible #if QT_VERSION >= 0x050000 mp_layer_list->header ()->setSectionResizeMode (QHeaderView::ResizeToContents); @@ -539,16 +505,14 @@ LayerControlPanel::cm_insert () BEGIN_PROTECTED_CLEANUP - transaction (tl::to_string (QObject::tr ("Insert views"))); + transaction (tl::to_string (QObject::tr ("Insert layer view"))); props.set_source (tl::to_string (n)); mp_view->init_layer_properties (props); + const LayerPropertiesNode &lp = mp_view->insert_layer (sel, props); - if (transacting ()) { - manager ()->queue (this, new LayerSelectionClearOp ()); - } - mp_layer_list->set_current (sel); + set_current_layer (sel); commit (); @@ -599,18 +563,12 @@ LayerControlPanel::cm_group () mp_view->insert_layer (ins_pos, node); - if (transacting ()) { - manager ()->queue (this, new LayerSelectionClearOp ()); - } + set_current_layer (*sel.rbegin ()); commit (); - end_updates (); - emit order_changed (); - mp_layer_list->set_current (*sel.rbegin ()); - } END_PROTECTED_CLEANUP { recover (); } @@ -1941,8 +1899,14 @@ LayerControlPanel::do_update_content () } void -LayerControlPanel::set_current_layer (const lay::LayerPropertiesConstIterator &l) const +LayerControlPanel::set_current_layer (const lay::LayerPropertiesConstIterator &l) { + if (transacting ()) { + manager ()->queue (this, new LayerSelectionClearOp ()); + } + + end_updates (); + mp_layer_list->set_current (l); } @@ -2070,6 +2034,17 @@ LayerControlPanel::update_required (int f) m_do_update_content_dm (); } +void +LayerControlPanel::current_index_changed (const QModelIndex &index) +{ + lay::LayerPropertiesConstIterator iter = mp_model->iterator (index); + if (! iter.is_null () && ! iter.at_end ()) { + emit current_layer_changed (iter); + } else { + emit current_layer_changed (lay::LayerPropertiesConstIterator ()); + } +} + void LayerControlPanel::group_collapsed (const QModelIndex &index) { diff --git a/src/laybasic/laybasic/layLayerControlPanel.h b/src/laybasic/laybasic/layLayerControlPanel.h index d7caea7a5..7276bdfbe 100644 --- a/src/laybasic/laybasic/layLayerControlPanel.h +++ b/src/laybasic/laybasic/layLayerControlPanel.h @@ -250,7 +250,7 @@ public: * * This will also select this layer. */ - void set_current_layer (const lay::LayerPropertiesConstIterator &l) const; + void set_current_layer (const lay::LayerPropertiesConstIterator &l); /** * @brief Return the current layer index @@ -284,6 +284,7 @@ public: signals: void order_changed (); void tab_changed (); + void current_layer_changed (const lay::LayerPropertiesConstIterator &iter); public slots: void cm_new_tab (); @@ -321,6 +322,7 @@ public slots: void tab_context_menu (const QPoint &pt); void group_collapsed (const QModelIndex &index); void group_expanded (const QModelIndex &index); + void current_index_changed (const QModelIndex &index); void up_clicked (); void upup_clicked (); void down_clicked (); diff --git a/src/laybasic/laybasic/layLayerTreeModel.cc b/src/laybasic/laybasic/layLayerTreeModel.cc index 3a6ce2f77..c622bf5e4 100644 --- a/src/laybasic/laybasic/layLayerTreeModel.cc +++ b/src/laybasic/laybasic/layLayerTreeModel.cc @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -558,6 +559,161 @@ LayerTreeModel::empty_within_view_predicate (const QModelIndex &index) const } } +LAYBASIC_PUBLIC +QIcon +LayerTreeModel::icon_for_layer (const lay::LayerPropertiesConstIterator &iter, lay::LayoutView *view, unsigned int w, unsigned int h, unsigned int di_off, bool no_state) +{ + h = std::max ((unsigned int) 16, h); + w = std::max ((unsigned int) 16, w); + + lay::color_t def_color = 0x808080; + lay::color_t fill_color = iter->has_fill_color (true) ? iter->eff_fill_color (true) : def_color; + lay::color_t frame_color = iter->has_frame_color (true) ? iter->eff_frame_color (true) : def_color; + + QImage image (w, h, QImage::Format_ARGB32); + image.fill (view->background_color ().rgb ()); + + // upper scanline is a dummy one + uint32_t *sl0 = (uint32_t *) image.scanLine (0); + uint32_t transparent = QColor (Qt::transparent).rgba (); + for (size_t i = 0; i < w; ++i) { + *sl0++ = transparent; + } + + // TODO: adjust the resolution according to the oversampling mode + lay::Bitmap fill (w, h, 1.0); + lay::Bitmap frame (w, h, 1.0); + lay::Bitmap text (w, h, 1.0); + lay::Bitmap vertex (w, h, 1.0); + + unsigned int wp = w - 1; + + if (! no_state && ! iter->visible (true)) { + + wp = w / 4; + + // Show the arrow if it is invisible also locally. + if (! iter->visible (false)) { + + unsigned int aw = h / 4; + unsigned int ap = w / 2 - 1; + for (unsigned int i = 0; i <= aw; ++i) { + text.fill (h / 2 - 1 - i, ap, ap + aw - i + 1); + text.fill (h / 2 - 1 + i, ap, ap + aw - i + 1); + } + + } + + } + + if (! no_state && view->no_stipples ()) { + // Show a partial stipple pattern only for "no stipple" mode + for (unsigned int i = 1; i < h - 2; ++i) { + fill.fill (i, w - 1 - w / 4, w); + } + } else { + for (unsigned int i = 1; i < h - 2; ++i) { + fill.fill (i, w - 1 - wp, w); + } + } + + int lw = iter->width (true); + if (lw < 0) { + // default line width is 0 for parents and 1 for leafs + lw = iter->has_children () ? 0 : 1; + } + + int p0 = lw / 2; + p0 = std::max (0, std::min (int (w / 4 - 1), p0)); + + int p1 = (lw - 1) / 2; + p1 = std::max (0, std::min (int (w / 4 - 1), p1)); + + int p0x = p0, p1x = p1; + unsigned int ddx = 0; + unsigned int ddy = h - 2 - p1 - p0; + if (iter->xfill (true)) { + ddx = wp - p0 - p1 - 1; + } + unsigned int d = ddx / 2; + + frame.fill (p0, w - 1 - (wp - p1), w); + frame.fill (h - 2 - p1, w - 1 - (wp - p1), w); + + for (unsigned int i = p0; i < h - 2; ++i) { + + frame.fill (i, w - 1 - p0, w - p0); + frame.fill (i, w - 1 - (wp - p1), w - (wp - p1)); + frame.fill (i, w - 1 - p0x, w - p0x); + frame.fill (i, w - 1 - (wp - p1x), w - (wp - p1x)); + + while (d < ddx) { + d += ddy; + frame.fill (i, w - 1 - p0x, w - p0x); + frame.fill (i, w - 1 - (wp - p1x), w - (wp - p1x)); + ++p0x; + ++p1x; + } + + if (d >= ddx) { + d -= ddx; + } + + } + + if (! no_state && ! iter->valid (true)) { + + unsigned int bp = w - 1 - ((w * 7) / 8 - 1); + unsigned int be = bp + h / 2; + unsigned int bw = h / 4 - 1; + unsigned int by = h / 2 - 1; + + for (unsigned int i = 0; i < bw + 2; ++i) { + fill.clear (by - i, bp - 1, be); + fill.clear (by + i, bp - 1, be); + } + + for (unsigned int i = 0; i < bw; ++i) { + text.fill (by - i, bp + bw - i - 1, bp + bw - i + 1); + text.fill (by - i - 1, bp + bw - i - 1, bp + bw - i + 1); + text.fill (by - i, bp + bw + i, bp + bw + i + 2); + text.fill (by - i - 1, bp + bw + i, bp + bw + i + 2); + text.fill (by + i, bp + bw - i - 1, bp + bw - i + 1); + text.fill (by + i + 1, bp + bw - i - 1, bp + bw - i + 1); + text.fill (by + i, bp + bw + i, bp + bw + i + 2); + text.fill (by + i + 1, bp + bw + i, bp + bw + i + 2); + } + + } + + vertex.fill (h / 2 - 1, w - 1 - wp / 2, w - wp / 2); + + lay::ViewOp::Mode mode = lay::ViewOp::Copy; + + // create fill + single_bitmap_to_image (lay::ViewOp (fill_color, mode, 0, iter->eff_dither_pattern (true), di_off), fill, &image, view->dither_pattern (), view->line_styles (), w, h); + // create frame + if (lw == 0) { + single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0 /*solid line*/, 2 /*dotted*/, 0), frame, &image, view->dither_pattern (), view->line_styles (), w, h); + } else { + single_bitmap_to_image (lay::ViewOp (frame_color, mode, iter->eff_line_style (true), 0, 0, lay::ViewOp::Rect, lw), frame, &image, view->dither_pattern (), view->line_styles (), w, h); + } + // create text + single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0, 0, 0), text, &image, view->dither_pattern (), view->line_styles (), w, h); + // create vertex + single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0, 0, 0, lay::ViewOp::Cross, iter->marked (true) ? 9/*mark size*/ : 0), vertex, &image, view->dither_pattern (), view->line_styles (), w, h); + + QPixmap pixmap = QPixmap::fromImage (image); // Qt 4.6.0 workaround + return QIcon (pixmap); +} + +QSize +LayerTreeModel::icon_size () const +{ + unsigned int is = ((QFontInfo (m_font).pixelSize () + 15) / 16) * 16; + return QSize (is * 2, is); +} + QVariant LayerTreeModel::data (const QModelIndex &index, int role) const { @@ -568,6 +724,16 @@ LayerTreeModel::data (const QModelIndex &index, int role) const return QVariant (); + } else if (role == Qt::SizeHintRole) { + + if (index.column () == 0) { + // NOTE: for some reason, the widget clips the icon when inside a tree and needs a some what bigger width .. + QSize is = icon_size (); + return QVariant (is + QSize (is.width () / 4, 0)); + } else { + return QVariant (); + } + } else if (role == Qt::DisplayRole || role == Qt::EditRole) { if (index.column () == 1) { @@ -593,142 +759,10 @@ LayerTreeModel::data (const QModelIndex &index, int role) const } } - unsigned int w = 32; - unsigned int h = 16; - + QSize is = icon_size (); + if (animate_visible) { - - lay::color_t def_color = 0x808080; - lay::color_t fill_color = iter->has_fill_color (true) ? iter->eff_fill_color (true) : def_color; - lay::color_t frame_color = iter->has_frame_color (true) ? iter->eff_frame_color (true) : def_color; - - QImage image (w, h, QImage::Format_RGB32); - image.fill (m_background_color.rgb ()); - - // TODO: adjust the resolution according to the oversampling mode - lay::Bitmap fill (w, h, 1.0); - lay::Bitmap frame (w, h, 1.0); - lay::Bitmap text (w, h, 1.0); - lay::Bitmap vertex (w, h, 1.0); - - unsigned int mask_w = 31; - unsigned int mask_all = 0xfffffffe; - unsigned int mask_left = 0x80000000; - unsigned int mask_right = 0x00000002; - unsigned int mask_center = 0x00010000; - - if (! iter->visible (true)) { - - mask_w = 8; - mask_all = 0xff800000; - mask_left = 0x80000000; - mask_right = 0x00800000; - mask_center = 0x08000000; - - // Show the arrow if it is invisible also locally. - if (! iter->visible (false)) { - text.scanline (4) [0] = 0x00008000 << 1; - text.scanline (5) [0] = 0x00018000 << 1; - text.scanline (6) [0] = 0x00038000 << 1; - text.scanline (7) [0] = 0x00078000 << 1; - text.scanline (8) [0] = 0x00038000 << 1; - text.scanline (9) [0] = 0x00018000 << 1; - text.scanline (10) [0] = 0x00008000 << 1; - } - - } - - if (mp_view->no_stipples ()) { - // Show a partial stipple pattern only for "no stipple" mode - for (unsigned int i = 1; i < h - 2; ++i) { - fill.scanline (i) [0] = 0xff800000; - } - } else { - for (unsigned int i = 1; i < h - 2; ++i) { - fill.scanline (i) [0] = mask_all; - } - } - - int lw = iter->width (true); - if (lw < 0) { - // default line width is 0 for parents and 1 for leafs - lw = iter->has_children () ? 0 : 1; - } - - int p0 = lw / 2; - int p1 = (lw - 1) / 2; - if (p0 < 0) { - p0 = 0; - } else if (p0 > 7) { - p0 = 7; - } - if (p1 < 0) { - p1 = 0; - } else if (p1 > 7) { - p1 = 7; - } - - int p0x = p0, p1x = p1; - unsigned int ddx = 0; - unsigned int ddy = h - 2 - p1 - p0; - if (iter->xfill (true)) { - ddx = mask_w - p0 - p1 - 1; - } - unsigned int d = ddx / 2; - - frame.scanline (p0) [0] = mask_all << p1; - for (unsigned int i = p0; i < h - 2; ++i) { - frame.scanline (i) [0] |= (mask_left >> p0) | (mask_right << p1); - frame.scanline (i) [0] |= (mask_left >> p0x) | (mask_right << p1x); - while (d < ddx) { - d += ddy; - frame.scanline (i) [0] |= (mask_left >> p0x) | (mask_right << p1x); - ++p0x; - ++p1x; - } - if (d >= ddx) { - d -= ddx; - } - } - frame.scanline (h - 2 - p1) [0] = mask_all << p1; - - if (! iter->valid (true)) { - - text.scanline (4) [0] |= 0x00000c60; - text.scanline (5) [0] |= 0x00000ee0; - text.scanline (6) [0] |= 0x000007c0; - text.scanline (7) [0] |= 0x00000380; - text.scanline (8) [0] |= 0x000007c0; - text.scanline (9) [0] |= 0x00000ee0; - text.scanline (10) [0] |= 0x00000c60; - - for (unsigned int i = 3; i < 12; ++i) { - fill.scanline (i) [0] &= ~0x00001ff0; - frame.scanline (i) [0] &= ~0x00001ff0; - } - - } - - vertex.scanline (h / 2 - 1) [0] = mask_center; - - lay::ViewOp::Mode mode = lay::ViewOp::Copy; - - // create fill - single_bitmap_to_image (lay::ViewOp (fill_color, mode, 0, iter->eff_dither_pattern (true), di_off), fill, &image, mp_view->dither_pattern (), mp_view->line_styles (), w, h); - // create frame - if (lw == 0) { - single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0 /*solid line*/, 2 /*dotted*/, 0), frame, &image, mp_view->dither_pattern (), mp_view->line_styles (), w, h); - } else { - single_bitmap_to_image (lay::ViewOp (frame_color, mode, iter->eff_line_style (true), 0, 0, lay::ViewOp::Rect, lw), frame, &image, mp_view->dither_pattern (), mp_view->line_styles (), w, h); - } - // create text - single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0, 0, 0), text, &image, mp_view->dither_pattern (), mp_view->line_styles (), w, h); - // create vertex - single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0, 0, 0, lay::ViewOp::Cross, iter->marked (true) ? 9/*mark size*/ : 0), vertex, &image, mp_view->dither_pattern (),mp_view->line_styles (), w, h); - - QPixmap pixmap = QPixmap::fromImage (image); // Qt 4.6.0 workaround - return QVariant (QIcon (pixmap)); - + return QVariant (icon_for_layer (iter, mp_view, is.width (), is.height (), di_off)); } else { return QVariant (QIcon ()); } diff --git a/src/laybasic/laybasic/layLayerTreeModel.h b/src/laybasic/laybasic/layLayerTreeModel.h index b52d7cdaf..852184ab1 100644 --- a/src/laybasic/laybasic/layLayerTreeModel.h +++ b/src/laybasic/laybasic/layLayerTreeModel.h @@ -104,6 +104,16 @@ public: virtual QModelIndex index (int row, int column, const QModelIndex &parent) const; virtual QModelIndex parent (const QModelIndex &index) const; + /** + * @brief Provides an icon for a given layer style + */ + static QIcon icon_for_layer (const lay::LayerPropertiesConstIterator &iter, lay::LayoutView *view, unsigned int w, unsigned int h, unsigned int di_offset, bool no_state = false); + + /** + * @brief Gets the preferred icon size + */ + QSize icon_size () const; + /** * @brief Convert a lay::LayerPropertiesConstIterator to a QModelIndex */ diff --git a/src/laybasic/laybasic/layLayoutView.cc b/src/laybasic/laybasic/layLayoutView.cc index f7ae519f5..fe3c642b7 100644 --- a/src/laybasic/laybasic/layLayoutView.cc +++ b/src/laybasic/laybasic/layLayoutView.cc @@ -46,14 +46,12 @@ #include "layLayoutView.h" #include "layViewOp.h" #include "layViewObject.h" -#include "layLayoutViewConfigPages.h" #include "laybasicConfig.h" #include "layConverters.h" #include "layGridNet.h" #include "layMove.h" #include "layZoomBox.h" #include "layMouseTracker.h" -#include "layTipDialog.h" #include "layEditable.h" #include "layFixedFont.h" #include "laySelector.h" @@ -68,16 +66,12 @@ #include "layBookmarkManagementForm.h" #include "layNetlistBrowserDialog.h" #include "layBookmarksView.h" -#include "laySelectCellViewForm.h" -#include "layCellSelectionForm.h" -#include "layLayoutPropertiesForm.h" -#include "layLayoutStatisticsForm.h" +#include "layEditorOptionsFrame.h" +#include "layEditorOptionsPages.h" #include "dbClipboard.h" #include "dbLayout.h" #include "dbLayoutUtils.h" -#include "dbRecursiveShapeIterator.h" #include "dbManager.h" -#include "dbEdgeProcessor.h" #include "dbLibrary.h" #include "rdb.h" #include "rdbMarkerBrowserDialog.h" @@ -262,7 +256,8 @@ LayoutView::LayoutView (db::Manager *manager, bool editable, lay::Plugin *plugin m_editable (editable), m_options (options), m_annotation_shapes (manager), - dm_prop_changed (this, &LayoutView::do_prop_changed) + dm_prop_changed (this, &LayoutView::do_prop_changed), + dm_setup_editor_option_pages (this, &LayoutView::do_setup_editor_options_pages) { // either it's us or the parent has a dispatcher tl_assert (dispatcher () != 0); @@ -280,7 +275,8 @@ LayoutView::LayoutView (lay::LayoutView *source, db::Manager *manager, bool edit m_editable (editable), m_options (options), m_annotation_shapes (manager), - dm_prop_changed (this, &LayoutView::do_prop_changed) + dm_prop_changed (this, &LayoutView::do_prop_changed), + dm_setup_editor_option_pages (this, &LayoutView::do_setup_editor_options_pages) { // either it's us or the parent has a dispatcher tl_assert (dispatcher () != 0); @@ -398,6 +394,7 @@ LayoutView::init (db::Manager *mgr, QWidget * /*parent*/) mp_libraries_view = 0; mp_bookmarks_view = 0; mp_libraries_frame = 0; + mp_editor_options_frame = 0; mp_bookmarks_frame = 0; mp_min_hier_spbx = 0; mp_max_hier_spbx = 0; @@ -460,20 +457,10 @@ LayoutView::init (db::Manager *mgr, QWidget * /*parent*/) m_marker_halo = true; m_transient_selection_mode = true; m_sel_inside_pcells = false; - m_move_to_origin_mode_x = 0; - m_move_to_origin_mode_y = 0; - m_del_cell_mode = 0; - m_layer_hier_mode = 0; m_add_other_layers = false; m_always_show_source = false; m_always_show_ld = true; m_always_show_layout_index = false; - m_duplicate_hier_mode = 2; - m_clear_before = true; - m_copy_cva = -1; - m_copy_cvr = -1; - m_copy_layera = -1; - m_copy_layerr = -1; m_search_range = 5; m_layer_properties_lists.push_back (new LayerPropertiesList ()); @@ -560,14 +547,13 @@ LayoutView::init (db::Manager *mgr, QWidget * /*parent*/) if ((m_options & LV_NoLibrariesView) == 0 && (m_options & LV_Naked) == 0) { - QFrame *libraries_frame = new QFrame (0); - libraries_frame->setObjectName (QString::fromUtf8 ("libs_frame")); - mp_libraries_frame = libraries_frame; - QVBoxLayout *left_frame_ly = new QVBoxLayout (libraries_frame); + mp_libraries_frame = new QFrame (0); + mp_libraries_frame->setObjectName (QString::fromUtf8 ("libs_frame")); + QVBoxLayout *left_frame_ly = new QVBoxLayout (mp_libraries_frame); left_frame_ly->setMargin (0); left_frame_ly->setSpacing (0); - mp_libraries_view = new lay::LibrariesView (this, libraries_frame, "libs"); + mp_libraries_view = new lay::LibrariesView (this, mp_libraries_frame, "libs"); left_frame_ly->addWidget (mp_libraries_view, 1 /*stretch*/); connect (mp_libraries_view, SIGNAL (active_library_changed (int)), this, SLOT (active_library_changed (int))); @@ -575,6 +561,15 @@ LayoutView::init (db::Manager *mgr, QWidget * /*parent*/) } + if ((m_options & LV_NoEditorOptionsPanel) == 0 && (m_options & LV_Naked) == 0) { + + mp_editor_options_frame = new lay::EditorOptionsFrame (0); + mp_editor_options_frame->populate (this); + + connect (mp_editor_options_frame, SIGNAL (destroyed ()), this, SLOT (side_panel_destroyed ())); + + } + // occupy services and editables: // these services get deleted by the canvas destructor automatically: if ((m_options & LV_NoTracker) == 0) { @@ -598,6 +593,7 @@ LayoutView::init (db::Manager *mgr, QWidget * /*parent*/) connect (mp_control_frame, SIGNAL (destroyed ()), this, SLOT (side_panel_destroyed ())); connect (mp_control_panel, SIGNAL (tab_changed ()), this, SLOT (layer_tab_changed ())); connect (mp_control_panel, SIGNAL (order_changed ()), this, SLOT (layer_order_changed ())); + connect (mp_control_panel, SIGNAL (current_layer_changed (const lay::LayerPropertiesConstIterator &)), this, SLOT (current_layer_changed_slot (const lay::LayerPropertiesConstIterator &))); /* connect (mp_control_panel, SIGNAL (marked_changed ()), this, SLOT (prop_changed ())); connect (mp_control_panel, SIGNAL (width_changed ()), this, SLOT (prop_changed ())); @@ -616,9 +612,6 @@ LayoutView::init (db::Manager *mgr, QWidget * /*parent*/) create_plugins (); - m_new_layer_props.layer = 1; - m_new_layer_props.datatype = 0; - config_setup (); } @@ -707,6 +700,11 @@ LayoutView::~LayoutView () mp_libraries_frame = 0; mp_libraries_view = 0; + if (mp_editor_options_frame) { + delete mp_editor_options_frame; + } + mp_editor_options_frame = 0; + if (mp_bookmarks_frame) { delete mp_bookmarks_frame; } @@ -719,6 +717,26 @@ QWidget *LayoutView::menu_parent_widget () return this; } +lay::EditorOptionsPages *LayoutView::editor_options_pages () +{ + if (! mp_editor_options_frame) { + return 0; + } else { + return mp_editor_options_frame->pages_widget (); + } +} + +void LayoutView::do_setup_editor_options_pages () +{ + // intialize the editor option pages + lay::EditorOptionsPages *eo_pages = editor_options_pages (); + if (eo_pages) { + for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { + (*op)->setup (this); + } + } +} + void LayoutView::side_panel_destroyed () { if (sender () == mp_control_frame) { @@ -730,6 +748,8 @@ void LayoutView::side_panel_destroyed () } else if (sender () == mp_libraries_frame) { mp_libraries_frame = 0; mp_libraries_view = 0; + } else if (sender () == mp_editor_options_frame) { + mp_editor_options_frame = 0; } else if (sender () == mp_bookmarks_frame) { mp_bookmarks_frame = 0; mp_bookmarks_view = 0; @@ -868,6 +888,8 @@ void LayoutView::create_plugins (const lay::PluginDeclaration *except_this) } + dm_setup_editor_option_pages (); + mode (default_mode ()); } @@ -1645,6 +1667,14 @@ LayoutView::configure (const std::string &name, const std::string &value) } } +void +LayoutView::config_finalize () +{ + // It's important that the editor option pages are updated last - because the + // configuration change may trigger other configuration changes + dm_setup_editor_option_pages (); +} + void LayoutView::enable_edits (bool enable) { @@ -1679,7 +1709,41 @@ LayoutView::enable_edits (bool enable) } } -void +bool +LayoutView::set_or_request_current_layer (unsigned int cv_index, const db::LayerProperties &lp) +{ + bool ok = set_current_layer (cv_index, lp); + if (ok) { + return true; + } + + if (! mp_control_panel) { + return false; + } + + const lay::CellView &cv = cellview (cv_index); + if (! cv.is_valid ()) { + return false; + } + + if (QMessageBox::question (this, tr ("Create Layer"), tr ("Layer %1 does not exist yet. Create it now?").arg (tl::to_qstring (lp.to_string ()))) == QMessageBox::Yes) { + + lay::LayerPropertiesNode lpn; + lpn.set_source (lay::ParsedLayerSource (lp, cv_index)); + init_layer_properties (lpn); + + transaction (tl::to_string (QObject::tr ("Create new layer"))); + set_current_layer (lay::LayerPropertiesConstIterator (& insert_layer (end_layers (), lpn))); + commit (); + + return true; + + } + + return false; +} + +bool LayoutView::set_current_layer (unsigned int cv_index, const db::LayerProperties &lp) { // rename the ones that got shifted. @@ -1687,13 +1751,14 @@ LayoutView::set_current_layer (unsigned int cv_index, const db::LayerProperties while (! l.at_end ()) { if (l->source (true).cv_index () == int (cv_index) && l->source (true).layer_props ().log_equal (lp)) { set_current_layer (l); - break; + return true; } ++l; } + return false; } -void +void LayoutView::set_current_layer (const lay::LayerPropertiesConstIterator &l) { if (mp_control_panel) { @@ -5272,923 +5337,6 @@ LayoutView::cut () } } -void -LayoutView::cm_align_cell_origin () -{ - int cv_index = active_cellview_index (); - if (cv_index >= 0) { - - const db::Cell *cell = cellview (cv_index).cell (); - if (! cell) { - return; - } - if (cell->is_proxy ()) { - throw tl::Exception (tl::to_string (QObject::tr ("Cannot use this function on a PCell or library cell"))); - } - - lay::AlignCellOptionsDialog dialog (this); - if (dialog.exec_dialog (m_align_cell_options)) { - - clear_selection (); - - transaction (tl::to_string (QObject::tr ("Align cell origin"))); - - db::Box bbox; - - if (m_align_cell_options.visible_only) { - for (lay::LayerPropertiesConstIterator l = begin_layers (); !l.at_end (); ++l) { - if (! l->has_children () && l->layer_index () >= 0 && l->cellview_index () == cv_index && l->visible (true /*real*/)) { - bbox += cell->bbox (l->layer_index ()); - } - } - } else { - bbox = cell->bbox (); - } - - db::Coord refx, refy; - switch (m_align_cell_options.mode_x) { - case -1: - refx = bbox.left (); - break; - case 1: - refx = bbox.right (); - break; - default: - refx = bbox.center ().x (); - break; - } - switch (m_align_cell_options.mode_y) { - case -1: - refy = bbox.bottom (); - break; - case 1: - refy = bbox.top (); - break; - default: - refy = bbox.center ().y (); - break; - } - - db::Layout &layout = cellview (cv_index)->layout (); - db::Cell &nc_cell = layout.cell (cell->cell_index ()); - - db::Trans t (db::Vector (-refx + db::coord_traits::rounded (m_align_cell_options.xpos / layout.dbu ()), -refy + db::coord_traits::rounded (m_align_cell_options.ypos / layout.dbu ()))); - - for (unsigned int i = 0; i < layout.layers (); ++i) { - if (layout.is_valid_layer (i)) { - db::Shapes &shapes = nc_cell.shapes (i); - for (db::Shapes::shape_iterator s = shapes.begin (db::ShapeIterator::All); ! s.at_end (); ++s) { - shapes.transform (*s, t); - } - } - } - - for (db::Cell::const_iterator inst = nc_cell.begin (); ! inst.at_end (); ++inst) { - nc_cell.transform (*inst, t); - } - - if (m_align_cell_options.adjust_parents) { - - std::vector > insts_to_modify; - for (db::Cell::parent_inst_iterator pi = nc_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { - insts_to_modify.push_back (std::make_pair (& layout.cell (pi->parent_cell_index ()), pi->child_inst ())); - } - - db::Trans ti (db::Vector (refx, refy)); - for (std::vector >::const_iterator im = insts_to_modify.begin (); im != insts_to_modify.end (); ++im) { - im->first->transform (im->second, db::Trans (db::Vector (im->second.complex_trans ().trans (db::Vector (refx, refy))))); - } - - } - - commit (); - - } - - } -} - -void -LayoutView::cm_cell_user_properties () -{ - if (! mp_hierarchy_panel) { - return; - } - - int cv_index = active_cellview_index (); - cell_path_type path; - mp_hierarchy_panel->current_cell (cv_index, path); - - if (cv_index >= 0 && path.size () > 0) { - - db::Layout &layout = cellview (cv_index)->layout (); - db::Cell &cell = layout.cell (path.back ()); - db::properties_id_type prop_id = cell.prop_id (); - - lay::UserPropertiesForm props_form (this); - if (props_form.show (this, cv_index, prop_id)) { - - transaction (tl::to_string (QObject::tr ("Edit cell's user propertes"))); - cell.prop_id (prop_id); - commit (); - - } - - } -} - -void -LayoutView::cm_cell_replace () -{ - if (! mp_hierarchy_panel) { - return; - } - - int cv_index = active_cellview_index (); - std::vector paths; - mp_hierarchy_panel->selected_cells (cv_index, paths); - - if (cv_index >= 0 && paths.size () > 0) { - - if (paths.size () > 1) { - throw tl::Exception (tl::to_string (QObject::tr ("Replace cell cannot be used when multiple cells are selected"))); - } - - db::Layout &layout = cellview (cv_index)->layout (); - - bool needs_to_ask = false; - for (std::vector::const_iterator p = paths.begin (); p != paths.end () && ! needs_to_ask; ++p) { - if (layout.is_valid_cell_index (p->back ()) && ! layout.cell (p->back ()).is_leaf ()) { - needs_to_ask = true; - } - } - - - lay::ReplaceCellOptionsDialog mode_dialog (this); - - db::cell_index_type with_cell = paths.front ().back (); - int mode = needs_to_ask ? m_del_cell_mode : 0; - - if (mode_dialog.exec_dialog (cellview (cv_index), mode, with_cell)) { - - if (needs_to_ask) { - m_del_cell_mode = mode; - } - - if (with_cell != paths.front ().back ()) { - - // remember the current path - cell_path_type cell_path (cellview (cv_index).combined_unspecific_path ()); - - clear_selection (); - - transaction (tl::to_string (QObject::tr ("Replace cells"))); - - // replace instances of the target cell with the new cell - - db::Cell &target_cell = layout.cell (paths.front ().back ()); - - std::vector > parents; - for (db::Cell::parent_inst_iterator pi = target_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { - parents.push_back (std::make_pair (pi->parent_cell_index (), pi->child_inst ())); - } - - for (std::vector >::const_iterator p = parents.begin (); p != parents.end (); ++p) { - db::CellInstArray ia = p->second.cell_inst (); - ia.object ().cell_index (with_cell); - layout.cell (p->first).replace (p->second, ia); - } - - std::set cells_to_delete; - for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { - if (! p->empty () && layout.is_valid_cell_index (p->back ())) { - cells_to_delete.insert (p->back ()); - if (mode == 2) { - layout.cell (p->back ()).collect_called_cells (cells_to_delete); - } - } - } - - // support a propagation use case: - std::set cells_below_replacement_cell; - cells_below_replacement_cell.insert (with_cell); - layout.cell (with_cell).collect_called_cells (cells_below_replacement_cell); - for (std::set::const_iterator c = cells_below_replacement_cell.begin (); c != cells_below_replacement_cell.end (); ++c) { - cells_to_delete.erase (*c); - } - - if (mode == 0 || mode == 2) { - layout.delete_cells (cells_to_delete); - } else if (mode == 1) { - layout.prune_cells (cells_to_delete); - } - - layout.cleanup (); - - commit (); - - // If one of the cells in the path was deleted, establish a valid path - - bool needs_update = false; - for (size_t i = cell_path.size (); i > 0; ) { - --i; - if (! layout.is_valid_cell_index (cell_path [i])) { - cell_path.erase (cell_path.begin () + i, cell_path.end ()); - needs_update = true; - } - } - - if (needs_update) { - select_cell (cell_path, cv_index); - } - - } - - } - - } -} - -void -LayoutView::cm_lay_convert_to_static () -{ - // end move operations, cancel edit operations - cancel_edits (); - clear_selection (); - - int cv_index = active_cellview_index (); - if (cv_index >= 0) { - - db::Layout &layout = cellview (cv_index)->layout (); - - transaction (tl::to_string (QObject::tr ("Convert all cells to static"))); - - std::vector cells; - for (db::Layout::const_iterator c = layout.begin (); c != layout.end (); ++c) { - cells.push_back (c->cell_index ()); - } - - std::map cell_map; - for (std::vector::const_iterator c = cells.begin (); c != cells.end (); ++c) { - if (layout.is_valid_cell_index (*c)) { - db::cell_index_type new_cell = layout.convert_cell_to_static (*c); - if (new_cell != *c) { - cell_map.insert (std::make_pair (*c, new_cell)); - } - } - } - - // rewrite instances - for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { - for (db::Cell::const_iterator i = c->begin (); ! i.at_end (); ++i) { - std::map::const_iterator cm = cell_map.find (i->cell_index ()); - if (cm != cell_map.end ()) { - db::CellInstArray ci = i->cell_inst (); - ci.object ().cell_index (cm->second); - c->replace (*i, ci); - } - } - } - - layout.cleanup (); - - commit (); - - } -} - -void -LayoutView::cm_cell_convert_to_static () -{ - if (! mp_hierarchy_panel) { - return; - } - - int cv_index = active_cellview_index (); - std::vector paths; - mp_hierarchy_panel->selected_cells (cv_index, paths); - - if (cv_index >= 0 && paths.size () > 0) { - - db::Layout &layout = cellview (cv_index)->layout (); - - // remember the current path - cell_path_type cell_path (cellview (cv_index).combined_unspecific_path ()); - - clear_selection (); - - transaction (tl::to_string (QObject::tr ("Convert cells to static"))); - - std::map cell_map; - - for (std::vector::iterator p = paths.begin (); p != paths.end (); ++p) { - if (! p->empty () && layout.is_valid_cell_index (p->back ())) { - db::cell_index_type new_cell = layout.convert_cell_to_static (p->back ()); - if (new_cell != p->back ()) { - cell_map.insert (std::make_pair (p->back (), new_cell)); - p->back () = new_cell; - } - } - } - - // rewrite instances - for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { - for (db::Cell::const_iterator i = c->begin (); ! i.at_end (); ++i) { - std::map::const_iterator cm = cell_map.find (i->cell_index ()); - if (cm != cell_map.end ()) { - db::CellInstArray ci = i->cell_inst (); - ci.object ().cell_index (cm->second); - c->replace (*i, ci); - } - } - } - - layout.cleanup (); - - commit (); - - // If one of the cells in the path was deleted, establish a valid path - - bool needs_update = false; - for (size_t i = cell_path.size (); i > 0; ) { - --i; - if (! layout.is_valid_cell_index (cell_path [i])) { - cell_path.erase (cell_path.begin () + i, cell_path.end ()); - needs_update = true; - } - } - - if (needs_update) { - select_cell (cell_path, cv_index); - } - - } -} - -static void -collect_cells_to_delete (const db::Layout &layout, const db::Cell &cell, std::set &called) -{ - // don't delete proxies - they are deleted later when the layout is cleaned - for (db::Cell::child_cell_iterator cc = cell.begin_child_cells (); ! cc.at_end (); ++cc) { - if (called.find (*cc) == called.end () && !layout.cell (*cc).is_proxy ()) { - called.insert (*cc); - collect_cells_to_delete (layout, layout.cell (*cc), called); - } - } -} - -void -LayoutView::cm_cell_delete () -{ - if (! mp_hierarchy_panel) { - return; - } - - int cv_index = active_cellview_index (); - std::vector paths; - mp_hierarchy_panel->selected_cells (cv_index, paths); - - if (cv_index >= 0 && paths.size () > 0) { - - db::Layout &layout = cellview (cv_index)->layout (); - - bool needs_to_ask = false; - for (std::vector::const_iterator p = paths.begin (); p != paths.end () && ! needs_to_ask; ++p) { - if (layout.is_valid_cell_index (p->back ()) && ! layout.cell (p->back ()).is_leaf ()) { - needs_to_ask = true; - } - } - - int mode = m_del_cell_mode; - if (! needs_to_ask) { - mode = 0; - } - - lay::DeleteCellModeDialog mode_dialog (this); - if (! needs_to_ask || mode_dialog.exec_dialog (mode)) { - - if (needs_to_ask) { - m_del_cell_mode = mode; - } - - // remember the current path - cell_path_type cell_path (cellview (cv_index).combined_unspecific_path ()); - - clear_selection (); - - std::set cells_to_delete; - for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { - if (! p->empty () && layout.is_valid_cell_index (p->back ())) { - cells_to_delete.insert (p->back ()); - if (mode == 2) { - collect_cells_to_delete (layout, layout.cell (p->back ()), cells_to_delete); - } - } - } - - transaction (tl::to_string (QObject::tr ("Delete cells"))); - - if (mode == 0 || mode == 2) { - layout.delete_cells (cells_to_delete); - } else if (mode == 1) { - layout.prune_cells (cells_to_delete); - } - - layout.cleanup (); - - commit (); - - // If one of the cells in the path was deleted, establish a valid path - - bool needs_update = false; - for (size_t i = cell_path.size (); i > 0; ) { - --i; - if (! layout.is_valid_cell_index (cell_path [i])) { - cell_path.erase (cell_path.begin () + i, cell_path.end ()); - needs_update = true; - } - } - - if (needs_update) { - select_cell (cell_path, cv_index); - } - - } - - } -} - -void -LayoutView::cm_layer_copy () -{ - if (mp_control_panel) { - mp_control_panel->copy (); - } -} - -void -LayoutView::cm_layer_cut () -{ - if (mp_control_panel) { - db::Transaction trans (manager (), tl::to_string (QObject::tr ("Cut Layers"))); - mp_control_panel->cut (); - } -} - -void -LayoutView::cm_layer_paste () -{ - if (mp_control_panel) { - db::Transaction trans (manager (), tl::to_string (QObject::tr ("Paste Layers"))); - mp_control_panel->paste (); - } -} - -void -LayoutView::cm_cell_cut () -{ - if (mp_hierarchy_panel) { - // TODO: currently the hierarchy panel's cut function does it's own transaction handling. - // Otherwise the cut function is not working propertly. - mp_hierarchy_panel->cut (); - } -} - -void -LayoutView::cm_cell_paste () -{ - if (mp_hierarchy_panel) { - db::Transaction trans (manager (), tl::to_string (QObject::tr ("Paste Cells"))); - mp_hierarchy_panel->paste (); - } -} - -void -LayoutView::cm_cell_copy () -{ - if (mp_hierarchy_panel) { - mp_hierarchy_panel->copy (); - } -} - -void -LayoutView::cm_cell_flatten () -{ - if (! mp_hierarchy_panel) { - return; - } - - tl_assert (is_editable ()); - - int cv_index = active_cellview_index (); - if (cv_index >= 0) { - - const lay::CellView &cv = cellview (cv_index); - if (cv.is_valid ()) { - - std::vector paths; - mp_hierarchy_panel->selected_cells (cv_index, paths); - if (paths.empty ()) { - throw tl::Exception (tl::to_string (QObject::tr ("No cells selected for flattening"))); - } - - for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { - if (p->size () > 0 && cv->layout ().cell (p->back ()).is_proxy ()) { - throw tl::Exception (tl::to_string (QObject::tr ("Cannot use this function on a PCell or library cell"))); - } - } - - FlattenInstOptionsDialog options_dialog (this); - - int flatten_insts_levels = -1; - bool prune = true; - if (options_dialog.exec_dialog (flatten_insts_levels, prune) && flatten_insts_levels != 0) { - - bool supports_undo = true; - - if (manager () && manager ()->is_enabled ()) { - - lay::TipDialog td (QApplication::activeWindow (), - tl::to_string (QObject::tr ("Undo buffering for the following operation can be memory and time consuming.\nChoose \"Yes\" to use undo buffering or \"No\" for no undo buffering. Warning: in the latter case, the undo history will be lost.\n\nChoose undo buffering?")), - "flatten-undo-buffering", - lay::TipDialog::yesnocancel_buttons); - - lay::TipDialog::button_type button = lay::TipDialog::null_button; - td.exec_dialog (button); - if (button == lay::TipDialog::cancel_button) { - return; - } - - supports_undo = (button == lay::TipDialog::yes_button); - - } else { - supports_undo = false; - } - - cancel_edits (); - clear_selection (); - - if (manager ()) { - if (! supports_undo) { - manager ()->clear (); - } else { - manager ()->transaction (tl::to_string (QObject::tr ("Flatten cell"))); - } - } - - db::Layout &layout = cv->layout (); - - std::set child_cells; - for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { - if (p->size () > 0) { - layout.cell (p->back ()).collect_called_cells (child_cells); - } - } - - // don't flatten cells which are child cells of the cells to flatten - std::set cells_to_flatten; - for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { - if (p->size () > 0 && child_cells.find (p->back ()) == child_cells.end ()) { - cells_to_flatten.insert (p->back ()); - } - } - - for (std::set::const_iterator c = cells_to_flatten.begin (); c != cells_to_flatten.end (); ++c) { - db::Cell &target_cell = layout.cell (*c); - layout.flatten (target_cell, flatten_insts_levels, prune); - } - - layout.cleanup (); - - if (supports_undo && manager ()) { - manager ()->commit (); - } - - } - - } - - } -} - -void -LayoutView::cm_cell_rename () -{ - if (! mp_hierarchy_panel) { - return; - } - - int cv_index = active_cellview_index (); - cell_path_type path; - mp_hierarchy_panel->current_cell (cv_index, path); - - if (cv_index >= 0 && path.size () > 0) { - - lay::RenameCellDialog name_dialog (this); - - db::Layout &layout = cellview (cv_index)->layout (); - std::string name (layout.cell_name (path.back ())); - if (name_dialog.exec_dialog (layout, name)) { - - transaction (tl::to_string (QObject::tr ("Rename cell"))); - layout.rename_cell (path.back (), name.c_str ()); - commit (); - - } - - } -} - -void -LayoutView::cm_cell_select () -{ - if (mp_hierarchy_panel) { - mp_hierarchy_panel->cm_cell_select (); - } -} - -void -LayoutView::cm_open_current_cell () -{ - set_current_cell_path (active_cellview_index (), cellview (active_cellview_index ()).combined_unspecific_path ()); -} - -void -LayoutView::cm_cell_hide () -{ - if (mp_hierarchy_panel) { - - std::vector paths; - mp_hierarchy_panel->selected_cells (active_cellview_index (), paths); - - transaction (tl::to_string (QObject::tr ("Hide cell"))); - - for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { - if (! p->empty ()) { - hide_cell (p->back (), active_cellview_index ()); - } - } - - commit (); - - } -} - -void -LayoutView::cm_cell_show () -{ - if (mp_hierarchy_panel) { - - std::vector paths; - mp_hierarchy_panel->selected_cells (active_cellview_index (), paths); - - transaction (tl::to_string (QObject::tr ("Show cell"))); - - for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { - if (! p->empty ()) { - show_cell (p->back (), active_cellview_index ()); - } - } - - commit (); - - } -} - -void -LayoutView::cm_cell_show_all () -{ - if (mp_hierarchy_panel) { - transaction (tl::to_string (QObject::tr ("Show all cells"))); - show_all_cells (); - commit (); - } -} - -void -LayoutView::cm_select_all () -{ - if (mp_control_panel) { - mp_control_panel->cm_select_all (); - } -} - -void -LayoutView::cm_new_tab () -{ - if (mp_control_panel) { - mp_control_panel->cm_new_tab (); - } -} - -void -LayoutView::cm_remove_tab () -{ - if (mp_control_panel) { - mp_control_panel->cm_remove_tab (); - } -} - -void -LayoutView::cm_rename_tab () -{ - if (mp_control_panel) { - mp_control_panel->cm_rename_tab (); - } -} - -void -LayoutView::cm_make_invalid () -{ - if (mp_control_panel) { - mp_control_panel->cm_make_invalid (); - } -} - -void -LayoutView::cm_make_valid () -{ - if (mp_control_panel) { - mp_control_panel->cm_make_valid (); - } -} - -void -LayoutView::cm_hide () -{ - if (mp_control_panel) { - mp_control_panel->cm_hide (); - } -} - -void -LayoutView::cm_hide_all () -{ - if (mp_control_panel) { - mp_control_panel->cm_hide_all (); - } -} - -void -LayoutView::cm_show_only () -{ - if (mp_control_panel) { - mp_control_panel->cm_show_only (); - } -} - -void -LayoutView::cm_show_all () -{ - if (mp_control_panel) { - mp_control_panel->cm_show_all (); - } -} - -void -LayoutView::cm_show () -{ - if (mp_control_panel) { - mp_control_panel->cm_show (); - } -} - -void -LayoutView::cm_rename () -{ - if (mp_control_panel) { - mp_control_panel->cm_rename (); - } -} - -void -LayoutView::cm_delete () -{ - if (mp_control_panel) { - mp_control_panel->cm_delete (); - } -} - -void -LayoutView::cm_insert () -{ - if (mp_control_panel) { - mp_control_panel->cm_insert (); - } -} - -void -LayoutView::cm_group () -{ - if (mp_control_panel) { - mp_control_panel->cm_group (); - } -} - -void -LayoutView::cm_ungroup () -{ - if (mp_control_panel) { - mp_control_panel->cm_ungroup (); - } -} - -void -LayoutView::cm_source () -{ - if (mp_control_panel) { - mp_control_panel->cm_source (); - } -} - -void -LayoutView::cm_sort_by_name () -{ - if (mp_control_panel) { - mp_control_panel->cm_sort_by_name (); - } -} - -void -LayoutView::cm_sort_by_ild () -{ - if (mp_control_panel) { - mp_control_panel->cm_sort_by_ild (); - } -} - -void -LayoutView::cm_sort_by_idl () -{ - if (mp_control_panel) { - mp_control_panel->cm_sort_by_idl (); - } -} - -void -LayoutView::cm_sort_by_ldi () -{ - if (mp_control_panel) { - mp_control_panel->cm_sort_by_ldi (); - } -} - -void -LayoutView::cm_sort_by_dli () -{ - if (mp_control_panel) { - mp_control_panel->cm_sort_by_dli (); - } -} - -void -LayoutView::cm_regroup_by_index () -{ - if (mp_control_panel) { - mp_control_panel->cm_regroup_by_index (); - } -} - -void -LayoutView::cm_regroup_by_datatype () -{ - if (mp_control_panel) { - mp_control_panel->cm_regroup_by_datatype (); - } -} - -void -LayoutView::cm_regroup_by_layer () -{ - if (mp_control_panel) { - mp_control_panel->cm_regroup_by_layer (); - } -} - -void -LayoutView::cm_regroup_flatten () -{ - if (mp_control_panel) { - mp_control_panel->cm_regroup_flatten (); - } -} - -void -LayoutView::cm_expand_all () -{ - if (mp_control_panel) { - mp_control_panel->cm_expand_all (); - } -} - -void -LayoutView::cm_add_missing () -{ - if (mp_control_panel) { - mp_control_panel->cm_add_missing (); - } -} - void LayoutView::add_missing_layers () { @@ -6240,6 +5388,12 @@ LayoutView::layer_snapshot () const return state; } +void +LayoutView::current_layer_changed_slot (const lay::LayerPropertiesConstIterator &iter) +{ + current_layer_changed_event (iter); +} + void LayoutView::add_new_layers (const LayerState &state) { @@ -6277,12 +5431,6 @@ LayoutView::add_new_layers (const LayerState &state) } } -void -LayoutView::cm_remove_unused () -{ - remove_unused_layers (); -} - void LayoutView::remove_unused_layers () { @@ -6442,11 +5590,13 @@ LayoutView::mode (int m) if (m != m_mode) { m_mode = m; + lay::Plugin *active_plugin = 0; if (m > 0) { for (std::vector::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) { if ((*p)->plugin_declaration ()->id () == m) { + active_plugin = *p; mp_canvas->activate ((*p)->view_service_interface ()); break; } @@ -6458,6 +5608,22 @@ LayoutView::mode (int m) mp_canvas->activate (mp_move_service); } + lay::EditorOptionsPages *eo_pages = editor_options_pages (); + if (eo_pages) { + + // TODO: this is very inefficient as each "activate" will regenerate the tabs + for (std::vector::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) { + bool is_active = false; + if ((*op)->plugin_declaration () == 0) { + is_active = true; + } else if (active_plugin && active_plugin->plugin_declaration () == (*op)->plugin_declaration ()) { + is_active = true; + } + (*op)->activate (is_active); + } + + } + } } @@ -6489,138 +5655,6 @@ LayoutView::default_mode () return 0; // TODO: any generic scheme? is select, should be ruler.. } -void -LayoutView::do_cm_duplicate (bool interactive) -{ - // Do duplicate simply by concatenating copy & paste currently. - // Save the clipboard state before in order to preserve the current content - db::Clipboard saved_clipboard; - db::Clipboard::instance ().swap (saved_clipboard); - - try { - copy (); - clear_selection (); - cancel (); - if (interactive) { - paste_interactive (); - } else { - paste (); - } - db::Clipboard::instance ().swap (saved_clipboard); - } catch (...) { - db::Clipboard::instance ().swap (saved_clipboard); - throw; - } -} - -void -LayoutView::do_cm_paste (bool interactive) -{ - if (! db::Clipboard::instance ().empty ()) { - cancel (); - clear_selection (); - if (interactive) { - paste_interactive (); - } else { - paste (); - } - } -} - -void -LayoutView::cm_new_cell () -{ - static double s_new_cell_window_size = 2.0; - static std::string s_new_cell_cell_name; - - NewCellPropertiesDialog cell_prop_dia (this); - if (cell_prop_dia.exec_dialog (& cellview (active_cellview_index ())->layout (), s_new_cell_cell_name, s_new_cell_window_size)) { - - db::cell_index_type new_ci = new_cell (active_cellview_index (), s_new_cell_cell_name.c_str ()); - select_cell (new_ci, active_cellview_index ()); - - db::DBox zb = db::DBox (-0.5 * s_new_cell_window_size, -0.5 * s_new_cell_window_size, 0.5 * s_new_cell_window_size, 0.5 * s_new_cell_window_size); - if (get_max_hier_levels () < 1 || get_min_hier_levels () > 0) { - zoom_box_and_set_hier_levels (zb, std::make_pair (0, 1)); - } else { - zoom_box (zb); - } - - } -} - -// TODO: this constant is defined in MainWindow.cc too ... -const int max_dirty_files = 15; - -void -LayoutView::cm_reload () -{ - std::vector selected; - - if (cellviews () > 1) { - - SelectCellViewForm form (0, this, tl::to_string (QObject::tr ("Select Layouts To Reload"))); - form.select_all (); - - if (form.exec () == QDialog::Accepted) { - selected = form.selected_cellviews (); - } - - } else if (cellviews () > 0) { - selected.push_back (0); - } - - if (selected.size () > 0) { - - int dirty_layouts = 0; - std::string dirty_files; - - for (std::vector ::const_iterator i = selected.begin (); i != selected.end (); ++i) { - - const lay::CellView &cv = cellview (*i); - - if (cv->layout ().is_editable () && cv->is_dirty ()) { - ++dirty_layouts; - if (dirty_layouts == max_dirty_files) { - dirty_files += "\n..."; - } else if (dirty_layouts < max_dirty_files) { - if (! dirty_files.empty ()) { - dirty_files += "\n"; - } - dirty_files += cv->name (); - } - } - - } - - bool can_reload = true; - if (dirty_layouts != 0) { - - QMessageBox mbox (this); - mbox.setText (tl::to_qstring (tl::to_string (QObject::tr ("The following layouts need saving:\n\n")) + dirty_files + "\n\nPress 'Reload Without Saving' to reload anyhow and discard changes.")); - mbox.setWindowTitle (QObject::tr ("Save Needed")); - mbox.setIcon (QMessageBox::Warning); - QAbstractButton *yes_button = mbox.addButton (QObject::tr ("Reload Without Saving"), QMessageBox::YesRole); - mbox.addButton (QMessageBox::Cancel); - - mbox.exec (); - - can_reload = (mbox.clickedButton() == yes_button); - - } - - if (can_reload) { - - // Actually reload - for (std::vector ::const_iterator i = selected.begin (); i != selected.end (); ++i) { - reload_layout (*i); - } - - } - - } -} - std::vector LayoutView::menu_symbols () { @@ -6640,309 +5674,9 @@ LayoutView::menu_activated (const std::string &symbol) } } - if (symbol == "cm_show_properties") { - show_properties (this); - } else if (symbol == "cm_delete") { - - del (); - // because a "delete" might involve objects currently edited, we cancel the edit after we have deleted the object - cancel (); - clear_selection (); - - } else if (symbol == "cm_unselect_all") { - select (db::DBox (), lay::Editable::Reset); - } else if (symbol == "cm_select_all") { - select (full_box (), lay::Editable::Replace); - } else if (symbol == "cm_lv_paste") { - cm_layer_paste (); - } else if (symbol == "cm_lv_cut") { - cm_layer_cut (); - } else if (symbol == "cm_lv_copy") { - cm_layer_copy (); - } else if (symbol == "cm_cell_paste") { - cm_cell_paste (); - } else if (symbol == "cm_cell_cut") { - cm_cell_cut (); - } else if (symbol == "cm_cell_copy") { - cm_cell_copy (); - } else if (symbol == "cm_duplicate") { - do_cm_duplicate (false); - } else if (symbol == "cm_duplicate_interactive") { - do_cm_duplicate (true); - } else if (symbol == "cm_copy") { - - copy (); - clear_selection (); - - } else if (symbol == "cm_paste") { - do_cm_paste (false); - } else if (symbol == "cm_paste_interactive") { - do_cm_paste (true); - } else if (symbol == "cm_cut") { - - cut (); - cancel (); // see del() for reason why cancel is after cut - clear_selection (); - - } else if (symbol == "cm_zoom_fit_sel") { - zoom_fit_sel (); - } else if (symbol == "cm_zoom_fit") { - zoom_fit (); - } else if (symbol == "cm_pan_left") { - pan_left (); - } else if (symbol == "cm_pan_right") { - pan_right (); - } else if (symbol == "cm_pan_up") { - pan_up (); - } else if (symbol == "cm_pan_down") { - pan_down (); - } else if (symbol == "cm_zoom_in") { - zoom_in (); - } else if (symbol == "cm_zoom_out") { - zoom_out (); - } else if (symbol == "cm_select_current_cell") { - - if (active_cellview_index () >= 0) { - lay::LayoutView::cell_path_type path; - int cvi = active_cellview_index (); - current_cell_path (path); - select_cell_fit (path, cvi); - } - - } else if (symbol == "cm_open_current_cell") { - - if (active_cellview_index () >= 0) { - cm_open_current_cell (); - } - - } else if (symbol == "cm_select_cell") { - - if (active_cellview_index () >= 0) { - - CellSelectionForm form (0, this, "cell_selection_form"); - - if (form.exec () == QDialog::Accepted && - form.selected_cellview_index () >= 0) { - select_cell (form.selected_cellview ().combined_unspecific_path (), form.selected_cellview_index ()); - set_current_cell_path (form.selected_cellview_index (), form.selected_cellview ().combined_unspecific_path ()); - zoom_fit (); - } - - } - - } else if (symbol == "cm_new_cell") { - cm_new_cell (); - } else if (symbol == "cm_adjust_origin") { - if (active_cellview_index () >= 0) { - cm_align_cell_origin (); - } - } else if (symbol == "cm_cell_convert_to_static") { - if (active_cellview_index () >= 0) { - cm_cell_convert_to_static (); - } - } else if (symbol == "cm_lay_convert_to_static") { - if (active_cellview_index () >= 0) { - cm_lay_convert_to_static (); - } - } else if (symbol == "cm_lay_move") { - if (active_cellview_index () >= 0) { - cm_lay_move (); - } - } else if (symbol == "cm_lay_scale") { - if (active_cellview_index () >= 0) { - cm_lay_scale (); - } - } else if (symbol == "cm_lay_free_rot") { - if (active_cellview_index () >= 0) { - cm_lay_free_rot (); - } - } else if (symbol == "cm_lay_rot_ccw") { - if (active_cellview_index () >= 0) { - cm_lay_rot_ccw (); - } - } else if (symbol == "cm_lay_rot_cw") { - if (active_cellview_index () >= 0) { - cm_lay_rot_cw (); - } - } else if (symbol == "cm_lay_flip_y") { - if (active_cellview_index () >= 0) { - cm_lay_flip_y (); - } - } else if (symbol == "cm_lay_flip_x") { - if (active_cellview_index () >= 0) { - cm_lay_flip_x (); - } - } else if (symbol == "cm_sel_move") { - if (active_cellview_index () >= 0) { - cm_sel_move (); - } - } else if (symbol == "cm_sel_move_to") { - if (active_cellview_index () >= 0) { - cm_sel_move_to (); - } - } else if (symbol == "cm_sel_move_interactive") { - if (active_cellview_index () >= 0) { - cm_sel_move_interactive (); - } - } else if (symbol == "cm_sel_scale") { - if (active_cellview_index () >= 0) { - cm_sel_scale (); - } - } else if (symbol == "cm_sel_free_rot") { - if (active_cellview_index () >= 0) { - cm_sel_free_rot (); - } - } else if (symbol == "cm_sel_rot_ccw") { - if (active_cellview_index () >= 0) { - cm_sel_rot_ccw (); - } - } else if (symbol == "cm_sel_rot_cw") { - if (active_cellview_index () >= 0) { - cm_sel_rot_cw (); - } - } else if (symbol == "cm_sel_flip_y") { - if (active_cellview_index () >= 0) { - cm_sel_flip_y (); - } - } else if (symbol == "cm_sel_flip_x") { - if (active_cellview_index () >= 0) { - cm_sel_flip_x (); - } - } else if (symbol == "cm_edit_layer") { - if (active_cellview_index () >= 0) { - cm_edit_layer (); - } - } else if (symbol == "cm_delete_layer") { - if (active_cellview_index () >= 0) { - cm_delete_layer (); - } - } else if (symbol == "cm_clear_layer") { - if (active_cellview_index () >= 0) { - cm_clear_layer (); - } - } else if (symbol == "cm_copy_layer") { - if (active_cellview_index () >= 0) { - cm_copy_layer (); - } - } else if (symbol == "cm_new_layer") { - if (active_cellview_index () >= 0) { - cm_new_layer (); - } - } else if (symbol == "cm_layout_props") { - LayoutPropertiesForm lp_form (this, this, "layout_props_form"); - lp_form.exec (); - } else if (symbol == "cm_layout_stats") { - LayoutStatisticsForm lp_form (this, this, "layout_props_form"); - lp_form.exec (); - } else if (symbol == "cm_reload") { - cm_reload (); - } else if (symbol == "cm_inc_max_hier") { - int new_to = get_max_hier_levels () + 1; - set_hier_levels (std::make_pair (get_min_hier_levels (), new_to)); - } else if (symbol == "cm_dec_max_hier") { - int new_to = get_max_hier_levels () > 0 ? get_max_hier_levels () - 1 : 0; - set_hier_levels (std::make_pair (std::min (get_min_hier_levels (), new_to), new_to)); - } else if (symbol == "cm_max_hier") { - max_hier (); - } else if (symbol == "cm_max_hier_0") { - set_hier_levels (std::make_pair (std::min (get_min_hier_levels (), 0), 0)); - } else if (symbol == "cm_max_hier_1") { - set_hier_levels (std::make_pair (std::min (get_min_hier_levels (), 0), 1)); - } else if (symbol == "cm_prev_display_state") { - if (has_prev_display_state ()) { - prev_display_state (); - } - } else if (symbol == "cm_next_display_state") { - if (has_next_display_state ()) { - next_display_state (); - } - } else if (symbol == "cm_redraw") { - redraw (); - } else if (symbol == "cm_cell_delete") { - cm_cell_delete (); - } else if (symbol == "cm_cell_replace") { - cm_cell_replace (); - } else if (symbol == "cm_cell_rename") { - cm_cell_rename (); - } else if (symbol == "cm_cell_flatten") { - cm_cell_flatten (); - } else if (symbol == "cm_cell_select") { - cm_cell_select (); - } else if (symbol == "cm_cell_hide") { - cm_cell_hide (); - } else if (symbol == "cm_cell_show") { - cm_cell_show (); - } else if (symbol == "cm_cell_show_all") { - cm_cell_show_all (); - } else if (symbol == "cm_cell_user_properties") { - if (active_cellview_index () >= 0) { - cm_cell_user_properties (); - } - } else if (symbol == "cm_lv_select_all") { - cm_select_all (); - } else if (symbol == "cm_lv_new_tab") { - cm_new_tab (); - } else if (symbol == "cm_lv_rename_tab") { - cm_rename_tab (); - } else if (symbol == "cm_lv_make_invalid") { - cm_make_invalid (); - } else if (symbol == "cm_lv_remove_tab") { - cm_remove_tab (); - } else if (symbol == "cm_lv_make_valid") { - cm_make_valid (); - } else if (symbol == "cm_lv_hide_all") { - cm_hide_all (); - } else if (symbol == "cm_lv_hide") { - cm_hide (); - } else if (symbol == "cm_lv_show_only") { - cm_show_only (); - } else if (symbol == "cm_lv_show_all") { - cm_show_all (); - } else if (symbol == "cm_lv_show") { - cm_show (); - } else if (symbol == "cm_lv_rename") { - cm_rename (); - } else if (symbol == "cm_lv_delete") { - cm_delete (); - } else if (symbol == "cm_lv_insert") { - cm_insert (); - } else if (symbol == "cm_lv_group") { - cm_group (); - } else if (symbol == "cm_lv_ungroup") { - cm_ungroup (); - } else if (symbol == "cm_lv_source") { - cm_source (); - } else if (symbol == "cm_lv_sort_by_name") { - cm_sort_by_name (); - } else if (symbol == "cm_lv_sort_by_ild") { - cm_sort_by_ild (); - } else if (symbol == "cm_lv_sort_by_idl") { - cm_sort_by_idl (); - } else if (symbol == "cm_lv_sort_by_ldi") { - cm_sort_by_ldi (); - } else if (symbol == "cm_lv_sort_by_dli") { - cm_sort_by_dli (); - } else if (symbol == "cm_lv_regroup_by_index") { - cm_regroup_by_index (); - } else if (symbol == "cm_lv_regroup_by_datatype") { - cm_regroup_by_datatype (); - } else if (symbol == "cm_lv_regroup_by_layer") { - cm_regroup_by_layer (); - } else if (symbol == "cm_lv_regroup_flatten") { - cm_regroup_flatten (); - } else if (symbol == "cm_lv_expand_all") { - cm_expand_all (); - } else if (symbol == "cm_lv_add_missing") { - cm_add_missing (); - } else if (symbol == "cm_lv_remove_unused") { - cm_remove_unused (); - } else { - - // distribute the menu on the plugins - one should take it. - for (std::vector::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) { - (*p)->menu_activated (symbol); - } - + // distribute the menu item call on the plugins - one should take it. + for (std::vector::const_iterator p = plugins ().begin (); p != plugins ().end (); ++p) { + (*p)->menu_activated (symbol); } } @@ -7132,229 +5866,6 @@ LayoutView::new_cell (int cv_index, const std::string &cell_name) return new_ci; } -void -LayoutView::do_transform (const db::DCplxTrans &tr) -{ - // end move operations, cancel edit operations - cancel_edits (); - lay::Editables::transform (tr); -} - -void -LayoutView::transform_layout (const db::DCplxTrans &tr_mic) -{ - // end move operations, cancel edit operations - cancel_edits (); - clear_selection (); - - int cv_index = active_cellview_index (); - if (cv_index >= 0) { - - db::Layout &layout = cellview (cv_index)->layout (); - - db::ICplxTrans tr (db::DCplxTrans (1.0 / layout.dbu ()) * tr_mic * db::DCplxTrans (layout.dbu ())); - - bool has_proxy = false; - for (db::Layout::const_iterator c = layout.begin (); ! has_proxy && c != layout.end (); ++c) { - has_proxy = c->is_proxy (); - } - - if (has_proxy && - QMessageBox::question (this, - QObject::tr ("Transforming PCells Or Library Cells"), - QObject::tr ("The layout contains PCells or library cells or both.\n" - "Any changes to such cells may be lost when their layout is refreshed later.\n" - "Consider using 'Convert all cells to static' before transforming the layout.\n" - "\n" - "Would you like to continue?\n" - "Choose 'Yes' to continue anyway. Choose 'No' to cancel."), - QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { - return; - } - - transaction (tl::to_string (QObject::tr ("Transform layout"))); - layout.transform (tr); - commit (); - - } -} - -void -LayoutView::cm_lay_flip_x () -{ - transform_layout (db::DCplxTrans (db::FTrans::m90)); -} - -void -LayoutView::cm_lay_flip_y () -{ - transform_layout (db::DCplxTrans (db::FTrans::m0)); -} - -void -LayoutView::cm_lay_rot_ccw () -{ - db::DCplxTrans tr (db::DFTrans::r90); - transform_layout (db::DCplxTrans (db::FTrans::r90)); -} - -void -LayoutView::cm_lay_rot_cw () -{ - transform_layout (db::DCplxTrans (db::FTrans::r270)); -} - -void -LayoutView::cm_lay_free_rot () -{ - bool ok = false; - QString s = QInputDialog::getText (QApplication::activeWindow (), - QObject::tr ("Free rotation"), - QObject::tr ("Rotation angle in degree (counterclockwise)"), - QLineEdit::Normal, QString::fromUtf8 ("0.0"), - &ok); - - if (ok) { - - double angle = 0.0; - tl::from_string (tl::to_string (s), angle); - - transform_layout (db::DCplxTrans (1.0, angle, false, db::DVector ())); - - } -} - -void -LayoutView::cm_lay_scale () -{ - bool ok = false; - QString s = QInputDialog::getText (QApplication::activeWindow (), - QObject::tr ("Scaling"), - QObject::tr ("Scaling factor"), - QLineEdit::Normal, QString::fromUtf8 ("1.0"), - &ok); - - if (ok) { - - double scale = 0.0; - tl::from_string (tl::to_string (s), scale); - - transform_layout (db::DCplxTrans (scale)); - - } -} - -void -LayoutView::cm_lay_move () -{ - lay::MoveOptionsDialog options (this); - if (options.exec_dialog (m_move_dist)) { - transform_layout (db::DCplxTrans (m_move_dist)); - } -} - -void -LayoutView::cm_sel_flip_x () -{ - db::DCplxTrans tr (db::DFTrans::m90); - db::DBox sel_bbox (lay::Editables::selection_bbox ()); - if (! sel_bbox.empty ()) { - tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); - } - do_transform (tr); -} - -void -LayoutView::cm_sel_flip_y () -{ - db::DCplxTrans tr (db::DFTrans::m0); - db::DBox sel_bbox (lay::Editables::selection_bbox ()); - if (! sel_bbox.empty ()) { - tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); - } - do_transform (tr); -} - -void -LayoutView::cm_sel_rot_ccw () -{ - db::DCplxTrans tr (db::DFTrans::r90); - db::DBox sel_bbox (lay::Editables::selection_bbox ()); - if (! sel_bbox.empty ()) { - tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); - } - do_transform (tr); -} - -void -LayoutView::cm_sel_rot_cw () -{ - db::DCplxTrans tr (db::DFTrans::r270); - db::DBox sel_bbox (lay::Editables::selection_bbox ()); - if (! sel_bbox.empty ()) { - tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); - } - do_transform (tr); -} - -void -LayoutView::cm_sel_free_rot () -{ - bool ok = false; - QString s = QInputDialog::getText (QApplication::activeWindow (), - QObject::tr ("Free rotation"), - QObject::tr ("Rotation angle in degree (counterclockwise)"), - QLineEdit::Normal, QString::fromUtf8 ("0.0"), - &ok); - - if (ok) { - - double angle = 0.0; - tl::from_string (tl::to_string (s), angle); - - db::DCplxTrans tr = db::DCplxTrans (1.0, angle, false, db::DVector ()); - db::DBox sel_bbox (lay::Editables::selection_bbox ()); - if (! sel_bbox.empty ()) { - tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); - } - do_transform (tr); - - } -} - -void -LayoutView::cm_sel_scale () -{ - bool ok = false; - QString s = QInputDialog::getText (QApplication::activeWindow (), - QObject::tr ("Scaling"), - QObject::tr ("Scaling factor"), - QLineEdit::Normal, QString::fromUtf8 ("1.0"), - &ok); - - if (ok) { - - double scale = 0.0; - tl::from_string (tl::to_string (s), scale); - - db::DCplxTrans tr = db::DCplxTrans (scale); - db::DBox sel_bbox (lay::Editables::selection_bbox ()); - if (! sel_bbox.empty ()) { - tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); - } - do_transform (tr); - - } -} - -void -LayoutView::cm_sel_move_interactive () -{ - if (mp_move_service->begin_move ()) { - switch_mode (-1); // move mode - } -} - void LayoutView::switch_mode (int m) { @@ -7364,400 +5875,6 @@ LayoutView::switch_mode (int m) } } -void -LayoutView::cm_sel_move_to () -{ - db::DBox sel_bbox (lay::Editables::selection_bbox ()); - if (sel_bbox.empty ()) { - throw tl::Exception (tl::to_string (QObject::tr ("Nothing selected to move"))); - } - - double x = sel_bbox.left () + (sel_bbox.width () * (1 + m_move_to_origin_mode_x) * 0.5); - double y = sel_bbox.bottom () + (sel_bbox.height () * (1 + m_move_to_origin_mode_y) * 0.5); - db::DPoint move_target (x, y); - - lay::MoveToOptionsDialog options (this); - if (options.exec_dialog (m_move_to_origin_mode_x, m_move_to_origin_mode_y, move_target)) { - - x = sel_bbox.left () + (sel_bbox.width () * (1 + m_move_to_origin_mode_x) * 0.5); - y = sel_bbox.bottom () + (sel_bbox.height () * (1 + m_move_to_origin_mode_y) * 0.5); - - do_transform (db::DCplxTrans (move_target - db::DPoint (x, y))); - - } -} - -void -LayoutView::cm_sel_move () -{ - lay::MoveOptionsDialog options (this); - if (options.exec_dialog (m_move_dist)) { - do_transform (db::DCplxTrans (m_move_dist)); - } -} - -void -LayoutView::cm_copy_layer () -{ - struct { int *cv; int *layer; } specs [] = { - { &m_copy_cva, &m_copy_layera }, - { &m_copy_cvr, &m_copy_layerr } - }; - - for (unsigned int i = 0; i < sizeof (specs) / sizeof (specs[0]); ++i) { - - int &cv = *(specs[i].cv); - int &layer = *(specs[i].layer); - - if (cv >= int (m_cellviews.size ())) { - cv = -1; - } - - int index = active_cellview_index (); - if (cv < 0) { - cv = index; - } - - if (cv < 0 || ! (*cellview_iter (cv))->layout ().is_valid_layer ((unsigned int) layer)) { - layer = -1; - } - - } - - lay::DuplicateLayerDialog dialog (this); - if (dialog.exec_dialog (this, m_copy_cva, m_copy_layera, m_copy_cvr, m_copy_layerr, m_duplicate_hier_mode, m_clear_before)) { - - bool supports_undo = true; - - if (manager () && manager ()->is_enabled ()) { - - lay::TipDialog td (QApplication::activeWindow (), - tl::to_string (QObject::tr ("Undo buffering for the following operation can be memory and time consuming.\nChoose \"Yes\" to use undo buffering or \"No\" for no undo buffering. Warning: in the latter case, the undo history will be lost.\n\nChoose undo buffering?")), - "copy-layer-undo-buffering", - lay::TipDialog::yesnocancel_buttons); - - lay::TipDialog::button_type button = lay::TipDialog::null_button; - td.exec_dialog (button); - if (button == lay::TipDialog::cancel_button) { - return; - } - - supports_undo = (button == lay::TipDialog::yes_button); - - } else { - supports_undo = false; - } - - cancel (); - - if (manager ()) { - if (! supports_undo) { - manager ()->clear (); - } else { - manager ()->transaction (tl::to_string (QObject::tr ("Duplicate layer"))); - } - } - - try { - - bool same_layout = (&cellview (m_copy_cvr)->layout () == &cellview (m_copy_cva)->layout ()); - if (same_layout && m_copy_layera == m_copy_layerr) { - throw tl::Exception (tl::to_string (QObject::tr ("Source and target layer must not be identical for duplicate operation"))); - } - - if (m_duplicate_hier_mode == 0) { - - // clear the result layer for all called cells in flat mode - if (m_clear_before) { - std::set called_cells; - called_cells.insert (cellview (m_copy_cvr).cell_index ()); - cellview (m_copy_cvr).cell ()->collect_called_cells (called_cells); - for (std::set::const_iterator c = called_cells.begin (); c != called_cells.end (); ++c) { - cellview (m_copy_cvr)->layout ().cell (*c).shapes (m_copy_layerr).clear (); - } - } - - db::Cell &target_cell = *cellview (m_copy_cvr).cell (); - - if (! same_layout) { - - // flat mode (different layouts) - db::PropertyMapper pm (cellview (m_copy_cvr)->layout (), cellview (m_copy_cva)->layout ()); - for (db::RecursiveShapeIterator si (cellview (m_copy_cva)->layout (), *cellview (m_copy_cva).cell (), m_copy_layera); ! si.at_end (); ++si) { - target_cell.shapes (m_copy_layerr).insert (*si, si.trans (), pm); - } - - } else { - - // flat mode (same layouts) - tl::ident_map pm1; - db::Shapes &res = target_cell.shapes (m_copy_layerr); - - db::Layout &layout = cellview (m_copy_cvr)->layout (); - try { - - // using update/start_layout and end_changes improves the performance since changing the - // shapes collection will invalidate the layout and cause updates inside the RecursiveShapeIerator - layout.update (); - layout.start_changes (); - for (db::RecursiveShapeIterator si (cellview (m_copy_cva)->layout (), *cellview (m_copy_cva).cell (), m_copy_layera); ! si.at_end (); ++si) { - res.insert (*si, si.trans (), pm1); - } - layout.end_changes (); - - } catch (...) { - layout.end_changes (); - throw; - } - - } - - } else if (m_duplicate_hier_mode == 1) { - - db::Cell &cell = *cellview (m_copy_cva).cell (); - db::Cell &target_cell = *cellview (m_copy_cvr).cell (); - - if (m_clear_before) { - target_cell.clear (m_copy_layerr); - } - - if (m_copy_cvr == m_copy_cva) { - - // current cell only mode: identical cell - cell.copy (m_copy_layera, m_copy_layerr); - - } else if (! same_layout) { - - // current cell only mode (different layouts) - db::PropertyMapper pm (cellview (m_copy_cvr)->layout (), cellview (m_copy_cva)->layout ()); - for (db::Shapes::shape_iterator si = cellview (m_copy_cva).cell ()->shapes (m_copy_layera).begin (db::ShapeIterator::All); ! si.at_end (); ++si) { - target_cell.shapes (m_copy_layerr).insert (*si, pm); - } - - } else { - - // current cell only mode (same layouts, but different cells) - for (db::Shapes::shape_iterator si = cellview (m_copy_cva).cell ()->shapes (m_copy_layera).begin (db::ShapeIterator::All); ! si.at_end (); ++si) { - target_cell.shapes (m_copy_layerr).insert (*si); - } - - } - - } else if (m_duplicate_hier_mode == 2) { - - // subcells cell by cell - source and target layout must be identical - std::set called_cells; - cellview (m_copy_cva).cell ()->collect_called_cells (called_cells); - called_cells.insert (cellview (m_copy_cva).cell_index ()); - - db::Layout &layout = cellview (m_copy_cva)->layout (); - for (std::set::const_iterator c = called_cells.begin (); c != called_cells.end (); ++c) { - db::Cell &cell = layout.cell (*c); - if (m_clear_before) { - cell.clear (m_copy_layerr); - } - cell.copy (m_copy_layera, m_copy_layerr); - } - - } - - if (manager () && supports_undo) { - manager ()->commit (); - } - - } catch (...) { - if (manager () && supports_undo) { - manager ()->commit (); - } - throw; - } - - } -} - -void -LayoutView::cm_new_layer () -{ - int index = active_cellview_index (); - - if (index >= 0 && int (m_cellviews.size ()) > index) { - - const lay::CellView &cv = cellview (index); - - lay::NewLayerPropertiesDialog prop_dia (this); - if (prop_dia.exec_dialog (cv, m_new_layer_props)) { - - for (unsigned int l = 0; l < cv->layout ().layers (); ++l) { - if (cv->layout ().is_valid_layer (l) && cv->layout ().get_properties (l).log_equal (m_new_layer_props)) { - throw tl::Exception (tl::to_string (QObject::tr ("A layer with that signature already exists: ")) + m_new_layer_props.to_string ()); - } - } - - transaction (tl::to_string (QObject::tr ("New layer"))); - - unsigned int l = cv->layout ().insert_layer (m_new_layer_props); - std::vector nl; - nl.push_back (l); - add_new_layers (nl, index); - update_content (); - - commit (); - - } - - } -} - -void -LayoutView::cm_edit_layer () -{ - lay::LayerPropertiesConstIterator sel = current_layer (); - if (sel.is_null ()) { - throw tl::Exception (tl::to_string (QObject::tr ("No layer selected for editing it's properties"))); - } - - int index = sel->cellview_index (); - if (sel->has_children () || index < 0 || int (m_cellviews.size ()) <= index || sel->layer_index () < 0) { - throw tl::Exception (tl::to_string (QObject::tr ("No valid layer selected for editing it's properties"))); - } - - const lay::CellView &cv = cellview (index); - - db::LayerProperties layer_props = cv->layout ().get_properties ((unsigned int) sel->layer_index ()); - - lay::NewLayerPropertiesDialog prop_dia (this); - if (prop_dia.exec_dialog (cv, layer_props)) { - - for (unsigned int l = 0; l < cv->layout ().layers (); ++l) { - if (cv->layout ().is_valid_layer (l) && int (l) != sel->layer_index () && cv->layout ().get_properties (l).log_equal (layer_props)) { - throw tl::Exception (tl::to_string (QObject::tr ("A layer with that signature already exists: ")) + layer_props.to_string ()); - } - } - - transaction (tl::to_string (QObject::tr ("Edit layer"))); - - cv->layout ().set_properties (sel->layer_index (), layer_props); - - lay::LayerProperties lp (*sel); - lay::ParsedLayerSource s = lp.source (false); - s.layer (layer_props.layer); - s.datatype (layer_props.datatype); - if (! layer_props.name.empty ()) { - s.name (layer_props.name); - } else { - s.clear_name (); - } - lp.set_source (s); - set_properties (sel, lp); - - update_content (); - - commit (); - - } -} - -void -LayoutView::cm_delete_layer () -{ - std::vector sel = selected_layers (); - std::sort (sel.begin (), sel.end (), CompareLayerIteratorBottomUp ()); - - // collect valid layers - std::vector valid_sel; - std::set > valid_layers; - for (std::vector::const_iterator si = sel.begin (); si != sel.end (); ++si) { - int cv_index = (*si)->cellview_index (); - const lay::CellView &cv = cellview (cv_index); - if (!(*si)->has_children () && cv_index >= 0 && int (m_cellviews.size ()) > cv_index && (*si)->layer_index () >= 0 && cv.is_valid ()) { - valid_sel.push_back (*si); - valid_layers.insert (std::make_pair (&cv->layout (), (*si)->layer_index ())); - } - } - - if (valid_sel.empty ()) { - throw tl::Exception (tl::to_string (QObject::tr ("No or no valid layer selected for deleting them"))); - } - - cancel_edits (); - clear_selection (); - - transaction (tl::to_string (QObject::tr ("Delete layers"))); - - // Hint: delete_layer must come before the layers are actually deleted in because - // for undo this must be the last thing to do (otherwise the layout is not propertly set up) - - for (std::vector::const_iterator si = valid_sel.begin (); si != valid_sel.end (); ++si) { - lay::LayerPropertiesConstIterator lp = *si; - delete_layer (lp); - } - - for (std::set >::const_iterator li = valid_layers.begin (); li != valid_layers.end(); ++li) { - - unsigned int layer_index = li->second; - db::Layout *layout = li->first; - - for (db::Layout::iterator c = layout->begin (); c != layout->end (); ++c) { - c->shapes (layer_index).clear (); - } - - layout->delete_layer (layer_index); - - } - - update_content (); - - commit (); -} - -void -LayoutView::cm_clear_layer () -{ - std::vector sel = selected_layers (); - if (sel.empty ()) { - throw tl::Exception (tl::to_string (QObject::tr ("No layer selected for clearing"))); - } - - lay::ClearLayerModeDialog mode_dialog (this); - if (mode_dialog.exec_dialog (m_layer_hier_mode)) { - - cancel_edits (); - clear_selection (); - - transaction (tl::to_string (QObject::tr ("Clear layer"))); - - for (std::vector::const_iterator si = sel.begin (); si != sel.end (); ++si) { - - if (! (*si)->has_children () && (*si)->layer_index () >= 0 && cellview ((*si)->cellview_index ()).is_valid ()) { - - int layer_index = (*si)->layer_index (); - const lay::CellView &cv = cellview ((*si)->cellview_index ()); - - if (m_layer_hier_mode == 0) { - cv.cell ()->clear ((unsigned int) layer_index); - } else if (m_layer_hier_mode == 1) { - - cv.cell ()->clear ((unsigned int) layer_index); - - std::set called_cells; - cv.cell ()->collect_called_cells (called_cells); - for (std::set ::const_iterator cc = called_cells.begin (); cc != called_cells.end (); ++cc) { - cv->layout ().cell (*cc).clear ((unsigned int) layer_index); - } - - } else { - cv->layout ().clear_layer ((unsigned int) layer_index); - } - - } - - } - - commit (); - - } -} - template static void make_unique_name (T *object, Iter from, Iter to) { @@ -7954,203 +6071,4 @@ LayoutView::sizeHint () const } } -// ------------------------------------------------------------ -// Declaration of the "plugin" for the menu entries - -class LayoutViewPluginDeclaration - : public lay::PluginDeclaration -{ -public: - virtual void get_menu_entries (std::vector &menu_entries) const - { - std::string at; - - // secret menu entries - at = "@secrets.end"; - menu_entries.push_back (lay::menu_item ("cm_paste_interactive", "paste_interactive:edit", at, tl::to_string (QObject::tr ("Paste Interactive")))); - menu_entries.push_back (lay::menu_item ("cm_duplicate_interactive", "duplicate_interactive:edit", at, tl::to_string (QObject::tr ("Duplicate Interactive")))); - menu_entries.push_back (lay::menu_item ("cm_sel_move_interactive", "sel_move_interactive:edit", at, tl::to_string (QObject::tr ("Move Interactive")))); - - at = "edit_menu.end"; - menu_entries.push_back (lay::menu_item ("cm_undo", "undo:edit", at, tl::to_string (QObject::tr ("Undo(Ctrl+Z)")))); - menu_entries.push_back (lay::menu_item ("cm_redo", "redo:edit", at, tl::to_string (QObject::tr ("Redo(Ctrl+Y)")))); - - menu_entries.push_back (lay::separator ("basic_group", at)); - menu_entries.push_back (lay::submenu ("layout_menu:edit:edit_mode", at, tl::to_string (QObject::tr ("Layout")))); - { - std::string at = "edit_menu.layout_menu.end"; - menu_entries.push_back (lay::menu_item ("cm_lay_flip_x", "lay_flip_x:edit_mode", at, tl::to_string (QObject::tr ("Flip Horizontally")))); - menu_entries.push_back (lay::menu_item ("cm_lay_flip_y", "lay_flip_y:edit_mode", at, tl::to_string (QObject::tr ("Flip Vertically")))); - menu_entries.push_back (lay::menu_item ("cm_lay_rot_cw", "lay_rot_cw:edit_mode", at, tl::to_string (QObject::tr ("Rotate Clockwise")))); - menu_entries.push_back (lay::menu_item ("cm_lay_rot_ccw", "lay_rot_ccw:edit_mode", at, tl::to_string (QObject::tr ("Rotate Counterclockwise")))); - menu_entries.push_back (lay::menu_item ("cm_lay_free_rot", "lay_free_rot:edit_mode", at, tl::to_string (QObject::tr ("Rotation By Angle")))); - menu_entries.push_back (lay::menu_item ("cm_lay_scale", "lay_scale:edit_mode", at, tl::to_string (QObject::tr ("Scale")))); - menu_entries.push_back (lay::menu_item ("cm_lay_move", "lay_move:edit_mode", at, tl::to_string (QObject::tr ("Move By")))); - menu_entries.push_back (lay::separator ("cellop_group", at)); - menu_entries.push_back (lay::menu_item ("cm_lay_convert_to_static", "lay_convert_to_static:edit_mode", at, tl::to_string (QObject::tr ("Convert All Cells To Static")))); - } - - menu_entries.push_back (lay::submenu ("cell_menu:edit:edit_mode", at, tl::to_string (QObject::tr ("Cell")))); - { - std::string at = "edit_menu.cell_menu.end"; - menu_entries.push_back (lay::menu_item ("cm_new_cell", "new_cell:edit:edit_mode", at, tl::to_string (QObject::tr ("New Cell")))); - menu_entries.push_back (lay::menu_item ("cm_cell_delete", "delete_cell:edit:edit_mode", at, tl::to_string (QObject::tr ("Delete Cell")))); - menu_entries.push_back (lay::menu_item ("cm_cell_rename", "rename_cell:edit:edit_mode", at, tl::to_string (QObject::tr ("Rename Cell")))); - menu_entries.push_back (lay::menu_item ("cm_cell_replace", "replace_cell:edit:edit_mode", at, tl::to_string (QObject::tr ("Replace Cell")))); - menu_entries.push_back (lay::menu_item ("cm_cell_flatten", "flatten_cell:edit:edit_mode", at, tl::to_string (QObject::tr ("Flatten Cell")))); - menu_entries.push_back (lay::separator ("ops_group", at)); - menu_entries.push_back (lay::menu_item ("cm_adjust_origin", "adjust_cell_origin:edit:edit_mode", at, tl::to_string (QObject::tr ("Adjust Origin")))); - menu_entries.push_back (lay::menu_item ("cm_cell_convert_to_static", "convert_cell_to_static:edit_mode", at, tl::to_string (QObject::tr ("Convert Cell To Static")))); - menu_entries.push_back (lay::separator ("props_group", at)); - menu_entries.push_back (lay::menu_item ("cm_cell_user_properties", "user_properties", at, tl::to_string (QObject::tr ("User Properties")))); - } - - menu_entries.push_back (lay::submenu ("layer_menu:edit:edit_mode", at, tl::to_string (QObject::tr ("Layer")))); - { - std::string at = "edit_menu.layer_menu.end"; - menu_entries.push_back (lay::menu_item ("cm_new_layer", "new_layer:edit:edit_mode", at, tl::to_string (QObject::tr ("New Layer")))); - menu_entries.push_back (lay::menu_item ("cm_clear_layer", "clear_layer:edit:edit_mode", at, tl::to_string (QObject::tr ("Clear Layer")))); - menu_entries.push_back (lay::menu_item ("cm_delete_layer", "delete_layer:edit:edit_mode", at, tl::to_string (QObject::tr ("Delete Layer")))); - menu_entries.push_back (lay::menu_item ("cm_copy_layer", "copy_layer:edit:edit_mode", at, tl::to_string (QObject::tr ("Copy Layer")))); - menu_entries.push_back (lay::menu_item ("cm_edit_layer", "edit_layer:edit:edit_mode", at, tl::to_string (QObject::tr ("Edit Layer Specification")))); - } - - menu_entries.push_back (lay::submenu ("selection_menu:edit", at, tl::to_string (QObject::tr ("Selection")))); - { - std::string at = "edit_menu.selection_menu.end"; - menu_entries.push_back (lay::menu_item ("cm_sel_flip_x", "sel_flip_x", at, tl::to_string (QObject::tr ("Flip Horizontally")))); - menu_entries.push_back (lay::menu_item ("cm_sel_flip_y", "sel_flip_y", at, tl::to_string (QObject::tr ("Flip Vertically")))); - menu_entries.push_back (lay::menu_item ("cm_sel_rot_cw", "sel_rot_cw", at, tl::to_string (QObject::tr ("Rotate Clockwise")))); - menu_entries.push_back (lay::menu_item ("cm_sel_rot_ccw", "sel_rot_ccw", at, tl::to_string (QObject::tr ("Rotate Counterclockwise")))); - menu_entries.push_back (lay::menu_item ("cm_sel_free_rot", "sel_free_rot", at, tl::to_string (QObject::tr ("Rotation By Angle")))); - menu_entries.push_back (lay::menu_item ("cm_sel_scale", "sel_scale", at, tl::to_string (QObject::tr ("Scale")))); - menu_entries.push_back (lay::menu_item ("cm_sel_move", "sel_move", at, tl::to_string (QObject::tr ("Move By")))); - menu_entries.push_back (lay::menu_item ("cm_sel_move_to", "sel_move_to", at, tl::to_string (QObject::tr ("Move To")))); - } - - menu_entries.push_back (lay::separator ("utils_group", at)); - menu_entries.push_back (lay::submenu ("utils_menu:edit:edit_mode", at, tl::to_string (QObject::tr ("Utilities")))); - - menu_entries.push_back (lay::separator ("misc_group", at)); - menu_entries.push_back (lay::menu_item ("cm_delete", "delete:edit", at, tl::to_string (QObject::tr ("Delete(Del)")))); - menu_entries.push_back (lay::menu_item ("cm_show_properties", "show_properties:edit", at, tl::to_string (QObject::tr ("Properties(Q)")))); - - menu_entries.push_back (lay::separator ("cpc_group", at)); - menu_entries.push_back (lay::menu_item ("cm_copy", "copy:edit", at, tl::to_string (QObject::tr ("Copy(Ctrl+C)")))); - menu_entries.push_back (lay::menu_item ("cm_cut", "cut:edit", at, tl::to_string (QObject::tr ("Cut(Ctrl+X)")))); - menu_entries.push_back (lay::menu_item ("cm_paste", "paste:edit", at, tl::to_string (QObject::tr ("Paste(Ctrl+V)")))); - menu_entries.push_back (lay::menu_item ("cm_duplicate", "duplicate:edit", at, tl::to_string (QObject::tr ("Duplicate(Ctrl+B)")))); - - menu_entries.push_back (lay::separator ("modes_group", at)); - menu_entries.push_back (lay::submenu ("mode_menu", at, tl::to_string (QObject::tr ("Mode")))); - - menu_entries.push_back (lay::submenu ("select_menu", at, tl::to_string (QObject::tr ("Select")))); - { - std::string at = "edit_menu.select_menu.end"; - menu_entries.push_back (lay::menu_item ("cm_select_all", "select_all", at, tl::to_string (QObject::tr ("Select All")))); - menu_entries.push_back (lay::menu_item ("cm_unselect_all", "unselect_all", at, tl::to_string (QObject::tr ("Unselect All")))); - menu_entries.push_back (lay::separator ("edit_select_basic_group", at)); - menu_entries.push_back (lay::menu_item ("lv:enable_all", "enable_all", at, tl::to_string (QObject::tr ("Enable All")))); - menu_entries.push_back (lay::menu_item ("lv:disable_all", "disable_all", at, tl::to_string (QObject::tr ("Disable All")))); - menu_entries.push_back (lay::separator ("edit_select_individual_group", at)); - }; - - menu_entries.push_back (lay::separator ("cancel_group", at)); - menu_entries.push_back (lay::menu_item ("cm_cancel", "cancel", at, tl::to_string (QObject::tr ("Cancel(Esc)")))); - - at = "bookmark_menu.end"; - menu_entries.push_back (lay::submenu ("goto_bookmark_menu", at, tl::to_string (QObject::tr ("Goto Bookmark")))); - menu_entries.push_back (lay::menu_item ("cm_bookmark_view", "bookmark_view", at, tl::to_string (QObject::tr ("Bookmark This View")))); - - menu_entries.push_back (lay::separator ("bookmark_mgm_group", at)); - menu_entries.push_back (lay::menu_item ("cm_manage_bookmarks", "manage_bookmarks", at, tl::to_string (QObject::tr ("Manage Bookmarks")))); - menu_entries.push_back (lay::menu_item ("cm_load_bookmarks", "load_bookmarks", at, tl::to_string (QObject::tr ("Load Bookmarks")))); - menu_entries.push_back (lay::menu_item ("cm_save_bookmarks", "save_bookmarks", at, tl::to_string (QObject::tr ("Save Bookmarks")))); - - at = "zoom_menu.end"; - menu_entries.push_back (lay::submenu ("global_trans", at, tl::to_string (QObject::tr ("Global Transformation")))); - { - std::string at = "zoom_menu.global_trans.end"; - menu_entries.push_back (lay::config_menu_item ("r0", at, tl::to_string (QObject::tr ("\\(r0\\)<:/r0.png>")), cfg_global_trans, "?r0 *1 0,0")); - menu_entries.push_back (lay::config_menu_item ("r90", at, tl::to_string (QObject::tr ("\\(r90\\)<:/r90.png>")), cfg_global_trans, "?r90 *1 0,0")); - menu_entries.push_back (lay::config_menu_item ("r180", at, tl::to_string (QObject::tr ("\\(r180\\)<:/r180.png>")), cfg_global_trans, "?r180 *1 0,0")); - menu_entries.push_back (lay::config_menu_item ("r270", at, tl::to_string (QObject::tr ("\\(r270\\)<:/r270.png>")), cfg_global_trans, "?r270 *1 0,0")); - menu_entries.push_back (lay::config_menu_item ("m0", at, tl::to_string (QObject::tr ("\\(m0\\)<:/m0.png>")), cfg_global_trans, "?m0 *1 0,0")); - menu_entries.push_back (lay::config_menu_item ("m45", at, tl::to_string (QObject::tr ("\\(m45\\)<:/m45.png>")), cfg_global_trans, "?m45 *1 0,0")); - menu_entries.push_back (lay::config_menu_item ("m90", at, tl::to_string (QObject::tr ("\\(m90\\)<:/m90.png>")), cfg_global_trans, "?m90 *1 0,0")); - menu_entries.push_back (lay::config_menu_item ("m135", at, tl::to_string (QObject::tr ("\\(m135\\)<:/m135.png>")), cfg_global_trans, "?m135 *1 0,0")); - } - - menu_entries.push_back (lay::separator ("hier_group", at)); - menu_entries.push_back (lay::menu_item ("cm_max_hier", "max_hier", at, tl::to_string (QObject::tr ("Full Hierarchy(*)")))); - menu_entries.push_back (lay::menu_item ("cm_max_hier_0", "max_hier_0", at, tl::to_string (QObject::tr ("Box Only(0)")))); - menu_entries.push_back (lay::menu_item ("cm_max_hier_1", "max_hier_1", at, tl::to_string (QObject::tr ("Top Level Only(1)")))); - menu_entries.push_back (lay::menu_item ("cm_inc_max_hier", "inc_max_hier", at, tl::to_string (QObject::tr ("Increment Hierarchy(+)")))); - menu_entries.push_back (lay::menu_item ("cm_dec_max_hier", "dec_max_hier", at, tl::to_string (QObject::tr ("Decrement Hierarchy(-)")))); - - menu_entries.push_back (lay::separator ("zoom_group", at)); - menu_entries.push_back (lay::menu_item ("cm_zoom_fit", "zoom_fit", at, tl::to_string (QObject::tr ("Zoom Fit(F2)")))); - menu_entries.push_back (lay::menu_item ("cm_zoom_fit_sel", "zoom_fit_sel", at, tl::to_string (QObject::tr ("Zoom Fit Selection(Shift+F2)")))); - menu_entries.push_back (lay::menu_item ("cm_zoom_in", "zoom_in", at, tl::to_string (QObject::tr ("Zoom In(Return)")))); - menu_entries.push_back (lay::menu_item ("cm_zoom_out", "zoom_out", at, tl::to_string (QObject::tr ("Zoom Out(Shift+Return)")))); - /* disabled because that interferes with the use of the arrow keys for moving the selection - MenuLayoutEntry::separator ("pan_group"); - menu_entries.push_back (lay::menu_item ("cm_pan_up", "pan_up", at, tl::to_string (QObject::tr ("Pan Up(Up)")))); - menu_entries.push_back (lay::menu_item ("cm_pan_down", "pan_down", at, tl::to_string (QObject::tr ("Pan Down(Down)")))); - menu_entries.push_back (lay::menu_item ("cm_pan_left", "pan_left", at, tl::to_string (QObject::tr ("Pan Left(Left)")))); - menu_entries.push_back (lay::menu_item ("cm_pan_right", "pan_right", at, tl::to_string (QObject::tr ("Pan Right(Right)")))); - */ - - menu_entries.push_back (lay::separator ("redraw_group", at)); - menu_entries.push_back (lay::menu_item ("cm_redraw", "redraw", at, tl::to_string (QObject::tr ("Redraw")))); - menu_entries.push_back (lay::separator ("state_group", at)); - menu_entries.push_back (lay::menu_item_copy ("cm_prev_display_state", "prev_display_state", at, "@toolbar.prev_display_state")); - menu_entries.push_back (lay::menu_item_copy ("cm_next_display_state", "next_display_state", at, "@toolbar.next_display_state")); - - menu_entries.push_back (lay::separator ("select_group", at)); - menu_entries.push_back (lay::menu_item ("cm_select_cell", "select_cell:edit", at, tl::to_string (QObject::tr ("Select Cell")))); - menu_entries.push_back (lay::menu_item ("cm_select_current_cell", "select_current_cell", at, tl::to_string (QObject::tr ("Show As New Top(Ctrl+S)")))); - menu_entries.push_back (lay::menu_item ("cm_goto_position", "goto_position", at, tl::to_string (QObject::tr ("Goto Position(Ctrl+G)")))); - - // Add a hook for inserting new items after the modes - menu_entries.push_back (lay::separator ("end_modes", "@toolbar.end")); - - } - - bool menu_activated (const std::string &symbol) const - { - if (symbol == "lv:enable_all") { - - for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { - cls->set_editable_enabled (true); - } - return true; - - } else if (symbol == "lv:disable_all") { - - for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { - cls->set_editable_enabled (false); - } - return true; - - } else { - return false; - } - } - - void implements_primary_mouse_modes (std::vector > > &modes) - { - std::vector mode_titles; - lay::LayoutView::intrinsic_mouse_modes (&mode_titles); - - int mode_id = 0; - for (std::vector ::const_iterator t = mode_titles.begin (); t != mode_titles.end (); ++t, --mode_id) { - // modes: pair(title, pair(insert_pos, id)) - modes.push_back (std::make_pair (*t, std::make_pair ("edit_menu.mode_menu.end;@toolbar.end_modes", mode_id))); - } - } -}; - -static tl::RegisteredClass config_decl (new LayoutViewPluginDeclaration (), -10, "LayoutViewPlugin"); - } // namespace lay diff --git a/src/laybasic/laybasic/layLayoutView.h b/src/laybasic/laybasic/layLayoutView.h index fc05c566e..66469b163 100644 --- a/src/laybasic/laybasic/layLayoutView.h +++ b/src/laybasic/laybasic/layLayoutView.h @@ -50,7 +50,7 @@ #include "layPlugin.h" #include "layDisplayState.h" #include "layBookmarkList.h" -#include "layDialogs.h" +#include "layEditorOptionsFrame.h" #include "gsi.h" #include "tlException.h" #include "tlEvents.h" @@ -83,6 +83,7 @@ class MoveService; class Browser; class ColorButton; class ConfigureAction; +class EditorOptionsPages; /** * @brief Stores a layer reference to create layers which have been added by some action @@ -176,14 +177,15 @@ public: LV_NoLayers = 1, LV_NoHierarchyPanel = 2, LV_NoLibrariesView = 4, - LV_NoBookmarksView = 8, - LV_Naked = 16, - LV_NoZoom = 32, - LV_NoGrid = 64, - LV_NoMove = 128, - LV_NoTracker = 256, - LV_NoSelection = 512, - LV_NoPlugins = 1024, + LV_NoEditorOptionsPanel = 8, + LV_NoBookmarksView = 16, + LV_Naked = 32, + LV_NoZoom = 64, + LV_NoGrid = 128, + LV_NoMove = 256, + LV_NoTracker = 512, + LV_NoSelection = 1024, + LV_NoPlugins = 2048, LV_NoServices = LV_NoMove + LV_NoTracker + LV_NoSelection + LV_NoPlugins }; @@ -237,6 +239,14 @@ public: return mp_control_frame; } + /** + * @brief Gets the layer control panel + */ + lay::LayerControlPanel *control_panel () + { + return mp_control_panel; + } + /** * @brief Gets the container with the hierarchy control panel */ @@ -245,6 +255,14 @@ public: return mp_hierarchy_frame; } + /** + * @brief Gets the hierarchy panel + */ + lay::HierarchyControlPanel *hierarchy_panel () + { + return mp_hierarchy_panel; + } + /** * @brief Gets the container with the libraries view */ @@ -261,6 +279,19 @@ public: return mp_bookmarks_frame; } + /** + * @brief Gets the container with the editor options + */ + QWidget *editor_options_frame () + { + return mp_editor_options_frame; + } + + /** + * @brief Gets the editor options pages widget + */ + lay::EditorOptionsPages *editor_options_pages (); + /** * @brief Pastes from clipboard * @@ -720,6 +751,12 @@ public: */ tl::event current_layer_list_changed_event; + /** + * @brief An event signalling that the current layer has changed + */ + tl::event current_layer_changed_event; + + /** * @brief An event signalling that the visibility of some cells has changed */ @@ -743,8 +780,18 @@ public: * * This method will look up that layer in the layer view tree and select that layer. * This method will also select this layer. + * + * Returns false if the layer is not a valid one. */ - void set_current_layer (unsigned int cv_index, const db::LayerProperties &properties); + bool set_current_layer (unsigned int cv_index, const db::LayerProperties &properties); + + /** + * @brief Sets the currently active layer by layer properties and cell view index + * + * If the layer does not exist, the user will be asked whether to create the layer. + * Returns false if the layer is not a valid one and the user defined to create the layer. + */ + bool set_or_request_current_layer (unsigned int cv_index, const db::LayerProperties &properties); /** * @brief Sets the currently active layer @@ -1958,6 +2005,14 @@ public: */ virtual void drop_url (const std::string &path_or_url); + /** + * @brief Gets a list of all plugins + */ + const std::vector &plugins () + { + return mp_plugins; + } + /** * @brief Localize a plugin by name * @@ -2506,6 +2561,14 @@ public: */ void create_plugins (const lay::PluginDeclaration *except_this = 0); + /** + * @brief Gets the full field box + * + * This is the box to which the view will zoom on zoom_fit(). + * This box is supposed to cover everything inside the view. + */ + db::DBox full_box () const; + public slots: /** * @brief Store the current state on the "previous states" stack @@ -2561,12 +2624,9 @@ public slots: void select_cell_dispatch (const cell_path_type &path, int cellview_index); /** - * @brief Gets the full field box - * - * This is the box to which the view will zoom on zoom_fit(). - * This box is supposed to cover everything inside the view. + * @brief Called when the current layer changed */ - db::DBox full_box () const; + void current_layer_changed_slot (const lay::LayerPropertiesConstIterator &iter); // zoom slots void zoom_fit (); @@ -2582,81 +2642,6 @@ public slots: void pan_right_fast (); void pan_down_fast (); - // menu callbacks - void cm_new_layer (); - void cm_clear_layer (); - void cm_delete_layer (); - void cm_copy_layer (); - void cm_align_cell_origin (); - void cm_edit_layer (); - void cm_lay_convert_to_static (); - void cm_lay_flip_x (); - void cm_lay_flip_y (); - void cm_lay_rot_cw (); - void cm_lay_rot_ccw (); - void cm_lay_free_rot (); - void cm_lay_scale (); - void cm_lay_move (); - void cm_sel_flip_x (); - void cm_sel_flip_y (); - void cm_sel_rot_cw (); - void cm_sel_rot_ccw (); - void cm_sel_free_rot (); - void cm_sel_scale (); - void cm_sel_move (); - void cm_sel_move_to (); - void cm_sel_move_interactive (); - - // forwarded to the layer control panel - void cm_new_tab (); - void cm_rename_tab (); - void cm_remove_tab (); - void cm_select_all (); - void cm_make_valid (); - void cm_make_invalid (); - void cm_hide (); - void cm_hide_all (); - void cm_show (); - void cm_show_all (); - void cm_show_only (); - void cm_rename (); - void cm_delete (); - void cm_insert (); - void cm_group (); - void cm_ungroup (); - void cm_source (); - void cm_sort_by_name (); - void cm_sort_by_ild (); - void cm_sort_by_idl (); - void cm_sort_by_ldi (); - void cm_sort_by_dli (); - void cm_regroup_by_index (); - void cm_regroup_by_datatype (); - void cm_regroup_by_layer (); - void cm_regroup_flatten (); - void cm_expand_all (); - void cm_add_missing (); - void cm_remove_unused (); - void cm_layer_copy (); - void cm_layer_cut (); - void cm_layer_paste (); - - // forwarded to the cell control panel - void cm_cell_user_properties (); - void cm_cell_flatten (); - void cm_cell_rename (); - void cm_cell_replace (); - void cm_cell_delete (); - void cm_cell_select (); - void cm_open_current_cell (); - void cm_cell_hide (); - void cm_cell_show (); - void cm_cell_show_all (); - void cm_cell_copy (); - void cm_cell_cut (); - void cm_cell_paste (); - void cm_cell_convert_to_static (); - // called by children and owner void redraw (); void redraw_layer (unsigned int index); @@ -2728,6 +2713,11 @@ signals: */ void mode_change (int m); + /** + * @brief The current layer changed + */ + void current_layer_changed (const lay::LayerPropertiesConstIterator &l); + protected: /** * @brief Establish the view operations @@ -2761,6 +2751,7 @@ private: lay::LibrariesView *mp_libraries_view; lay::BookmarksView *mp_bookmarks_view; QWidget *mp_control_frame, *mp_hierarchy_frame, *mp_libraries_frame, *mp_bookmarks_frame; + lay::EditorOptionsFrame *mp_editor_options_frame; QSpinBox *mp_min_hier_spbx; QSpinBox *mp_max_hier_spbx; std::list m_cellviews; @@ -2869,29 +2860,18 @@ private: std::vector mp_plugins; - db::LayerProperties m_new_layer_props; - db::DVector m_move_dist; - int m_move_to_origin_mode_x, m_move_to_origin_mode_y; - lay::AlignCellOptions m_align_cell_options; - int m_del_cell_mode; - int m_layer_hier_mode; - int m_duplicate_hier_mode; - bool m_clear_before; - int m_copy_cva, m_copy_cvr; - int m_copy_layera, m_copy_layerr; - bool m_visibility_changed; bool m_active_cellview_changed_event_enabled; tl::DeferredMethod dm_prop_changed; + tl::DeferredMethod dm_setup_editor_option_pages; + void init (db::Manager *mgr, QWidget *parent); void init_menu (); void do_prop_changed (); void do_redraw (int layer); void do_redraw (); - void do_transform (const db::DCplxTrans &tr); - void transform_layout (const db::DCplxTrans &tr); void background_color (QColor c); void ctx_color (QColor c); @@ -2907,22 +2887,20 @@ private: int max_hier_level () const; bool set_hier_levels_basic (std::pair l); + void do_setup_editor_options_pages (); + void update_event_handlers (); void viewport_changed (); void cellview_changed (unsigned int index); bool configure (const std::string &name, const std::string &value); + void config_finalize (); void do_load_layer_props (const std::string &fn, bool map_cv, int cv_index, bool add_default); void finish_cellviews_changed (); void init_layer_properties (LayerProperties &props, const LayerPropertiesList &lp_list) const; void merge_dither_pattern (lay::LayerPropertiesList &props); - void do_cm_duplicate (bool interactive); - void do_cm_paste (bool interactive); - void cm_new_cell (); - void cm_reload (); - // overrides Editables method to display a message void signal_selection_changed (); diff --git a/src/laybasic/laybasic/layLayoutViewConfigPages.cc b/src/laybasic/laybasic/layLayoutViewConfigPages.cc index 30c4aa6bb..d2e66440e 100644 --- a/src/laybasic/laybasic/layLayoutViewConfigPages.cc +++ b/src/laybasic/laybasic/layLayoutViewConfigPages.cc @@ -31,6 +31,7 @@ #include "ui_LayoutViewConfigPage2a.h" #include "ui_LayoutViewConfigPage2b.h" #include "ui_LayoutViewConfigPage2c.h" +#include "ui_LayoutViewConfigPage2d.h" #include "ui_LayoutViewConfigPage3a.h" #include "ui_LayoutViewConfigPage3b.h" #include "ui_LayoutViewConfigPage3c.h" @@ -389,6 +390,42 @@ LayoutViewConfigPage2c::commit (lay::Dispatcher *root) root->config_set (cfg_search_range, (unsigned int) mp_ui->search_range_spinbx->value ()); } +// ------------------------------------------------------------ +// LayoutConfigPage2d implementation + +LayoutViewConfigPage2d::LayoutViewConfigPage2d (QWidget *parent) + : lay::ConfigPage (parent) +{ + mp_ui = new Ui::LayoutViewConfigPage2d (); + mp_ui->setupUi (this); +} + +LayoutViewConfigPage2d::~LayoutViewConfigPage2d () +{ + delete mp_ui; + mp_ui = 0; +} + +void +LayoutViewConfigPage2d::setup (lay::Dispatcher *root) +{ + QColor color; + root->config_get (cfg_tracking_cursor_color, color, lay::ColorConverter ()); + mp_ui->color_pb->set_color (color); + + bool enabled = 0; + root->config_get (cfg_tracking_cursor_enabled, enabled); + mp_ui->tracking_cb->setChecked (enabled); +} + +void +LayoutViewConfigPage2d::commit (lay::Dispatcher *root) +{ + lay::ColorConverter cc; + root->config_set (cfg_tracking_cursor_color, mp_ui->color_pb->get_color (), cc); + root->config_set (cfg_tracking_cursor_enabled, mp_ui->tracking_cb->isChecked ()); +} + // ------------------------------------------------------------ // LayoutConfigPage3a implementation @@ -1516,6 +1553,8 @@ public: options.push_back (std::pair (cfg_sel_halo, "true")); options.push_back (std::pair (cfg_sel_transient_mode, "true")); options.push_back (std::pair (cfg_sel_inside_pcells_mode, "false")); + options.push_back (std::pair (cfg_tracking_cursor_enabled, "true")); + options.push_back (std::pair (cfg_tracking_cursor_color, cc.to_string (QColor ()))); options.push_back (std::pair (cfg_background_color, cc.to_string (QColor ()))); options.push_back (std::pair (cfg_ctx_color, cc.to_string (QColor ()))); options.push_back (std::pair (cfg_ctx_dimming, "50")); @@ -1571,6 +1610,7 @@ public: pages.push_back (std::make_pair (tl::to_string (QObject::tr ("Display|Optimization")), new LayoutViewConfigPage3f (parent))); pages.push_back (std::make_pair (tl::to_string (QObject::tr ("Application|Selection")), new LayoutViewConfigPage2c (parent))); + pages.push_back (std::make_pair (tl::to_string (QObject::tr ("Application|Tracking")), new LayoutViewConfigPage2d (parent))); pages.push_back (std::make_pair (tl::to_string (QObject::tr ("Application|Layer Properties")), new LayoutViewConfigPage5 (parent))); pages.push_back (std::make_pair (tl::to_string (QObject::tr ("Application|Units")), new LayoutViewConfigPage3c (parent))); diff --git a/src/laybasic/laybasic/layLayoutViewConfigPages.h b/src/laybasic/laybasic/layLayoutViewConfigPages.h index 10ffb7a23..1170a6a19 100644 --- a/src/laybasic/laybasic/layLayoutViewConfigPages.h +++ b/src/laybasic/laybasic/layLayoutViewConfigPages.h @@ -38,6 +38,7 @@ namespace Ui { class LayoutViewConfigPage2a; class LayoutViewConfigPage2b; class LayoutViewConfigPage2c; + class LayoutViewConfigPage2d; class LayoutViewConfigPage3a; class LayoutViewConfigPage3b; class LayoutViewConfigPage3c; @@ -138,7 +139,23 @@ private: Ui::LayoutViewConfigPage2c *mp_ui; }; -class LayoutViewConfigPage3a +class LayoutViewConfigPage2d + : public lay::ConfigPage +{ +Q_OBJECT + +public: + LayoutViewConfigPage2d (QWidget *parent); + ~LayoutViewConfigPage2d (); + + virtual void setup (lay::Dispatcher *root); + virtual void commit (lay::Dispatcher *root); + +private: + Ui::LayoutViewConfigPage2d *mp_ui; +}; + +class LayoutViewConfigPage3a : public lay::ConfigPage { Q_OBJECT diff --git a/src/laybasic/laybasic/layLayoutViewFunctions.cc b/src/laybasic/laybasic/layLayoutViewFunctions.cc new file mode 100644 index 000000000..dc774dd26 --- /dev/null +++ b/src/laybasic/laybasic/layLayoutViewFunctions.cc @@ -0,0 +1,2256 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "layLayoutViewFunctions.h" +#include "layLayoutView.h" +#include "layCellSelectionForm.h" +#include "layLayoutStatisticsForm.h" +#include "layLayoutPropertiesForm.h" +#include "layHierarchyControlPanel.h" +#include "layLayerControlPanel.h" +#include "layTipDialog.h" +#include "laySelectCellViewForm.h" +#include "layMove.h" +#include "laybasicConfig.h" + +#include "dbClipboard.h" +#include "dbRecursiveShapeIterator.h" +#include "dbLayoutUtils.h" + +#include +#include + +namespace lay +{ + +LayoutViewFunctions::LayoutViewFunctions (db::Manager *manager, LayoutView *view) + : lay::Plugin (view), mp_view (view), mp_manager (manager) +{ + m_del_cell_mode = 0; + m_move_to_origin_mode_x = 0; + m_move_to_origin_mode_y = 0; + m_del_cell_mode = 0; + m_layer_hier_mode = 0; + m_duplicate_hier_mode = 2; + m_clear_before = true; + m_copy_cva = -1; + m_copy_cvr = -1; + m_copy_layera = -1; + m_copy_layerr = -1; + + m_new_layer_props.layer = 1; + m_new_layer_props.datatype = 0; +} + +LayoutViewFunctions::~LayoutViewFunctions () +{ + // .. nothing yet.. +} + +void +LayoutViewFunctions::menu_activated (const std::string &symbol) +{ + if (symbol == "cm_show_properties") { + + view ()->show_properties (view ()); + + } else if (symbol == "cm_delete") { + + view ()->del (); + // because a "delete" might involve objects currently edited, we cancel the edit after we have deleted the object + view ()->cancel (); + view ()->clear_selection (); + + } else if (symbol == "cm_unselect_all") { + view ()->select (db::DBox (), lay::Editable::Reset); + } else if (symbol == "cm_select_all") { + view ()->select (view ()->full_box (), lay::Editable::Replace); + } else if (symbol == "cm_select_next_item") { + view ()->repeat_selection (lay::Editable::Replace); + } else if (symbol == "cm_select_next_item_add") { + view ()->repeat_selection (lay::Editable::Add); + } else if (symbol == "cm_lv_paste") { + cm_layer_paste (); + } else if (symbol == "cm_lv_cut") { + cm_layer_cut (); + } else if (symbol == "cm_lv_copy") { + cm_layer_copy (); + } else if (symbol == "cm_cell_paste") { + cm_cell_paste (); + } else if (symbol == "cm_cell_cut") { + cm_cell_cut (); + } else if (symbol == "cm_cell_copy") { + cm_cell_copy (); + } else if (symbol == "cm_duplicate") { + do_cm_duplicate (false); + } else if (symbol == "cm_duplicate_interactive") { + do_cm_duplicate (true); + } else if (symbol == "cm_copy") { + + view ()->copy (); + view ()->clear_selection (); + + } else if (symbol == "cm_paste") { + do_cm_paste (false); + } else if (symbol == "cm_paste_interactive") { + do_cm_paste (true); + } else if (symbol == "cm_cut") { + + view ()->cut (); + view ()->cancel (); // see del() for reason why cancel is after cut + view ()->clear_selection (); + + } else if (symbol == "cm_zoom_fit_sel") { + view ()->zoom_fit_sel (); + } else if (symbol == "cm_zoom_fit") { + view ()->zoom_fit (); + } else if (symbol == "cm_pan_left") { + view ()->pan_left (); + } else if (symbol == "cm_pan_right") { + view ()->pan_right (); + } else if (symbol == "cm_pan_up") { + view ()->pan_up (); + } else if (symbol == "cm_pan_down") { + view ()->pan_down (); + } else if (symbol == "cm_zoom_in") { + view ()->zoom_in (); + } else if (symbol == "cm_zoom_out") { + view ()->zoom_out (); + } else if (symbol == "cm_select_current_cell") { + + if (view ()->active_cellview_index () >= 0) { + lay::LayoutView::cell_path_type path; + int cvi = view ()->active_cellview_index (); + view ()->current_cell_path (path); + view ()->select_cell_fit (path, cvi); + } + + } else if (symbol == "cm_open_current_cell") { + + if (view ()->active_cellview_index () >= 0) { + cm_open_current_cell (); + } + + } else if (symbol == "cm_select_cell") { + + if (view ()->active_cellview_index () >= 0) { + + lay::CellSelectionForm form (0, view (), "cell_selection_form"); + + if (form.exec () == QDialog::Accepted && + form.selected_cellview_index () >= 0) { + view ()->select_cell (form.selected_cellview ().combined_unspecific_path (), form.selected_cellview_index ()); + view ()->set_current_cell_path (form.selected_cellview_index (), form.selected_cellview ().combined_unspecific_path ()); + view ()->zoom_fit (); + } + + } + + } else if (symbol == "cm_new_cell") { + cm_new_cell (); + } else if (symbol == "cm_adjust_origin") { + if (view ()->active_cellview_index () >= 0) { + cm_align_cell_origin (); + } + } else if (symbol == "cm_cell_convert_to_static") { + if (view ()->active_cellview_index () >= 0) { + cm_cell_convert_to_static (); + } + } else if (symbol == "cm_lay_convert_to_static") { + if (view ()->active_cellview_index () >= 0) { + cm_lay_convert_to_static (); + } + } else if (symbol == "cm_lay_move") { + if (view ()->active_cellview_index () >= 0) { + cm_lay_move (); + } + } else if (symbol == "cm_lay_scale") { + if (view ()->active_cellview_index () >= 0) { + cm_lay_scale (); + } + } else if (symbol == "cm_lay_free_rot") { + if (view ()->active_cellview_index () >= 0) { + cm_lay_free_rot (); + } + } else if (symbol == "cm_lay_rot_ccw") { + if (view ()->active_cellview_index () >= 0) { + cm_lay_rot_ccw (); + } + } else if (symbol == "cm_lay_rot_cw") { + if (view ()->active_cellview_index () >= 0) { + cm_lay_rot_cw (); + } + } else if (symbol == "cm_lay_flip_y") { + if (view ()->active_cellview_index () >= 0) { + cm_lay_flip_y (); + } + } else if (symbol == "cm_lay_flip_x") { + if (view ()->active_cellview_index () >= 0) { + cm_lay_flip_x (); + } + } else if (symbol == "cm_sel_move") { + if (view ()->active_cellview_index () >= 0) { + cm_sel_move (); + } + } else if (symbol == "cm_sel_move_to") { + if (view ()->active_cellview_index () >= 0) { + cm_sel_move_to (); + } + } else if (symbol == "cm_sel_move_interactive") { + if (view ()->active_cellview_index () >= 0) { + cm_sel_move_interactive (); + } + } else if (symbol == "cm_sel_scale") { + if (view ()->active_cellview_index () >= 0) { + cm_sel_scale (); + } + } else if (symbol == "cm_sel_free_rot") { + if (view ()->active_cellview_index () >= 0) { + cm_sel_free_rot (); + } + } else if (symbol == "cm_sel_rot_ccw") { + if (view ()->active_cellview_index () >= 0) { + cm_sel_rot_ccw (); + } + } else if (symbol == "cm_sel_rot_cw") { + if (view ()->active_cellview_index () >= 0) { + cm_sel_rot_cw (); + } + } else if (symbol == "cm_sel_flip_y") { + if (view ()->active_cellview_index () >= 0) { + cm_sel_flip_y (); + } + } else if (symbol == "cm_sel_flip_x") { + if (view ()->active_cellview_index () >= 0) { + cm_sel_flip_x (); + } + } else if (symbol == "cm_edit_layer") { + if (view ()->active_cellview_index () >= 0) { + cm_edit_layer (); + } + } else if (symbol == "cm_delete_layer") { + if (view ()->active_cellview_index () >= 0) { + cm_delete_layer (); + } + } else if (symbol == "cm_clear_layer") { + if (view ()->active_cellview_index () >= 0) { + cm_clear_layer (); + } + } else if (symbol == "cm_copy_layer") { + if (view ()->active_cellview_index () >= 0) { + cm_copy_layer (); + } + } else if (symbol == "cm_new_layer") { + if (view ()->active_cellview_index () >= 0) { + cm_new_layer (); + } + } else if (symbol == "cm_layout_props") { + lay::LayoutPropertiesForm lp_form (view (), view (), "layout_props_form"); + lp_form.exec (); + } else if (symbol == "cm_layout_stats") { + lay::LayoutStatisticsForm lp_form (view (), view (), "layout_props_form"); + lp_form.exec (); + } else if (symbol == "cm_reload") { + cm_reload (); + } else if (symbol == "cm_inc_max_hier") { + int new_to = view ()->get_max_hier_levels () + 1; + view ()->set_hier_levels (std::make_pair (view ()->get_min_hier_levels (), new_to)); + } else if (symbol == "cm_dec_max_hier") { + int new_to = view ()->get_max_hier_levels () > 0 ? view ()->get_max_hier_levels () - 1 : 0; + view ()->set_hier_levels (std::make_pair (std::min (view ()->get_min_hier_levels (), new_to), new_to)); + } else if (symbol == "cm_max_hier") { + view ()->max_hier (); + } else if (symbol == "cm_max_hier_0") { + view ()->set_hier_levels (std::make_pair (std::min (view ()->get_min_hier_levels (), 0), 0)); + } else if (symbol == "cm_max_hier_1") { + view ()->set_hier_levels (std::make_pair (std::min (view ()->get_min_hier_levels (), 0), 1)); + } else if (symbol == "cm_prev_display_state") { + if (view ()->has_prev_display_state ()) { + view ()->prev_display_state (); + } + } else if (symbol == "cm_next_display_state") { + if (view ()->has_next_display_state ()) { + view ()->next_display_state (); + } + } else if (symbol == "cm_redraw") { + view ()->redraw (); + } else if (symbol == "cm_cell_delete") { + cm_cell_delete (); + } else if (symbol == "cm_cell_replace") { + cm_cell_replace (); + } else if (symbol == "cm_cell_rename") { + cm_cell_rename (); + } else if (symbol == "cm_cell_flatten") { + cm_cell_flatten (); + } else if (symbol == "cm_cell_select") { + cm_cell_select (); + } else if (symbol == "cm_cell_hide") { + cm_cell_hide (); + } else if (symbol == "cm_cell_show") { + cm_cell_show (); + } else if (symbol == "cm_cell_show_all") { + cm_cell_show_all (); + } else if (symbol == "cm_cell_user_properties") { + if (view ()->active_cellview_index () >= 0) { + cm_cell_user_properties (); + } + } else if (symbol == "cm_lv_select_all") { + cm_select_all (); + } else if (symbol == "cm_lv_new_tab") { + cm_new_tab (); + } else if (symbol == "cm_lv_rename_tab") { + cm_rename_tab (); + } else if (symbol == "cm_lv_make_invalid") { + cm_make_invalid (); + } else if (symbol == "cm_lv_remove_tab") { + cm_remove_tab (); + } else if (symbol == "cm_lv_make_valid") { + cm_make_valid (); + } else if (symbol == "cm_lv_hide_all") { + cm_hide_all (); + } else if (symbol == "cm_lv_hide") { + cm_hide (); + } else if (symbol == "cm_lv_show_only") { + cm_show_only (); + } else if (symbol == "cm_lv_show_all") { + cm_show_all (); + } else if (symbol == "cm_lv_show") { + cm_show (); + } else if (symbol == "cm_lv_rename") { + cm_rename (); + } else if (symbol == "cm_lv_delete") { + cm_delete (); + } else if (symbol == "cm_lv_insert") { + cm_insert (); + } else if (symbol == "cm_lv_group") { + cm_group (); + } else if (symbol == "cm_lv_ungroup") { + cm_ungroup (); + } else if (symbol == "cm_lv_source") { + cm_source (); + } else if (symbol == "cm_lv_sort_by_name") { + cm_sort_by_name (); + } else if (symbol == "cm_lv_sort_by_ild") { + cm_sort_by_ild (); + } else if (symbol == "cm_lv_sort_by_idl") { + cm_sort_by_idl (); + } else if (symbol == "cm_lv_sort_by_ldi") { + cm_sort_by_ldi (); + } else if (symbol == "cm_lv_sort_by_dli") { + cm_sort_by_dli (); + } else if (symbol == "cm_lv_regroup_by_index") { + cm_regroup_by_index (); + } else if (symbol == "cm_lv_regroup_by_datatype") { + cm_regroup_by_datatype (); + } else if (symbol == "cm_lv_regroup_by_layer") { + cm_regroup_by_layer (); + } else if (symbol == "cm_lv_regroup_flatten") { + cm_regroup_flatten (); + } else if (symbol == "cm_lv_expand_all") { + cm_expand_all (); + } else if (symbol == "cm_lv_add_missing") { + cm_add_missing (); + } else if (symbol == "cm_lv_remove_unused") { + cm_remove_unused (); + } +} + +void +LayoutViewFunctions::cm_cell_user_properties () +{ + if (! view ()->hierarchy_panel ()) { + return; + } + + int cv_index = view ()->active_cellview_index (); + lay::LayoutView::cell_path_type path; + view ()->hierarchy_panel ()->current_cell (cv_index, path); + + if (cv_index >= 0 && path.size () > 0) { + + db::Layout &layout = view ()->cellview (cv_index)->layout (); + db::Cell &cell = layout.cell (path.back ()); + db::properties_id_type prop_id = cell.prop_id (); + + lay::UserPropertiesForm props_form (view ()); + if (props_form.show (view (), cv_index, prop_id)) { + + view ()->transaction (tl::to_string (QObject::tr ("Edit cell's user propertes"))); + cell.prop_id (prop_id); + view ()->commit (); + + } + + } +} + +void +LayoutViewFunctions::cm_cell_replace () +{ + if (! view ()->hierarchy_panel ()) { + return; + } + + int cv_index = view ()->active_cellview_index (); + std::vector paths; + view ()->hierarchy_panel ()->selected_cells (cv_index, paths); + + if (cv_index >= 0 && paths.size () > 0) { + + if (paths.size () > 1) { + throw tl::Exception (tl::to_string (QObject::tr ("Replace cell cannot be used when multiple cells are selected"))); + } + + db::Layout &layout = view ()->cellview (cv_index)->layout (); + + bool needs_to_ask = false; + for (std::vector::const_iterator p = paths.begin (); p != paths.end () && ! needs_to_ask; ++p) { + if (layout.is_valid_cell_index (p->back ()) && ! layout.cell (p->back ()).is_leaf ()) { + needs_to_ask = true; + } + } + + + lay::ReplaceCellOptionsDialog mode_dialog (view ()); + + db::cell_index_type with_cell = paths.front ().back (); + int mode = needs_to_ask ? m_del_cell_mode : 0; + + if (mode_dialog.exec_dialog (view ()->cellview (cv_index), mode, with_cell)) { + + if (needs_to_ask) { + m_del_cell_mode = mode; + } + + if (with_cell != paths.front ().back ()) { + + // remember the current path + lay::LayoutView::cell_path_type cell_path (view ()->cellview (cv_index).combined_unspecific_path ()); + + view ()->clear_selection (); + + view ()->transaction (tl::to_string (QObject::tr ("Replace cells"))); + + // replace instances of the target cell with the new cell + + db::Cell &target_cell = layout.cell (paths.front ().back ()); + + std::vector > parents; + for (db::Cell::parent_inst_iterator pi = target_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { + parents.push_back (std::make_pair (pi->parent_cell_index (), pi->child_inst ())); + } + + for (std::vector >::const_iterator p = parents.begin (); p != parents.end (); ++p) { + db::CellInstArray ia = p->second.cell_inst (); + ia.object ().cell_index (with_cell); + layout.cell (p->first).replace (p->second, ia); + } + + std::set cells_to_delete; + for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { + if (! p->empty () && layout.is_valid_cell_index (p->back ())) { + cells_to_delete.insert (p->back ()); + if (mode == 2) { + layout.cell (p->back ()).collect_called_cells (cells_to_delete); + } + } + } + + // support a propagation use case: + std::set cells_below_replacement_cell; + cells_below_replacement_cell.insert (with_cell); + layout.cell (with_cell).collect_called_cells (cells_below_replacement_cell); + for (std::set::const_iterator c = cells_below_replacement_cell.begin (); c != cells_below_replacement_cell.end (); ++c) { + cells_to_delete.erase (*c); + } + + if (mode == 0 || mode == 2) { + layout.delete_cells (cells_to_delete); + } else if (mode == 1) { + layout.prune_cells (cells_to_delete); + } + + layout.cleanup (); + + view ()->commit (); + + // If one of the cells in the path was deleted, establish a valid path + + bool needs_update = false; + for (size_t i = cell_path.size (); i > 0; ) { + --i; + if (! layout.is_valid_cell_index (cell_path [i])) { + cell_path.erase (cell_path.begin () + i, cell_path.end ()); + needs_update = true; + } + } + + if (needs_update) { + view ()->select_cell (cell_path, cv_index); + } + + } + + } + + } +} + +void +LayoutViewFunctions::cm_lay_convert_to_static () +{ + // end move operations, cancel edit operations + view ()->cancel_edits (); + view ()->clear_selection (); + + int cv_index = view ()->active_cellview_index (); + if (cv_index >= 0) { + + db::Layout &layout = view ()->cellview (cv_index)->layout (); + + view ()->transaction (tl::to_string (QObject::tr ("Convert all cells to static"))); + + std::vector cells; + for (db::Layout::const_iterator c = layout.begin (); c != layout.end (); ++c) { + cells.push_back (c->cell_index ()); + } + + std::map cell_map; + for (std::vector::const_iterator c = cells.begin (); c != cells.end (); ++c) { + if (layout.is_valid_cell_index (*c)) { + db::cell_index_type new_cell = layout.convert_cell_to_static (*c); + if (new_cell != *c) { + cell_map.insert (std::make_pair (*c, new_cell)); + } + } + } + + // rewrite instances + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + for (db::Cell::const_iterator i = c->begin (); ! i.at_end (); ++i) { + std::map::const_iterator cm = cell_map.find (i->cell_index ()); + if (cm != cell_map.end ()) { + db::CellInstArray ci = i->cell_inst (); + ci.object ().cell_index (cm->second); + c->replace (*i, ci); + } + } + } + + layout.cleanup (); + + view ()->commit (); + + } +} + +void +LayoutViewFunctions::cm_cell_convert_to_static () +{ + if (! view ()->hierarchy_panel ()) { + return; + } + + int cv_index = view ()->active_cellview_index (); + std::vector paths; + view ()->hierarchy_panel ()->selected_cells (cv_index, paths); + + if (cv_index >= 0 && paths.size () > 0) { + + db::Layout &layout = view ()->cellview (cv_index)->layout (); + + // remember the current path + lay::LayoutView::cell_path_type cell_path (view ()->cellview (cv_index).combined_unspecific_path ()); + + view ()->clear_selection (); + + view ()->transaction (tl::to_string (QObject::tr ("Convert cells to static"))); + + std::map cell_map; + + for (std::vector::iterator p = paths.begin (); p != paths.end (); ++p) { + if (! p->empty () && layout.is_valid_cell_index (p->back ())) { + db::cell_index_type new_cell = layout.convert_cell_to_static (p->back ()); + if (new_cell != p->back ()) { + cell_map.insert (std::make_pair (p->back (), new_cell)); + p->back () = new_cell; + } + } + } + + // rewrite instances + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + for (db::Cell::const_iterator i = c->begin (); ! i.at_end (); ++i) { + std::map::const_iterator cm = cell_map.find (i->cell_index ()); + if (cm != cell_map.end ()) { + db::CellInstArray ci = i->cell_inst (); + ci.object ().cell_index (cm->second); + c->replace (*i, ci); + } + } + } + + layout.cleanup (); + + view ()->commit (); + + // If one of the cells in the path was deleted, establish a valid path + + bool needs_update = false; + for (size_t i = cell_path.size (); i > 0; ) { + --i; + if (! layout.is_valid_cell_index (cell_path [i])) { + cell_path.erase (cell_path.begin () + i, cell_path.end ()); + needs_update = true; + } + } + + if (needs_update) { + view ()->select_cell (cell_path, cv_index); + } + + } +} + +static void +collect_cells_to_delete (const db::Layout &layout, const db::Cell &cell, std::set &called) +{ + // don't delete proxies - they are deleted later when the layout is cleaned + for (db::Cell::child_cell_iterator cc = cell.begin_child_cells (); ! cc.at_end (); ++cc) { + if (called.find (*cc) == called.end () && !layout.cell (*cc).is_proxy ()) { + called.insert (*cc); + collect_cells_to_delete (layout, layout.cell (*cc), called); + } + } +} + +void +LayoutViewFunctions::cm_cell_delete () +{ + if (! view ()->hierarchy_panel ()) { + return; + } + + int cv_index = view ()->active_cellview_index (); + std::vector paths; + view ()->hierarchy_panel ()->selected_cells (cv_index, paths); + + if (cv_index >= 0 && paths.size () > 0) { + + db::Layout &layout = view ()->cellview (cv_index)->layout (); + + bool needs_to_ask = false; + for (std::vector::const_iterator p = paths.begin (); p != paths.end () && ! needs_to_ask; ++p) { + if (layout.is_valid_cell_index (p->back ()) && ! layout.cell (p->back ()).is_leaf ()) { + needs_to_ask = true; + } + } + + int mode = m_del_cell_mode; + if (! needs_to_ask) { + mode = 0; + } + + lay::DeleteCellModeDialog mode_dialog (view ()); + if (! needs_to_ask || mode_dialog.exec_dialog (mode)) { + + if (needs_to_ask) { + m_del_cell_mode = mode; + } + + // remember the current path + lay::LayoutView::cell_path_type cell_path (view ()->cellview (cv_index).combined_unspecific_path ()); + + view ()->clear_selection (); + + std::set cells_to_delete; + for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { + if (! p->empty () && layout.is_valid_cell_index (p->back ())) { + cells_to_delete.insert (p->back ()); + if (mode == 2) { + collect_cells_to_delete (layout, layout.cell (p->back ()), cells_to_delete); + } + } + } + + view ()->transaction (tl::to_string (QObject::tr ("Delete cells"))); + + if (mode == 0 || mode == 2) { + layout.delete_cells (cells_to_delete); + } else if (mode == 1) { + layout.prune_cells (cells_to_delete); + } + + layout.cleanup (); + + view ()->commit (); + + // If one of the cells in the path was deleted, establish a valid path + + bool needs_update = false; + for (size_t i = cell_path.size (); i > 0; ) { + --i; + if (! layout.is_valid_cell_index (cell_path [i])) { + cell_path.erase (cell_path.begin () + i, cell_path.end ()); + needs_update = true; + } + } + + if (needs_update) { + view ()->select_cell (cell_path, cv_index); + } + + } + + } +} + +void +LayoutViewFunctions::cm_layer_copy () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->copy (); + } +} + +void +LayoutViewFunctions::cm_layer_cut () +{ + if (view ()->control_panel ()) { + db::Transaction trans (manager (), tl::to_string (QObject::tr ("Cut Layers"))); + view ()->control_panel ()->cut (); + } +} + +void +LayoutViewFunctions::cm_layer_paste () +{ + if (view ()->control_panel ()) { + db::Transaction trans (manager (), tl::to_string (QObject::tr ("Paste Layers"))); + view ()->control_panel ()->paste (); + } +} + +void +LayoutViewFunctions::cm_cell_cut () +{ + if (view ()->hierarchy_panel ()) { + // TODO: currently the hierarchy panel's cut function does it's own transaction handling. + // Otherwise the cut function is not working propertly. + view ()->hierarchy_panel ()->cut (); + } +} + +void +LayoutViewFunctions::cm_cell_paste () +{ + if (view ()->hierarchy_panel ()) { + db::Transaction trans (manager (), tl::to_string (QObject::tr ("Paste Cells"))); + view ()->hierarchy_panel ()->paste (); + } +} + +void +LayoutViewFunctions::cm_cell_copy () +{ + if (view ()->hierarchy_panel ()) { + view ()->hierarchy_panel ()->copy (); + } +} + +void +LayoutViewFunctions::cm_cell_flatten () +{ + if (! view ()->hierarchy_panel ()) { + return; + } + + tl_assert (view ()->is_editable ()); + + int cv_index = view ()->active_cellview_index (); + if (cv_index >= 0) { + + const lay::CellView &cv = view ()->cellview (cv_index); + if (cv.is_valid ()) { + + std::vector paths; + view ()->hierarchy_panel ()->selected_cells (cv_index, paths); + if (paths.empty ()) { + throw tl::Exception (tl::to_string (QObject::tr ("No cells selected for flattening"))); + } + + for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { + if (p->size () > 0 && cv->layout ().cell (p->back ()).is_proxy ()) { + throw tl::Exception (tl::to_string (QObject::tr ("Cannot use this function on a PCell or library cell"))); + } + } + + FlattenInstOptionsDialog options_dialog (view ()); + + int flatten_insts_levels = -1; + bool prune = true; + if (options_dialog.exec_dialog (flatten_insts_levels, prune) && flatten_insts_levels != 0) { + + bool supports_undo = true; + + if (manager () && manager ()->is_enabled ()) { + + lay::TipDialog td (QApplication::activeWindow (), + tl::to_string (QObject::tr ("Undo buffering for the following operation can be memory and time consuming.\nChoose \"Yes\" to use undo buffering or \"No\" for no undo buffering. Warning: in the latter case, the undo history will be lost.\n\nChoose undo buffering?")), + "flatten-undo-buffering", + lay::TipDialog::yesnocancel_buttons); + + lay::TipDialog::button_type button = lay::TipDialog::null_button; + td.exec_dialog (button); + if (button == lay::TipDialog::cancel_button) { + return; + } + + supports_undo = (button == lay::TipDialog::yes_button); + + } else { + supports_undo = false; + } + + view ()->cancel_edits (); + view ()->clear_selection (); + + if (manager ()) { + if (! supports_undo) { + manager ()->clear (); + } else { + manager ()->transaction (tl::to_string (QObject::tr ("Flatten cell"))); + } + } + + db::Layout &layout = cv->layout (); + + std::set child_cells; + for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { + if (p->size () > 0) { + layout.cell (p->back ()).collect_called_cells (child_cells); + } + } + + // don't flatten cells which are child cells of the cells to flatten + std::set cells_to_flatten; + for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { + if (p->size () > 0 && child_cells.find (p->back ()) == child_cells.end ()) { + cells_to_flatten.insert (p->back ()); + } + } + + for (std::set::const_iterator c = cells_to_flatten.begin (); c != cells_to_flatten.end (); ++c) { + db::Cell &target_cell = layout.cell (*c); + layout.flatten (target_cell, flatten_insts_levels, prune); + } + + layout.cleanup (); + + if (supports_undo && manager ()) { + manager ()->commit (); + } + + } + + } + + } +} + +void +LayoutViewFunctions::cm_cell_rename () +{ + if (! view ()->hierarchy_panel ()) { + return; + } + + int cv_index = view ()->active_cellview_index (); + lay::LayoutView::cell_path_type path; + view ()->hierarchy_panel ()->current_cell (cv_index, path); + + if (cv_index >= 0 && path.size () > 0) { + + lay::RenameCellDialog name_dialog (view ()); + + db::Layout &layout = view ()->cellview (cv_index)->layout (); + std::string name (layout.cell_name (path.back ())); + if (name_dialog.exec_dialog (layout, name)) { + + view ()->transaction (tl::to_string (QObject::tr ("Rename cell"))); + layout.rename_cell (path.back (), name.c_str ()); + view ()->commit (); + + } + + } +} + +void +LayoutViewFunctions::cm_cell_select () +{ + if (view ()->hierarchy_panel ()) { + view ()->hierarchy_panel ()->cm_cell_select (); + } +} + +void +LayoutViewFunctions::cm_open_current_cell () +{ + view ()->set_current_cell_path (view ()->active_cellview_index (), view ()->cellview (view ()->active_cellview_index ()).combined_unspecific_path ()); +} + +void +LayoutViewFunctions::cm_cell_hide () +{ + if (view ()->hierarchy_panel ()) { + + std::vector paths; + view ()->hierarchy_panel ()->selected_cells (view ()->active_cellview_index (), paths); + + view ()->transaction (tl::to_string (QObject::tr ("Hide cell"))); + + for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { + if (! p->empty ()) { + view ()->hide_cell (p->back (), view ()->active_cellview_index ()); + } + } + + view ()->commit (); + + } +} + +void +LayoutViewFunctions::cm_cell_show () +{ + if (view ()->hierarchy_panel ()) { + + std::vector paths; + view ()->hierarchy_panel ()->selected_cells (view ()->active_cellview_index (), paths); + + view ()->transaction (tl::to_string (QObject::tr ("Show cell"))); + + for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { + if (! p->empty ()) { + view ()->show_cell (p->back (), view ()->active_cellview_index ()); + } + } + + view ()->commit (); + + } +} + +void +LayoutViewFunctions::cm_cell_show_all () +{ + if (view ()->hierarchy_panel ()) { + view ()->transaction (tl::to_string (QObject::tr ("Show all cells"))); + view ()->show_all_cells (); + view ()->commit (); + } +} + +void +LayoutViewFunctions::cm_select_all () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_select_all (); + } +} + +void +LayoutViewFunctions::cm_new_tab () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_new_tab (); + } +} + +void +LayoutViewFunctions::cm_remove_tab () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_remove_tab (); + } +} + +void +LayoutViewFunctions::cm_rename_tab () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_rename_tab (); + } +} + +void +LayoutViewFunctions::cm_make_invalid () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_make_invalid (); + } +} + +void +LayoutViewFunctions::cm_make_valid () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_make_valid (); + } +} + +void +LayoutViewFunctions::cm_hide () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_hide (); + } +} + +void +LayoutViewFunctions::cm_hide_all () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_hide_all (); + } +} + +void +LayoutViewFunctions::cm_show_only () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_show_only (); + } +} + +void +LayoutViewFunctions::cm_show_all () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_show_all (); + } +} + +void +LayoutViewFunctions::cm_show () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_show (); + } +} + +void +LayoutViewFunctions::cm_rename () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_rename (); + } +} + +void +LayoutViewFunctions::cm_delete () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_delete (); + } +} + +void +LayoutViewFunctions::cm_insert () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_insert (); + } +} + +void +LayoutViewFunctions::cm_group () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_group (); + } +} + +void +LayoutViewFunctions::cm_ungroup () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_ungroup (); + } +} + +void +LayoutViewFunctions::cm_source () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_source (); + } +} + +void +LayoutViewFunctions::cm_sort_by_name () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_sort_by_name (); + } +} + +void +LayoutViewFunctions::cm_sort_by_ild () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_sort_by_ild (); + } +} + +void +LayoutViewFunctions::cm_sort_by_idl () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_sort_by_idl (); + } +} + +void +LayoutViewFunctions::cm_sort_by_ldi () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_sort_by_ldi (); + } +} + +void +LayoutViewFunctions::cm_sort_by_dli () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_sort_by_dli (); + } +} + +void +LayoutViewFunctions::cm_regroup_by_index () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_regroup_by_index (); + } +} + +void +LayoutViewFunctions::cm_regroup_by_datatype () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_regroup_by_datatype (); + } +} + +void +LayoutViewFunctions::cm_regroup_by_layer () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_regroup_by_layer (); + } +} + +void +LayoutViewFunctions::cm_regroup_flatten () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_regroup_flatten (); + } +} + +void +LayoutViewFunctions::cm_expand_all () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_expand_all (); + } +} + +void +LayoutViewFunctions::cm_add_missing () +{ + if (view ()->control_panel ()) { + view ()->control_panel ()->cm_add_missing (); + } +} + +void +LayoutViewFunctions::cm_remove_unused () +{ + view ()->remove_unused_layers (); +} + +void +LayoutViewFunctions::do_cm_duplicate (bool interactive) +{ + // Do duplicate simply by concatenating copy & paste currently. + // Save the clipboard state before in order to preserve the current content + db::Clipboard saved_clipboard; + db::Clipboard::instance ().swap (saved_clipboard); + + try { + view ()->copy (); + view ()->clear_selection (); + view ()->cancel (); + if (interactive) { + view ()->paste_interactive (); + } else { + view ()->paste (); + } + db::Clipboard::instance ().swap (saved_clipboard); + } catch (...) { + db::Clipboard::instance ().swap (saved_clipboard); + throw; + } +} + +void +LayoutViewFunctions::do_cm_paste (bool interactive) +{ + if (! db::Clipboard::instance ().empty ()) { + view ()->cancel (); + view ()->clear_selection (); + if (interactive) { + view ()->paste_interactive (); + } else { + view ()->paste (); + } + } +} + +void +LayoutViewFunctions::cm_new_cell () +{ + static double s_new_cell_window_size = 2.0; + static std::string s_new_cell_cell_name; + + NewCellPropertiesDialog cell_prop_dia (view ()); + if (cell_prop_dia.exec_dialog (& view ()->cellview (view ()->active_cellview_index ())->layout (), s_new_cell_cell_name, s_new_cell_window_size)) { + + db::cell_index_type new_ci = view ()->new_cell (view ()->active_cellview_index (), s_new_cell_cell_name.c_str ()); + view ()->select_cell (new_ci, view ()->active_cellview_index ()); + + db::DBox zb = db::DBox (-0.5 * s_new_cell_window_size, -0.5 * s_new_cell_window_size, 0.5 * s_new_cell_window_size, 0.5 * s_new_cell_window_size); + if (view ()->get_max_hier_levels () < 1 || view ()->get_min_hier_levels () > 0) { + view ()->zoom_box_and_set_hier_levels (zb, std::make_pair (0, 1)); + } else { + view ()->zoom_box (zb); + } + + } +} + +// TODO: this constant is defined in MainWindow.cc too ... +const int max_dirty_files = 15; + +void +LayoutViewFunctions::cm_reload () +{ + std::vector selected; + + if (view ()->cellviews () > 1) { + + lay::SelectCellViewForm form (0, view (), tl::to_string (QObject::tr ("Select Layouts To Reload"))); + form.select_all (); + + if (form.exec () == QDialog::Accepted) { + selected = form.selected_cellviews (); + } + + } else if (view ()->cellviews () > 0) { + selected.push_back (0); + } + + if (selected.size () > 0) { + + int dirty_layouts = 0; + std::string dirty_files; + + for (std::vector ::const_iterator i = selected.begin (); i != selected.end (); ++i) { + + const lay::CellView &cv = view ()->cellview (*i); + + if (cv->layout ().is_editable () && cv->is_dirty ()) { + ++dirty_layouts; + if (dirty_layouts == max_dirty_files) { + dirty_files += "\n..."; + } else if (dirty_layouts < max_dirty_files) { + if (! dirty_files.empty ()) { + dirty_files += "\n"; + } + dirty_files += cv->name (); + } + } + + } + + bool can_reload = true; + if (dirty_layouts != 0) { + + QMessageBox mbox (view ()); + mbox.setText (tl::to_qstring (tl::to_string (QObject::tr ("The following layouts need saving:\n\n")) + dirty_files + "\n\nPress 'Reload Without Saving' to reload anyhow and discard changes.")); + mbox.setWindowTitle (QObject::tr ("Save Needed")); + mbox.setIcon (QMessageBox::Warning); + QAbstractButton *yes_button = mbox.addButton (QObject::tr ("Reload Without Saving"), QMessageBox::YesRole); + mbox.addButton (QMessageBox::Cancel); + + mbox.exec (); + + can_reload = (mbox.clickedButton() == yes_button); + + } + + if (can_reload) { + + // Actually reload + for (std::vector ::const_iterator i = selected.begin (); i != selected.end (); ++i) { + view ()->reload_layout (*i); + } + + } + + } +} + +void +LayoutViewFunctions::do_transform (const db::DCplxTrans &tr) +{ + // end move operations, cancel edit operations + view ()->cancel_edits (); + view ()->lay::Editables::transform (tr); +} + +void +LayoutViewFunctions::transform_layout (const db::DCplxTrans &tr_mic) +{ + // end move operations, cancel edit operations + view ()->cancel_edits (); + view ()->clear_selection (); + + int cv_index = view ()->active_cellview_index (); + if (cv_index >= 0) { + + db::Layout &layout = view ()->cellview (cv_index)->layout (); + + db::ICplxTrans tr (db::DCplxTrans (1.0 / layout.dbu ()) * tr_mic * db::DCplxTrans (layout.dbu ())); + + bool has_proxy = false; + for (db::Layout::const_iterator c = layout.begin (); ! has_proxy && c != layout.end (); ++c) { + has_proxy = c->is_proxy (); + } + + if (has_proxy && + QMessageBox::question (view (), + QObject::tr ("Transforming PCells Or Library Cells"), + QObject::tr ("The layout contains PCells or library cells or both.\n" + "Any changes to such cells may be lost when their layout is refreshed later.\n" + "Consider using 'Convert all cells to static' before transforming the layout.\n" + "\n" + "Would you like to continue?\n" + "Choose 'Yes' to continue anyway. Choose 'No' to cancel."), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { + return; + } + + view ()->transaction (tl::to_string (QObject::tr ("Transform layout"))); + layout.transform (tr); + view ()->commit (); + + } +} + +void +LayoutViewFunctions::cm_lay_flip_x () +{ + transform_layout (db::DCplxTrans (db::FTrans::m90)); +} + +void +LayoutViewFunctions::cm_lay_flip_y () +{ + transform_layout (db::DCplxTrans (db::FTrans::m0)); +} + +void +LayoutViewFunctions::cm_lay_rot_ccw () +{ + db::DCplxTrans tr (db::DFTrans::r90); + transform_layout (db::DCplxTrans (db::FTrans::r90)); +} + +void +LayoutViewFunctions::cm_lay_rot_cw () +{ + transform_layout (db::DCplxTrans (db::FTrans::r270)); +} + +void +LayoutViewFunctions::cm_lay_free_rot () +{ + bool ok = false; + QString s = QInputDialog::getText (QApplication::activeWindow (), + QObject::tr ("Free rotation"), + QObject::tr ("Rotation angle in degree (counterclockwise)"), + QLineEdit::Normal, QString::fromUtf8 ("0.0"), + &ok); + + if (ok) { + + double angle = 0.0; + tl::from_string (tl::to_string (s), angle); + + transform_layout (db::DCplxTrans (1.0, angle, false, db::DVector ())); + + } +} + +void +LayoutViewFunctions::cm_lay_scale () +{ + bool ok = false; + QString s = QInputDialog::getText (QApplication::activeWindow (), + QObject::tr ("Scaling"), + QObject::tr ("Scaling factor"), + QLineEdit::Normal, QString::fromUtf8 ("1.0"), + &ok); + + if (ok) { + + double scale = 0.0; + tl::from_string (tl::to_string (s), scale); + + transform_layout (db::DCplxTrans (scale)); + + } +} + +void +LayoutViewFunctions::cm_lay_move () +{ + lay::MoveOptionsDialog options (view ()); + if (options.exec_dialog (m_move_dist)) { + transform_layout (db::DCplxTrans (m_move_dist)); + } +} + +void +LayoutViewFunctions::cm_sel_flip_x () +{ + db::DCplxTrans tr (db::DFTrans::m90); + db::DBox sel_bbox (view ()->lay::Editables::selection_bbox ()); + if (! sel_bbox.empty ()) { + tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); + } + do_transform (tr); +} + +void +LayoutViewFunctions::cm_sel_flip_y () +{ + db::DCplxTrans tr (db::DFTrans::m0); + db::DBox sel_bbox (view ()->lay::Editables::selection_bbox ()); + if (! sel_bbox.empty ()) { + tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); + } + do_transform (tr); +} + +void +LayoutViewFunctions::cm_sel_rot_ccw () +{ + db::DCplxTrans tr (db::DFTrans::r90); + db::DBox sel_bbox (view ()->lay::Editables::selection_bbox ()); + if (! sel_bbox.empty ()) { + tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); + } + do_transform (tr); +} + +void +LayoutViewFunctions::cm_sel_rot_cw () +{ + db::DCplxTrans tr (db::DFTrans::r270); + db::DBox sel_bbox (view ()->lay::Editables::selection_bbox ()); + if (! sel_bbox.empty ()) { + tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); + } + do_transform (tr); +} + +void +LayoutViewFunctions::cm_sel_free_rot () +{ + bool ok = false; + QString s = QInputDialog::getText (QApplication::activeWindow (), + QObject::tr ("Free rotation"), + QObject::tr ("Rotation angle in degree (counterclockwise)"), + QLineEdit::Normal, QString::fromUtf8 ("0.0"), + &ok); + + if (ok) { + + double angle = 0.0; + tl::from_string (tl::to_string (s), angle); + + db::DCplxTrans tr = db::DCplxTrans (1.0, angle, false, db::DVector ()); + db::DBox sel_bbox (view ()->lay::Editables::selection_bbox ()); + if (! sel_bbox.empty ()) { + tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); + } + do_transform (tr); + + } +} + +void +LayoutViewFunctions::cm_sel_scale () +{ + bool ok = false; + QString s = QInputDialog::getText (QApplication::activeWindow (), + QObject::tr ("Scaling"), + QObject::tr ("Scaling factor"), + QLineEdit::Normal, QString::fromUtf8 ("1.0"), + &ok); + + if (ok) { + + double scale = 0.0; + tl::from_string (tl::to_string (s), scale); + + db::DCplxTrans tr = db::DCplxTrans (scale); + db::DBox sel_bbox (view ()->lay::Editables::selection_bbox ()); + if (! sel_bbox.empty ()) { + tr = db::DCplxTrans (sel_bbox.center () - db::DPoint ()) * tr * db::DCplxTrans (db::DPoint () - sel_bbox.center ()); + } + do_transform (tr); + + } +} + +void +LayoutViewFunctions::cm_sel_move_interactive () +{ + if (view ()->move_service ()->begin_move ()) { + view ()->switch_mode (-1); // move mode + } +} + +void +LayoutViewFunctions::cm_sel_move_to () +{ + db::DBox sel_bbox (view ()->lay::Editables::selection_bbox ()); + if (sel_bbox.empty ()) { + throw tl::Exception (tl::to_string (QObject::tr ("Nothing selected to move"))); + } + + double x = sel_bbox.left () + (sel_bbox.width () * (1 + m_move_to_origin_mode_x) * 0.5); + double y = sel_bbox.bottom () + (sel_bbox.height () * (1 + m_move_to_origin_mode_y) * 0.5); + db::DPoint move_target (x, y); + + lay::MoveToOptionsDialog options (view ()); + if (options.exec_dialog (m_move_to_origin_mode_x, m_move_to_origin_mode_y, move_target)) { + + x = sel_bbox.left () + (sel_bbox.width () * (1 + m_move_to_origin_mode_x) * 0.5); + y = sel_bbox.bottom () + (sel_bbox.height () * (1 + m_move_to_origin_mode_y) * 0.5); + + do_transform (db::DCplxTrans (move_target - db::DPoint (x, y))); + + } +} + +void +LayoutViewFunctions::cm_sel_move () +{ + lay::MoveOptionsDialog options (view ()); + if (options.exec_dialog (m_move_dist)) { + do_transform (db::DCplxTrans (m_move_dist)); + } +} + +void +LayoutViewFunctions::cm_copy_layer () +{ + struct { int *cv; int *layer; } specs [] = { + { &m_copy_cva, &m_copy_layera }, + { &m_copy_cvr, &m_copy_layerr } + }; + + for (unsigned int i = 0; i < sizeof (specs) / sizeof (specs[0]); ++i) { + + int &cv = *(specs[i].cv); + int &layer = *(specs[i].layer); + + if (cv >= int (view ()->cellviews ())) { + cv = -1; + } + + int index = view ()->active_cellview_index (); + if (cv < 0) { + cv = index; + } + + if (cv < 0 || ! view ()->cellview (cv)->layout ().is_valid_layer ((unsigned int) layer)) { + layer = -1; + } + + } + + lay::DuplicateLayerDialog dialog (view ()); + if (dialog.exec_dialog (view (), m_copy_cva, m_copy_layera, m_copy_cvr, m_copy_layerr, m_duplicate_hier_mode, m_clear_before)) { + + bool supports_undo = true; + + if (manager () && manager ()->is_enabled ()) { + + lay::TipDialog td (QApplication::activeWindow (), + tl::to_string (QObject::tr ("Undo buffering for the following operation can be memory and time consuming.\nChoose \"Yes\" to use undo buffering or \"No\" for no undo buffering. Warning: in the latter case, the undo history will be lost.\n\nChoose undo buffering?")), + "copy-layer-undo-buffering", + lay::TipDialog::yesnocancel_buttons); + + lay::TipDialog::button_type button = lay::TipDialog::null_button; + td.exec_dialog (button); + if (button == lay::TipDialog::cancel_button) { + return; + } + + supports_undo = (button == lay::TipDialog::yes_button); + + } else { + supports_undo = false; + } + + view ()->cancel (); + + if (manager ()) { + if (! supports_undo) { + manager ()->clear (); + } else { + manager ()->transaction (tl::to_string (QObject::tr ("Duplicate layer"))); + } + } + + try { + + bool same_layout = (&view ()->cellview (m_copy_cvr)->layout () == &view ()->cellview (m_copy_cva)->layout ()); + if (same_layout && m_copy_layera == m_copy_layerr) { + throw tl::Exception (tl::to_string (QObject::tr ("Source and target layer must not be identical for duplicate operation"))); + } + + if (m_duplicate_hier_mode == 0) { + + // clear the result layer for all called cells in flat mode + if (m_clear_before) { + std::set called_cells; + called_cells.insert (view ()->cellview (m_copy_cvr).cell_index ()); + view ()->cellview (m_copy_cvr).cell ()->collect_called_cells (called_cells); + for (std::set::const_iterator c = called_cells.begin (); c != called_cells.end (); ++c) { + view ()->cellview (m_copy_cvr)->layout ().cell (*c).shapes (m_copy_layerr).clear (); + } + } + + db::Cell &target_cell = *view ()->cellview (m_copy_cvr).cell (); + + if (! same_layout) { + + // flat mode (different layouts) + db::PropertyMapper pm (view ()->cellview (m_copy_cvr)->layout (), view ()->cellview (m_copy_cva)->layout ()); + for (db::RecursiveShapeIterator si (view ()->cellview (m_copy_cva)->layout (), *view ()->cellview (m_copy_cva).cell (), m_copy_layera); ! si.at_end (); ++si) { + target_cell.shapes (m_copy_layerr).insert (*si, si.trans (), pm); + } + + } else { + + // flat mode (same layouts) + tl::ident_map pm1; + db::Shapes &res = target_cell.shapes (m_copy_layerr); + + db::Layout &layout = view ()->cellview (m_copy_cvr)->layout (); + try { + + // using update/start_layout and end_changes improves the performance since changing the + // shapes collection will invalidate the layout and cause updates inside the RecursiveShapeIerator + layout.update (); + layout.start_changes (); + for (db::RecursiveShapeIterator si (view ()->cellview (m_copy_cva)->layout (), *view ()->cellview (m_copy_cva).cell (), m_copy_layera); ! si.at_end (); ++si) { + res.insert (*si, si.trans (), pm1); + } + layout.end_changes (); + + } catch (...) { + layout.end_changes (); + throw; + } + + } + + } else if (m_duplicate_hier_mode == 1) { + + db::Cell &cell = *view ()->cellview (m_copy_cva).cell (); + db::Cell &target_cell = *view ()->cellview (m_copy_cvr).cell (); + + if (m_clear_before) { + target_cell.clear (m_copy_layerr); + } + + if (m_copy_cvr == m_copy_cva) { + + // current cell only mode: identical cell + cell.copy (m_copy_layera, m_copy_layerr); + + } else if (! same_layout) { + + // current cell only mode (different layouts) + db::PropertyMapper pm (view ()->cellview (m_copy_cvr)->layout (), view ()->cellview (m_copy_cva)->layout ()); + for (db::Shapes::shape_iterator si = view ()->cellview (m_copy_cva).cell ()->shapes (m_copy_layera).begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + target_cell.shapes (m_copy_layerr).insert (*si, pm); + } + + } else { + + // current cell only mode (same layouts, but different cells) + for (db::Shapes::shape_iterator si = view ()->cellview (m_copy_cva).cell ()->shapes (m_copy_layera).begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + target_cell.shapes (m_copy_layerr).insert (*si); + } + + } + + } else if (m_duplicate_hier_mode == 2) { + + // subcells cell by cell - source and target layout must be identical + std::set called_cells; + view ()->cellview (m_copy_cva).cell ()->collect_called_cells (called_cells); + called_cells.insert (view ()->cellview (m_copy_cva).cell_index ()); + + db::Layout &layout = view ()->cellview (m_copy_cva)->layout (); + for (std::set::const_iterator c = called_cells.begin (); c != called_cells.end (); ++c) { + db::Cell &cell = layout.cell (*c); + if (m_clear_before) { + cell.clear (m_copy_layerr); + } + cell.copy (m_copy_layera, m_copy_layerr); + } + + } + + if (manager () && supports_undo) { + manager ()->commit (); + } + + } catch (...) { + if (manager () && supports_undo) { + manager ()->commit (); + } + throw; + } + + } +} + +void +LayoutViewFunctions::cm_new_layer () +{ + int index = view ()->active_cellview_index (); + + if (index >= 0 && int (view ()->cellviews ()) > index) { + + const lay::CellView &cv = view ()->cellview (index); + + lay::NewLayerPropertiesDialog prop_dia (view ()); + if (prop_dia.exec_dialog (cv, m_new_layer_props)) { + + for (unsigned int l = 0; l < cv->layout ().layers (); ++l) { + if (cv->layout ().is_valid_layer (l) && cv->layout ().get_properties (l).log_equal (m_new_layer_props)) { + throw tl::Exception (tl::to_string (QObject::tr ("A layer with that signature already exists: ")) + m_new_layer_props.to_string ()); + } + } + + view ()->transaction (tl::to_string (QObject::tr ("New layer"))); + + unsigned int l = cv->layout ().insert_layer (m_new_layer_props); + std::vector nl; + nl.push_back (l); + view ()->add_new_layers (nl, index); + view ()->update_content (); + + view ()->commit (); + + } + + } +} + +void +LayoutViewFunctions::cm_align_cell_origin () +{ + int cv_index = view ()->active_cellview_index (); + if (cv_index >= 0) { + + const db::Cell *cell = view ()->cellview (cv_index).cell (); + if (! cell) { + return; + } + if (cell->is_proxy ()) { + throw tl::Exception (tl::to_string (QObject::tr ("Cannot use this function on a PCell or library cell"))); + } + + lay::AlignCellOptionsDialog dialog (view ()); + if (dialog.exec_dialog (m_align_cell_options)) { + + view ()->clear_selection (); + + view ()->transaction (tl::to_string (QObject::tr ("Align cell origin"))); + + db::Box bbox; + + if (m_align_cell_options.visible_only) { + for (lay::LayerPropertiesConstIterator l = view ()->begin_layers (); !l.at_end (); ++l) { + if (! l->has_children () && l->layer_index () >= 0 && l->cellview_index () == cv_index && l->visible (true /*real*/)) { + bbox += cell->bbox (l->layer_index ()); + } + } + } else { + bbox = cell->bbox (); + } + + db::Coord refx, refy; + switch (m_align_cell_options.mode_x) { + case -1: + refx = bbox.left (); + break; + case 1: + refx = bbox.right (); + break; + default: + refx = bbox.center ().x (); + break; + } + switch (m_align_cell_options.mode_y) { + case -1: + refy = bbox.bottom (); + break; + case 1: + refy = bbox.top (); + break; + default: + refy = bbox.center ().y (); + break; + } + + db::Layout &layout = view ()->cellview (cv_index)->layout (); + db::Cell &nc_cell = layout.cell (cell->cell_index ()); + + db::Trans t (db::Vector (-refx + db::coord_traits::rounded (m_align_cell_options.xpos / layout.dbu ()), -refy + db::coord_traits::rounded (m_align_cell_options.ypos / layout.dbu ()))); + + for (unsigned int i = 0; i < layout.layers (); ++i) { + if (layout.is_valid_layer (i)) { + db::Shapes &shapes = nc_cell.shapes (i); + for (db::Shapes::shape_iterator s = shapes.begin (db::ShapeIterator::All); ! s.at_end (); ++s) { + shapes.transform (*s, t); + } + } + } + + for (db::Cell::const_iterator inst = nc_cell.begin (); ! inst.at_end (); ++inst) { + nc_cell.transform (*inst, t); + } + + if (m_align_cell_options.adjust_parents) { + + std::vector > insts_to_modify; + for (db::Cell::parent_inst_iterator pi = nc_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { + insts_to_modify.push_back (std::make_pair (& layout.cell (pi->parent_cell_index ()), pi->child_inst ())); + } + + db::Trans ti (db::Vector (refx, refy)); + for (std::vector >::const_iterator im = insts_to_modify.begin (); im != insts_to_modify.end (); ++im) { + im->first->transform (im->second, db::Trans (db::Vector (im->second.complex_trans ().trans (db::Vector (refx, refy))))); + } + + } + + view ()->commit (); + + } + + } +} + +void +LayoutViewFunctions::cm_edit_layer () +{ + lay::LayerPropertiesConstIterator sel = view ()->current_layer (); + if (sel.is_null ()) { + throw tl::Exception (tl::to_string (QObject::tr ("No layer selected for editing it's properties"))); + } + + int index = sel->cellview_index (); + if (sel->has_children () || index < 0 || int (view ()->cellviews ()) <= index || sel->layer_index () < 0) { + throw tl::Exception (tl::to_string (QObject::tr ("No valid layer selected for editing it's properties"))); + } + + const lay::CellView &cv = view ()->cellview (index); + + db::LayerProperties layer_props = cv->layout ().get_properties ((unsigned int) sel->layer_index ()); + + lay::NewLayerPropertiesDialog prop_dia (view ()); + if (prop_dia.exec_dialog (cv, layer_props)) { + + for (unsigned int l = 0; l < cv->layout ().layers (); ++l) { + if (cv->layout ().is_valid_layer (l) && int (l) != sel->layer_index () && cv->layout ().get_properties (l).log_equal (layer_props)) { + throw tl::Exception (tl::to_string (QObject::tr ("A layer with that signature already exists: ")) + layer_props.to_string ()); + } + } + + view ()->transaction (tl::to_string (QObject::tr ("Edit layer"))); + + cv->layout ().set_properties (sel->layer_index (), layer_props); + + lay::LayerProperties lp (*sel); + lay::ParsedLayerSource s = lp.source (false); + s.layer (layer_props.layer); + s.datatype (layer_props.datatype); + if (! layer_props.name.empty ()) { + s.name (layer_props.name); + } else { + s.clear_name (); + } + lp.set_source (s); + view ()->set_properties (sel, lp); + + view ()->update_content (); + + view ()->commit (); + + } +} + +void +LayoutViewFunctions::cm_delete_layer () +{ + std::vector sel = view ()->selected_layers (); + std::sort (sel.begin (), sel.end (), CompareLayerIteratorBottomUp ()); + + // collect valid layers + std::vector valid_sel; + std::set > valid_layers; + for (std::vector::const_iterator si = sel.begin (); si != sel.end (); ++si) { + int cv_index = (*si)->cellview_index (); + const lay::CellView &cv = view ()->cellview (cv_index); + if (!(*si)->has_children () && cv_index >= 0 && int (view ()->cellviews ()) > cv_index && (*si)->layer_index () >= 0 && cv.is_valid ()) { + valid_sel.push_back (*si); + valid_layers.insert (std::make_pair (&cv->layout (), (*si)->layer_index ())); + } + } + + if (valid_sel.empty ()) { + throw tl::Exception (tl::to_string (QObject::tr ("No or no valid layer selected for deleting them"))); + } + + view ()->cancel_edits (); + view ()->clear_selection (); + + view ()->transaction (tl::to_string (QObject::tr ("Delete layers"))); + + // Hint: delete_layer must come before the layers are actually deleted in because + // for undo this must be the last thing to do (otherwise the layout is not propertly set up) + + for (std::vector::const_iterator si = valid_sel.begin (); si != valid_sel.end (); ++si) { + lay::LayerPropertiesConstIterator lp = *si; + view ()->delete_layer (lp); + } + + for (std::set >::const_iterator li = valid_layers.begin (); li != valid_layers.end(); ++li) { + + unsigned int layer_index = li->second; + db::Layout *layout = li->first; + + for (db::Layout::iterator c = layout->begin (); c != layout->end (); ++c) { + c->shapes (layer_index).clear (); + } + + layout->delete_layer (layer_index); + + } + + view ()->update_content (); + + view ()->commit (); +} + +void +LayoutViewFunctions::cm_clear_layer () +{ + std::vector sel = view ()->selected_layers (); + if (sel.empty ()) { + throw tl::Exception (tl::to_string (QObject::tr ("No layer selected for clearing"))); + } + + lay::ClearLayerModeDialog mode_dialog (view ()); + if (mode_dialog.exec_dialog (m_layer_hier_mode)) { + + view ()->cancel_edits (); + view ()->clear_selection (); + + view ()->transaction (tl::to_string (QObject::tr ("Clear layer"))); + + for (std::vector::const_iterator si = sel.begin (); si != sel.end (); ++si) { + + if (! (*si)->has_children () && (*si)->layer_index () >= 0 && view ()->cellview ((*si)->cellview_index ()).is_valid ()) { + + int layer_index = (*si)->layer_index (); + const lay::CellView &cv = view ()->cellview ((*si)->cellview_index ()); + + if (m_layer_hier_mode == 0) { + cv.cell ()->clear ((unsigned int) layer_index); + } else if (m_layer_hier_mode == 1) { + + cv.cell ()->clear ((unsigned int) layer_index); + + std::set called_cells; + cv.cell ()->collect_called_cells (called_cells); + for (std::set ::const_iterator cc = called_cells.begin (); cc != called_cells.end (); ++cc) { + cv->layout ().cell (*cc).clear ((unsigned int) layer_index); + } + + } else { + cv->layout ().clear_layer ((unsigned int) layer_index); + } + + } + + } + + view ()->commit (); + + } +} + +// ------------------------------------------------------------ +// Declaration of the "plugin" for the menu entries + +class LayoutViewPluginDeclaration + : public lay::PluginDeclaration +{ +public: + virtual void get_menu_entries (std::vector &menu_entries) const + { + std::string at; + + // secret menu entries + at = "@secrets.end"; + menu_entries.push_back (lay::menu_item ("cm_paste_interactive", "paste_interactive:edit", at, tl::to_string (QObject::tr ("Paste Interactive")))); + menu_entries.push_back (lay::menu_item ("cm_duplicate_interactive", "duplicate_interactive:edit", at, tl::to_string (QObject::tr ("Duplicate Interactive")))); + menu_entries.push_back (lay::menu_item ("cm_sel_move_interactive", "sel_move_interactive:edit", at, tl::to_string (QObject::tr ("Move Interactive")))); + menu_entries.push_back (lay::menu_item ("cm_select_next_item", "select_next_item:edit", at, tl::to_string (QObject::tr ("Select Next Item(Space)")))); + menu_entries.push_back (lay::menu_item ("cm_select_next_item_add", "select_next_item_add:edit", at, tl::to_string (QObject::tr ("Select Next Item too(Shift+Space)")))); + + at = "edit_menu.edit_options_group"; + menu_entries.push_back (lay::menu_item ("cm_undo", "undo:edit", at, tl::to_string (QObject::tr ("Undo(Ctrl+Z)")))); + menu_entries.push_back (lay::menu_item ("cm_redo", "redo:edit", at, tl::to_string (QObject::tr ("Redo(Ctrl+Y)")))); + + menu_entries.push_back (lay::separator ("basic_group", at)); + menu_entries.push_back (lay::submenu ("layout_menu:edit:edit_mode", at, tl::to_string (QObject::tr ("Layout")))); + { + std::string at = "edit_menu.layout_menu.end"; + menu_entries.push_back (lay::menu_item ("cm_lay_flip_x", "lay_flip_x:edit_mode", at, tl::to_string (QObject::tr ("Flip Horizontally")))); + menu_entries.push_back (lay::menu_item ("cm_lay_flip_y", "lay_flip_y:edit_mode", at, tl::to_string (QObject::tr ("Flip Vertically")))); + menu_entries.push_back (lay::menu_item ("cm_lay_rot_cw", "lay_rot_cw:edit_mode", at, tl::to_string (QObject::tr ("Rotate Clockwise")))); + menu_entries.push_back (lay::menu_item ("cm_lay_rot_ccw", "lay_rot_ccw:edit_mode", at, tl::to_string (QObject::tr ("Rotate Counterclockwise")))); + menu_entries.push_back (lay::menu_item ("cm_lay_free_rot", "lay_free_rot:edit_mode", at, tl::to_string (QObject::tr ("Rotation By Angle")))); + menu_entries.push_back (lay::menu_item ("cm_lay_scale", "lay_scale:edit_mode", at, tl::to_string (QObject::tr ("Scale")))); + menu_entries.push_back (lay::menu_item ("cm_lay_move", "lay_move:edit_mode", at, tl::to_string (QObject::tr ("Move By")))); + menu_entries.push_back (lay::separator ("cellop_group", at)); + menu_entries.push_back (lay::menu_item ("cm_lay_convert_to_static", "lay_convert_to_static:edit_mode", at, tl::to_string (QObject::tr ("Convert All Cells To Static")))); + } + + menu_entries.push_back (lay::submenu ("cell_menu:edit:edit_mode", at, tl::to_string (QObject::tr ("Cell")))); + { + std::string at = "edit_menu.cell_menu.end"; + menu_entries.push_back (lay::menu_item ("cm_new_cell", "new_cell:edit:edit_mode", at, tl::to_string (QObject::tr ("New Cell")))); + menu_entries.push_back (lay::menu_item ("cm_cell_delete", "delete_cell:edit:edit_mode", at, tl::to_string (QObject::tr ("Delete Cell")))); + menu_entries.push_back (lay::menu_item ("cm_cell_rename", "rename_cell:edit:edit_mode", at, tl::to_string (QObject::tr ("Rename Cell")))); + menu_entries.push_back (lay::menu_item ("cm_cell_replace", "replace_cell:edit:edit_mode", at, tl::to_string (QObject::tr ("Replace Cell")))); + menu_entries.push_back (lay::menu_item ("cm_cell_flatten", "flatten_cell:edit:edit_mode", at, tl::to_string (QObject::tr ("Flatten Cell")))); + menu_entries.push_back (lay::separator ("ops_group", at)); + menu_entries.push_back (lay::menu_item ("cm_adjust_origin", "adjust_cell_origin:edit:edit_mode", at, tl::to_string (QObject::tr ("Adjust Origin")))); + menu_entries.push_back (lay::menu_item ("cm_cell_convert_to_static", "convert_cell_to_static:edit_mode", at, tl::to_string (QObject::tr ("Convert Cell To Static")))); + menu_entries.push_back (lay::separator ("props_group", at)); + menu_entries.push_back (lay::menu_item ("cm_cell_user_properties", "user_properties", at, tl::to_string (QObject::tr ("User Properties")))); + } + + menu_entries.push_back (lay::submenu ("layer_menu:edit:edit_mode", at, tl::to_string (QObject::tr ("Layer")))); + { + std::string at = "edit_menu.layer_menu.end"; + menu_entries.push_back (lay::menu_item ("cm_new_layer", "new_layer:edit:edit_mode", at, tl::to_string (QObject::tr ("New Layer")))); + menu_entries.push_back (lay::menu_item ("cm_clear_layer", "clear_layer:edit:edit_mode", at, tl::to_string (QObject::tr ("Clear Layer")))); + menu_entries.push_back (lay::menu_item ("cm_delete_layer", "delete_layer:edit:edit_mode", at, tl::to_string (QObject::tr ("Delete Layer")))); + menu_entries.push_back (lay::menu_item ("cm_copy_layer", "copy_layer:edit:edit_mode", at, tl::to_string (QObject::tr ("Copy Layer")))); + menu_entries.push_back (lay::menu_item ("cm_edit_layer", "edit_layer:edit:edit_mode", at, tl::to_string (QObject::tr ("Edit Layer Specification")))); + } + + menu_entries.push_back (lay::submenu ("selection_menu:edit", at, tl::to_string (QObject::tr ("Selection")))); + { + std::string at = "edit_menu.selection_menu.end"; + menu_entries.push_back (lay::menu_item ("cm_sel_flip_x", "sel_flip_x", at, tl::to_string (QObject::tr ("Flip Horizontally")))); + menu_entries.push_back (lay::menu_item ("cm_sel_flip_y", "sel_flip_y", at, tl::to_string (QObject::tr ("Flip Vertically")))); + menu_entries.push_back (lay::menu_item ("cm_sel_rot_cw", "sel_rot_cw", at, tl::to_string (QObject::tr ("Rotate Clockwise")))); + menu_entries.push_back (lay::menu_item ("cm_sel_rot_ccw", "sel_rot_ccw", at, tl::to_string (QObject::tr ("Rotate Counterclockwise")))); + menu_entries.push_back (lay::menu_item ("cm_sel_free_rot", "sel_free_rot", at, tl::to_string (QObject::tr ("Rotation By Angle")))); + menu_entries.push_back (lay::menu_item ("cm_sel_scale", "sel_scale", at, tl::to_string (QObject::tr ("Scale")))); + menu_entries.push_back (lay::menu_item ("cm_sel_move", "sel_move", at, tl::to_string (QObject::tr ("Move By")))); + menu_entries.push_back (lay::menu_item ("cm_sel_move_to", "sel_move_to", at, tl::to_string (QObject::tr ("Move To")))); + } + + menu_entries.push_back (lay::separator ("utils_group", at)); + menu_entries.push_back (lay::submenu ("utils_menu:edit:edit_mode", at, tl::to_string (QObject::tr ("Utilities")))); + + menu_entries.push_back (lay::separator ("misc_group", at)); + menu_entries.push_back (lay::menu_item ("cm_delete", "delete:edit", at, tl::to_string (QObject::tr ("Delete(Del)")))); + menu_entries.push_back (lay::menu_item ("cm_show_properties", "show_properties:edit", at, tl::to_string (QObject::tr ("Properties(Q)")))); + + menu_entries.push_back (lay::separator ("cpc_group", at)); + menu_entries.push_back (lay::menu_item ("cm_copy", "copy:edit", at, tl::to_string (QObject::tr ("Copy(Ctrl+C)")))); + menu_entries.push_back (lay::menu_item ("cm_cut", "cut:edit", at, tl::to_string (QObject::tr ("Cut(Ctrl+X)")))); + menu_entries.push_back (lay::menu_item ("cm_paste", "paste:edit", at, tl::to_string (QObject::tr ("Paste(Ctrl+V)")))); + menu_entries.push_back (lay::menu_item ("cm_duplicate", "duplicate:edit", at, tl::to_string (QObject::tr ("Duplicate(Ctrl+B)")))); + + menu_entries.push_back (lay::separator ("modes_group", at)); + menu_entries.push_back (lay::submenu ("mode_menu", at, tl::to_string (QObject::tr ("Mode")))); + + menu_entries.push_back (lay::submenu ("select_menu", at, tl::to_string (QObject::tr ("Select")))); + { + std::string at = "edit_menu.select_menu.end"; + menu_entries.push_back (lay::menu_item ("cm_select_all", "select_all", at, tl::to_string (QObject::tr ("Select All")))); + menu_entries.push_back (lay::menu_item ("cm_unselect_all", "unselect_all", at, tl::to_string (QObject::tr ("Unselect All")))); + menu_entries.push_back (lay::separator ("edit_select_basic_group", at)); + menu_entries.push_back (lay::menu_item ("lv:enable_all", "enable_all", at, tl::to_string (QObject::tr ("Enable All")))); + menu_entries.push_back (lay::menu_item ("lv:disable_all", "disable_all", at, tl::to_string (QObject::tr ("Disable All")))); + menu_entries.push_back (lay::separator ("edit_select_individual_group", at)); + }; + + menu_entries.push_back (lay::separator ("cancel_group", at)); + menu_entries.push_back (lay::menu_item ("cm_cancel", "cancel", at, tl::to_string (QObject::tr ("Cancel(Esc)")))); + + at = "bookmark_menu.end"; + menu_entries.push_back (lay::submenu ("goto_bookmark_menu", at, tl::to_string (QObject::tr ("Goto Bookmark")))); + menu_entries.push_back (lay::menu_item ("cm_bookmark_view", "bookmark_view", at, tl::to_string (QObject::tr ("Bookmark This View")))); + + menu_entries.push_back (lay::separator ("bookmark_mgm_group", at)); + menu_entries.push_back (lay::menu_item ("cm_manage_bookmarks", "manage_bookmarks", at, tl::to_string (QObject::tr ("Manage Bookmarks")))); + menu_entries.push_back (lay::menu_item ("cm_load_bookmarks", "load_bookmarks", at, tl::to_string (QObject::tr ("Load Bookmarks")))); + menu_entries.push_back (lay::menu_item ("cm_save_bookmarks", "save_bookmarks", at, tl::to_string (QObject::tr ("Save Bookmarks")))); + menu_entries.push_back (lay::submenu ("open_recent_menu_bookmarks", at, tl::to_string (QObject::tr ("Recent Bookmark Files")))); + + at = "zoom_menu.end"; + menu_entries.push_back (lay::submenu ("global_trans", at, tl::to_string (QObject::tr ("Global Transformation")))); + { + std::string at = "zoom_menu.global_trans.end"; + menu_entries.push_back (lay::config_menu_item ("r0", at, tl::to_string (QObject::tr ("\\(r0\\)<:/r0.png>")), cfg_global_trans, "?r0 *1 0,0")); + menu_entries.push_back (lay::config_menu_item ("r90", at, tl::to_string (QObject::tr ("\\(r90\\)<:/r90.png>")), cfg_global_trans, "?r90 *1 0,0")); + menu_entries.push_back (lay::config_menu_item ("r180", at, tl::to_string (QObject::tr ("\\(r180\\)<:/r180.png>")), cfg_global_trans, "?r180 *1 0,0")); + menu_entries.push_back (lay::config_menu_item ("r270", at, tl::to_string (QObject::tr ("\\(r270\\)<:/r270.png>")), cfg_global_trans, "?r270 *1 0,0")); + menu_entries.push_back (lay::config_menu_item ("m0", at, tl::to_string (QObject::tr ("\\(m0\\)<:/m0.png>")), cfg_global_trans, "?m0 *1 0,0")); + menu_entries.push_back (lay::config_menu_item ("m45", at, tl::to_string (QObject::tr ("\\(m45\\)<:/m45.png>")), cfg_global_trans, "?m45 *1 0,0")); + menu_entries.push_back (lay::config_menu_item ("m90", at, tl::to_string (QObject::tr ("\\(m90\\)<:/m90.png>")), cfg_global_trans, "?m90 *1 0,0")); + menu_entries.push_back (lay::config_menu_item ("m135", at, tl::to_string (QObject::tr ("\\(m135\\)<:/m135.png>")), cfg_global_trans, "?m135 *1 0,0")); + } + + menu_entries.push_back (lay::separator ("hier_group", at)); + menu_entries.push_back (lay::menu_item ("cm_max_hier", "max_hier", at, tl::to_string (QObject::tr ("Full Hierarchy(*)")))); + menu_entries.push_back (lay::menu_item ("cm_max_hier_0", "max_hier_0", at, tl::to_string (QObject::tr ("Box Only(0)")))); + menu_entries.push_back (lay::menu_item ("cm_max_hier_1", "max_hier_1", at, tl::to_string (QObject::tr ("Top Level Only(1)")))); + menu_entries.push_back (lay::menu_item ("cm_inc_max_hier", "inc_max_hier", at, tl::to_string (QObject::tr ("Increment Hierarchy(+)")))); + menu_entries.push_back (lay::menu_item ("cm_dec_max_hier", "dec_max_hier", at, tl::to_string (QObject::tr ("Decrement Hierarchy(-)")))); + + menu_entries.push_back (lay::separator ("zoom_group", at)); + menu_entries.push_back (lay::menu_item ("cm_zoom_fit", "zoom_fit", at, tl::to_string (QObject::tr ("Zoom Fit(F2)")))); + menu_entries.push_back (lay::menu_item ("cm_zoom_fit_sel", "zoom_fit_sel", at, tl::to_string (QObject::tr ("Zoom Fit Selection(Shift+F2)")))); + menu_entries.push_back (lay::menu_item ("cm_zoom_in", "zoom_in", at, tl::to_string (QObject::tr ("Zoom In(Ctrl++)")))); + menu_entries.push_back (lay::menu_item ("cm_zoom_out", "zoom_out", at, tl::to_string (QObject::tr ("Zoom Out(Ctrl+-)")))); + /* disabled because that interferes with the use of the arrow keys for moving the selection + MenuLayoutEntry::separator ("pan_group"); + menu_entries.push_back (lay::menu_item ("cm_pan_up", "pan_up", at, tl::to_string (QObject::tr ("Pan Up(Up)")))); + menu_entries.push_back (lay::menu_item ("cm_pan_down", "pan_down", at, tl::to_string (QObject::tr ("Pan Down(Down)")))); + menu_entries.push_back (lay::menu_item ("cm_pan_left", "pan_left", at, tl::to_string (QObject::tr ("Pan Left(Left)")))); + menu_entries.push_back (lay::menu_item ("cm_pan_right", "pan_right", at, tl::to_string (QObject::tr ("Pan Right(Right)")))); + */ + + menu_entries.push_back (lay::separator ("redraw_group", at)); + menu_entries.push_back (lay::menu_item ("cm_redraw", "redraw", at, tl::to_string (QObject::tr ("Redraw")))); + menu_entries.push_back (lay::separator ("state_group", at)); + menu_entries.push_back (lay::menu_item_copy ("cm_prev_display_state", "prev_display_state", at, "@toolbar.prev_display_state")); + menu_entries.push_back (lay::menu_item_copy ("cm_next_display_state", "next_display_state", at, "@toolbar.next_display_state")); + + menu_entries.push_back (lay::separator ("select_group", at)); + menu_entries.push_back (lay::menu_item ("cm_select_cell", "select_cell:edit", at, tl::to_string (QObject::tr ("Select Cell")))); + menu_entries.push_back (lay::menu_item ("cm_select_current_cell", "select_current_cell", at, tl::to_string (QObject::tr ("Show As New Top(Ctrl+S)")))); + menu_entries.push_back (lay::menu_item ("cm_goto_position", "goto_position", at, tl::to_string (QObject::tr ("Goto Position(Ctrl+G)")))); + + // Add a hook for inserting new items after the modes + menu_entries.push_back (lay::separator ("end_modes", "@toolbar.end")); + + } + + bool menu_activated (const std::string &symbol) const + { + if (symbol == "lv:enable_all") { + + for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { + cls->set_editable_enabled (true); + } + return true; + + } else if (symbol == "lv:disable_all") { + + for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { + cls->set_editable_enabled (false); + } + return true; + + } else { + return false; + } + } + + void implements_primary_mouse_modes (std::vector > > &modes) + { + std::vector mode_titles; + lay::LayoutView::intrinsic_mouse_modes (&mode_titles); + + int mode_id = 0; + for (std::vector ::const_iterator t = mode_titles.begin (); t != mode_titles.end (); ++t, --mode_id) { + // modes: pair(title, pair(insert_pos, id)) + modes.push_back (std::make_pair (*t, std::make_pair ("edit_menu.mode_menu.end;@toolbar.end_modes", mode_id))); + } + } + + lay::Plugin *create_plugin (db::Manager *manager, Dispatcher *, LayoutView *view) const + { + return new LayoutViewFunctions (manager, view); + } +}; + +static tl::RegisteredClass config_decl (new LayoutViewPluginDeclaration (), -10, "LayoutViewPlugin"); + +} // namespace lay diff --git a/src/laybasic/laybasic/layLayoutViewFunctions.h b/src/laybasic/laybasic/layLayoutViewFunctions.h new file mode 100644 index 000000000..15b2f3f56 --- /dev/null +++ b/src/laybasic/laybasic/layLayoutViewFunctions.h @@ -0,0 +1,171 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_layLayoutViewFunctions +#define HDR_layLayoutViewFunctions + +#include "laybasicCommon.h" + +#include "layPlugin.h" +#include "layDialogs.h" // For AlignCellOptions + +#include "dbTrans.h" +#include "dbLayerProperties.h" + +namespace lay { + +class LayoutView; + +/** + * @brief The layout view's functions implementation + */ +class LAYBASIC_PUBLIC LayoutViewFunctions + : public lay::Plugin +{ +public: + /** + * @brief Constructor + */ + LayoutViewFunctions (db::Manager *manager, lay::LayoutView *view); + + /** + * @brief Destructor + */ + ~LayoutViewFunctions (); + + // Plugin interface implementation + void menu_activated (const std::string &symbol); + + // menu callbacks + void cm_new_layer (); + void cm_clear_layer (); + void cm_delete_layer (); + void cm_copy_layer (); + void cm_align_cell_origin (); + void cm_edit_layer (); + void cm_lay_convert_to_static (); + void cm_lay_flip_x (); + void cm_lay_flip_y (); + void cm_lay_rot_cw (); + void cm_lay_rot_ccw (); + void cm_lay_free_rot (); + void cm_lay_scale (); + void cm_lay_move (); + void cm_sel_flip_x (); + void cm_sel_flip_y (); + void cm_sel_rot_cw (); + void cm_sel_rot_ccw (); + void cm_sel_free_rot (); + void cm_sel_scale (); + void cm_sel_move (); + void cm_sel_move_to (); + void cm_sel_move_interactive (); + + // forwarded to the layer control panel + void cm_new_tab (); + void cm_rename_tab (); + void cm_remove_tab (); + void cm_select_all (); + void cm_make_valid (); + void cm_make_invalid (); + void cm_hide (); + void cm_hide_all (); + void cm_show (); + void cm_show_all (); + void cm_show_only (); + void cm_rename (); + void cm_delete (); + void cm_insert (); + void cm_group (); + void cm_ungroup (); + void cm_source (); + void cm_sort_by_name (); + void cm_sort_by_ild (); + void cm_sort_by_idl (); + void cm_sort_by_ldi (); + void cm_sort_by_dli (); + void cm_regroup_by_index (); + void cm_regroup_by_datatype (); + void cm_regroup_by_layer (); + void cm_regroup_flatten (); + void cm_expand_all (); + void cm_add_missing (); + void cm_remove_unused (); + void cm_layer_copy (); + void cm_layer_cut (); + void cm_layer_paste (); + + // forwarded to the cell control panel + void cm_cell_user_properties (); + void cm_cell_flatten (); + void cm_cell_rename (); + void cm_cell_replace (); + void cm_cell_delete (); + void cm_cell_select (); + void cm_open_current_cell (); + void cm_cell_hide (); + void cm_cell_show (); + void cm_cell_show_all (); + void cm_cell_copy (); + void cm_cell_cut (); + void cm_cell_paste (); + void cm_cell_convert_to_static (); + +protected: + lay::LayoutView *view () + { + return mp_view; + } + + db::Manager *manager () + { + return mp_manager; + } + + void do_cm_duplicate (bool interactive); + void do_cm_paste (bool interactive); + void cm_new_cell (); + void cm_reload (); + + void do_transform (const db::DCplxTrans &tr); + void transform_layout (const db::DCplxTrans &tr); + +private: + lay::LayoutView *mp_view; + db::Manager *mp_manager; + db::LayerProperties m_new_layer_props; + db::DVector m_move_dist; + int m_move_to_origin_mode_x, m_move_to_origin_mode_y; + lay::AlignCellOptions m_align_cell_options; + int m_del_cell_mode; + int m_layer_hier_mode; + int m_duplicate_hier_mode; + bool m_clear_before; + int m_copy_cva, m_copy_cvr; + int m_copy_layera, m_copy_layerr; +}; + +} + +#endif + diff --git a/src/laybasic/laybasic/layPlugin.h b/src/laybasic/laybasic/layPlugin.h index 2b74177dd..df042c998 100644 --- a/src/laybasic/laybasic/layPlugin.h +++ b/src/laybasic/laybasic/layPlugin.h @@ -54,6 +54,7 @@ class ViewService; class Editable; class Drawing; class TechnologyComponentProvider; +class EditorOptionsPage; /** * @brief The base class for configuration pages @@ -234,7 +235,7 @@ public: { return std::vector > (); } - + /** * @brief The global configuration * @@ -317,7 +318,7 @@ public: } /** - * @brief Fetch the menu objects for this plugin + * @brief Fetches the menu objects for this plugin * * The implementation of this method is supposed to call the base * class'es "get_menu_entries" method and add it's own entries. @@ -328,7 +329,7 @@ public: } /** - * @brief Create a plugin object of the respective kind + * @brief Creates a plugin object of the respective kind * * This method may return 0 for "dummy" plugins that just register menu entries * or configuration options. @@ -339,7 +340,7 @@ public: } /** - * @brief Tell if the plugin implements the "lay::Editable" interface + * @brief Tells if the plugin implements the "lay::Editable" interface * * This method is supposed to return true if the plugin implements the * lay::Editable interface and wants to be listed as an "edit" mode in the @@ -350,9 +351,22 @@ public: { return false; } - + /** - * @brief Tell if the plugin implements a "lay::ViewService" active mouse mode + * @brief Gets the editor options pages + * + * Editor options pages are shown in the editor options dockable tool widget. Each plugin can create such pages + * and these will be shown in tabs inside this widget. + * + * The new pages are returned in the "pages" vector. The layout view will take ownership of these pages. + */ + virtual void get_editor_options_pages (std::vector & /*pages*/, lay::LayoutView * /*view*/, lay::Dispatcher * /*dispatcher*/) const + { + // .. no pages in the default implementation .. + } + + /** + * @brief Tells if the plugin implements a "lay::ViewService" active mouse mode * * This method is supposed to return true if the plugin implements the * lay::ViewService interface and wants to be listed as an mouse mode in the diff --git a/src/laybasic/laybasic/layProperties.h b/src/laybasic/laybasic/layProperties.h index 6c918b8c0..9eedcdafc 100644 --- a/src/laybasic/laybasic/layProperties.h +++ b/src/laybasic/laybasic/layProperties.h @@ -48,6 +48,8 @@ class Editable; class LAYBASIC_PUBLIC PropertiesPage : public QFrame { +Q_OBJECT + public: /** * @brief The constructor attaching the properties page to a parent widget @@ -140,7 +142,7 @@ public: */ virtual void operator++ () = 0; - /** + /** * @brief Update the display * * This method is called by the dialog to transfer data from the @@ -196,7 +198,7 @@ public: * If nothing was changed, the objects may be left untouched. * The dialog will start a transaction on the manager object. */ - virtual void apply_to_all () + virtual void apply_to_all (bool /*relative*/) { // default implementation is empty. } @@ -219,6 +221,12 @@ public: return mp_manager; } +signals: + /** + * @brief This signal is emitted if a value has been changed + */ + void edited (); + private: db::Manager *mp_manager; lay::Editable *mp_editable; diff --git a/src/laybasic/laybasic/layPropertiesDialog.cc b/src/laybasic/laybasic/layPropertiesDialog.cc index 31bdb36ce..cc40f5aba 100644 --- a/src/laybasic/laybasic/layPropertiesDialog.cc +++ b/src/laybasic/laybasic/layPropertiesDialog.cc @@ -35,7 +35,7 @@ namespace lay PropertiesDialog::PropertiesDialog (QWidget * /*parent*/, db::Manager *manager, lay::Editables *editables) : QDialog (0 /*parent*/), - mp_manager (manager), mp_editables (editables), m_index (-1) + mp_manager (manager), mp_editables (editables), m_index (-1), m_auto_applied (false), m_transaction_id (0) { mp_editables->enable_edits (false); @@ -49,6 +49,7 @@ PropertiesDialog::PropertiesDialog (QWidget * /*parent*/, db::Manager *manager, mp_properties_pages.push_back (e->properties_page (mp_manager, content_frame)); if (mp_properties_pages.back ()) { mp_stack->addWidget (mp_properties_pages.back ()); + connect (mp_properties_pages.back (), SIGNAL (edited ()), this, SLOT (apply ())); } } @@ -63,8 +64,8 @@ PropertiesDialog::PropertiesDialog (QWidget * /*parent*/, db::Manager *manager, content_frame->setLayout (mp_stack); // disable the apply button for first .. - apply_button->setEnabled (false); - apply_to_all_button->setEnabled (false); + apply_to_all_cbx->setEnabled (false); + relative_cbx->setEnabled (false); ok_button->setEnabled (false); // as a proposal, the start button can be enabled in most cases @@ -86,22 +87,28 @@ PropertiesDialog::PropertiesDialog (QWidget * /*parent*/, db::Manager *manager, // if at end disable the "Next" button and return (this may only happen at the first call) if (m_index >= int (mp_properties_pages.size ())) { + next_button->setEnabled (false); mp_stack->setCurrentWidget (dummy); - apply_button->setEnabled (false); - apply_to_all_button->setEnabled (false); + apply_to_all_cbx->setEnabled (false); + apply_to_all_cbx->setChecked (false); + relative_cbx->setEnabled (false); + relative_cbx->setChecked (false); ok_button->setEnabled (false); + } else { + next_button->setEnabled (any_next ()); mp_properties_pages [m_index]->update (); mp_stack->setCurrentWidget (mp_properties_pages [m_index]); - apply_button->setEnabled (! mp_properties_pages [m_index]->readonly ()); - apply_to_all_button->setEnabled (! mp_properties_pages [m_index]->readonly () && mp_properties_pages [m_index]->can_apply_to_all ()); + apply_to_all_cbx->setEnabled (! mp_properties_pages [m_index]->readonly () && mp_properties_pages [m_index]->can_apply_to_all ()); + apply_to_all_cbx->setChecked (false); + relative_cbx->setEnabled (apply_to_all_cbx->isEnabled () && apply_to_all_cbx->isChecked ()); + relative_cbx->setChecked (true); ok_button->setEnabled (! mp_properties_pages [m_index]->readonly ()); + } - connect (apply_button, SIGNAL (clicked ()), this, SLOT (apply_pressed ())); - connect (apply_to_all_button, SIGNAL (clicked ()), this, SLOT (apply_to_all_pressed ())); connect (ok_button, SIGNAL (clicked ()), this, SLOT (ok_pressed ())); connect (cancel_button, SIGNAL (clicked ()), this, SLOT (cancel_pressed ())); connect (prev_button, SIGNAL (clicked ()), this, SLOT (prev_pressed ())); @@ -130,8 +137,9 @@ PropertiesDialog::next_pressed () BEGIN_PROTECTED if (! mp_properties_pages [m_index]->readonly ()) { - db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes"))); + db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); mp_properties_pages [m_index]->apply (); + m_transaction_id = t.id (); } // advance the current entry @@ -157,8 +165,8 @@ BEGIN_PROTECTED prev_button->setEnabled (true); next_button->setEnabled (any_next ()); - apply_button->setEnabled (! mp_properties_pages [m_index]->readonly ()); - apply_to_all_button->setEnabled (! mp_properties_pages [m_index]->readonly () && mp_properties_pages [m_index]->can_apply_to_all ()); + apply_to_all_cbx->setEnabled (! mp_properties_pages [m_index]->readonly () && mp_properties_pages [m_index]->can_apply_to_all ()); + relative_cbx->setEnabled (apply_to_all_cbx->isEnabled () && apply_to_all_cbx->isChecked ()); ok_button->setEnabled (! mp_properties_pages [m_index]->readonly ()); mp_properties_pages [m_index]->update (); @@ -171,8 +179,9 @@ PropertiesDialog::prev_pressed () BEGIN_PROTECTED if (! mp_properties_pages [m_index]->readonly ()) { - db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes"))); + db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); mp_properties_pages [m_index]->apply (); + m_transaction_id = t.id (); } if (mp_properties_pages [m_index]->at_begin ()) { @@ -200,8 +209,8 @@ BEGIN_PROTECTED next_button->setEnabled (true); prev_button->setEnabled (any_prev ()); - apply_button->setEnabled (! mp_properties_pages [m_index]->readonly ()); - apply_to_all_button->setEnabled (! mp_properties_pages [m_index]->readonly () && mp_properties_pages [m_index]->can_apply_to_all ()); + apply_to_all_cbx->setEnabled (! mp_properties_pages [m_index]->readonly () && mp_properties_pages [m_index]->can_apply_to_all ()); + relative_cbx->setEnabled (apply_to_all_cbx->isEnabled () && apply_to_all_cbx->isChecked ()); ok_button->setEnabled (! mp_properties_pages [m_index]->readonly ()); mp_properties_pages [m_index]->update (); @@ -251,28 +260,27 @@ PropertiesDialog::any_prev () const return (index >= 0); } -void -PropertiesDialog::apply_to_all_pressed () +void +PropertiesDialog::apply () { BEGIN_PROTECTED - db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes to all"))); + db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Auto-apply changes")), m_transaction_id); - mp_properties_pages [m_index]->apply_to_all (); - mp_properties_pages [m_index]->update (); + try { -END_PROTECTED -} + if (apply_to_all_cbx->isChecked () && mp_properties_pages [m_index]->can_apply_to_all ()) { + mp_properties_pages [m_index]->apply_to_all (relative_cbx->isChecked ()); + } else { + mp_properties_pages [m_index]->apply (); + } + mp_properties_pages [m_index]->update (); -void -PropertiesDialog::apply_pressed () -{ -BEGIN_PROTECTED + } catch (tl::Exception &) { + // we assume the page somehow indicates the error and does not apply the values + } - db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes"))); - - mp_properties_pages [m_index]->apply (); - mp_properties_pages [m_index]->update (); + m_transaction_id = t.id (); END_PROTECTED } @@ -280,6 +288,17 @@ END_PROTECTED void PropertiesDialog::cancel_pressed () { + // undo whatever we've done so far + if (m_transaction_id > 0) { + + // because undo does not maintain a valid selection we clear it + mp_editables->clear_selection (); + + mp_manager->undo (); + m_transaction_id = 0; + + } + // make sure that the property pages are no longer used .. disconnect (); // close the dialog @@ -293,11 +312,13 @@ BEGIN_PROTECTED if (! mp_properties_pages [m_index]->readonly ()) { - db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes"))); + db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); mp_properties_pages [m_index]->apply (); mp_properties_pages [m_index]->update (); + m_transaction_id = t.id (); + } // make sure that the property pages are no longer used .. diff --git a/src/laybasic/laybasic/layPropertiesDialog.h b/src/laybasic/laybasic/layPropertiesDialog.h index c06efcb69..26a342dad 100644 --- a/src/laybasic/laybasic/layPropertiesDialog.h +++ b/src/laybasic/laybasic/layPropertiesDialog.h @@ -32,12 +32,11 @@ #include #include +#include + #include "ui_PropertiesDialog.h" -namespace db -{ - class Manager; -} +#include class QStackedLayout; @@ -74,13 +73,15 @@ public: ~PropertiesDialog (); private: - std::vector mp_properties_pages; + std::vector mp_properties_pages; db::Manager *mp_manager; lay::Editables *mp_editables; int m_index; QStackedLayout *mp_stack; lay::MainWindow *mp_mw; size_t m_objects, m_current_object; + bool m_auto_applied; + db::Manager::transaction_id_t m_transaction_id; void disconnect (); bool any_prev () const; @@ -88,10 +89,9 @@ private: void update_title (); public slots: + void apply (); void next_pressed (); void prev_pressed (); - void apply_pressed (); - void apply_to_all_pressed (); void cancel_pressed (); void ok_pressed (); diff --git a/src/laybasic/laybasic/layQtTools.cc b/src/laybasic/laybasic/layQtTools.cc index 4d174ade4..96d44fe05 100644 --- a/src/laybasic/laybasic/layQtTools.cc +++ b/src/laybasic/laybasic/layQtTools.cc @@ -31,6 +31,7 @@ #include #include #include +#include #include @@ -159,5 +160,24 @@ restore_dialog_state (QWidget *dialog, const std::string &s, bool with_section_s } } +void +indicate_error (QWidget *le, const tl::Exception *ex) +{ + // by the way, update the foreground color of the cell edit box as well (red, if not valid) + QPalette pl = le->palette (); + if (ex) { + pl.setColor (QPalette::Active, QPalette::Text, Qt::red); + pl.setColor (QPalette::Active, QPalette::Base, QColor (Qt::red).lighter (180)); + le->setToolTip (tl::to_qstring (ex->msg ())); + } else { + QWidget *pw = dynamic_cast (le->parent ()); + tl_assert (pw != 0); + pl.setColor (QPalette::Active, QPalette::Text, pw->palette ().color (QPalette::Text)); + pl.setColor (QPalette::Active, QPalette::Base, pw->palette ().color (QPalette::Base)); + le->setToolTip (QString ()); + } + le->setPalette (pl); +} + } diff --git a/src/laybasic/laybasic/layQtTools.h b/src/laybasic/laybasic/layQtTools.h index 36afc666c..49232c9d6 100644 --- a/src/laybasic/laybasic/layQtTools.h +++ b/src/laybasic/laybasic/layQtTools.h @@ -32,6 +32,11 @@ class QLabel; class QWidget; class QObject; +namespace tl +{ + class Exception; +} + namespace lay { @@ -62,6 +67,14 @@ LAYBASIC_PUBLIC void activate_modal_help_links (QLabel *label); */ LAYBASIC_PUBLIC void register_help_handler (QObject *object, const char *slot, const char *modal_slot); +/** + * @brief Configures a QLineEdit or other widget to indicate an error + * + * When a non-null ex pointer is passed, the background will be turned red + * and the exception's text will be used as tooltip. Use this function with + * a null ex pointer to clear the error condition. + */ +LAYBASIC_PUBLIC void indicate_error (QWidget *le, const tl::Exception *ex); } // namespace lay diff --git a/src/laybasic/laybasic/laySelector.cc b/src/laybasic/laybasic/laySelector.cc index ffe76e411..d1e5d1425 100644 --- a/src/laybasic/laybasic/laySelector.cc +++ b/src/laybasic/laybasic/laySelector.cc @@ -74,10 +74,7 @@ void SelectionService::deactivated () { mp_view->clear_transient_selection (); - if (mp_box) { - delete mp_box; - mp_box = 0; - } + reset_box (); } void @@ -102,7 +99,20 @@ SelectionService::timeout () mp_view->transient_select (m_hover_point); } -bool +void +SelectionService::reset_box () +{ + if (mp_box) { + + widget ()->ungrab_mouse (this); + + delete mp_box; + mp_box = 0; + + } +} + +bool SelectionService::wheel_event (int /*delta*/, bool /*horizonal*/, const db::DPoint & /*p*/, unsigned int /*buttons*/, bool /*prio*/) { return false; @@ -115,19 +125,31 @@ SelectionService::enter_event (bool /*prio*/) return false; } -bool -SelectionService::leave_event (bool /*prio*/) +bool +SelectionService::leave_event (bool prio) { m_mouse_in_window = false; + hover_reset (); + + if (prio) { + reset_box (); + } + return false; } bool -SelectionService::mouse_move_event (const db::DPoint &p, unsigned int /*buttons*/, bool prio) +SelectionService::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) { if (prio) { + m_current_position = p; + + if ((buttons & LeftButton) == 0) { + reset_box (); + } + if (mp_box) { m_p2 = p; mp_box->set_points (m_p1, m_p2); @@ -138,6 +160,7 @@ SelectionService::mouse_move_event (const db::DPoint &p, unsigned int /*buttons* } } + return false; } @@ -145,6 +168,11 @@ bool SelectionService::mouse_double_click_event (const db::DPoint & /*p*/, unsigned int buttons, bool prio) { hover_reset (); + + if (prio) { + reset_box (); + } + if (prio && (buttons & lay::LeftButton) != 0) { mp_view->show_properties (QApplication::activeWindow ()); return true; @@ -157,11 +185,18 @@ bool SelectionService::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio) { hover_reset (); - if (prio && ! mp_box && (buttons & lay::LeftButton) != 0) { - mp_view->stop_redraw (); // TODO: how to restart if selection is aborted? - m_buttons = buttons; - begin (p); - return true; + + if (prio) { + + reset_box (); + + if ((buttons & lay::LeftButton) != 0) { + mp_view->stop_redraw (); // TODO: how to restart if selection is aborted? + m_buttons = buttons; + begin (p); + return true; + } + } return false; @@ -170,7 +205,11 @@ SelectionService::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool SelectionService::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio) { - if (prio && mp_view && widget ()->mouse_event_viewport ().contains (p) && (buttons & lay::LeftButton) != 0) { + if (prio) { + reset_box (); + } + + if (prio && mp_view && widget ()->mouse_event_viewport ().contains (p) && (buttons & lay::LeftButton) != 0) { lay::Editable::SelectionMode mode = lay::Editable::Replace; bool shift = ((buttons & lay::ShiftButton) != 0); @@ -211,12 +250,10 @@ bool SelectionService::mouse_release_event (const db::DPoint & /*p*/, unsigned int /*buttons*/, bool prio) { hover_reset (); + if (prio && mp_box) { - widget ()->ungrab_mouse (this); - - delete mp_box; - mp_box = 0; + reset_box (); if (mp_view) { diff --git a/src/laybasic/laybasic/laySelector.h b/src/laybasic/laybasic/laySelector.h index ddf60c6f8..e302d70f4 100644 --- a/src/laybasic/laybasic/laySelector.h +++ b/src/laybasic/laybasic/laySelector.h @@ -25,6 +25,8 @@ #ifndef HDR_laySelector #define HDR_laySelector +#include "laybasicCommon.h" + #include "layViewObject.h" #include "layEditable.h" @@ -37,7 +39,7 @@ class RubberBox; class LayoutView; class LayoutCanvas; -class SelectionService +class LAYBASIC_PUBLIC SelectionService : public QObject, public lay::ViewService { @@ -77,6 +79,7 @@ private: virtual void deactivated (); db::DPoint m_p1, m_p2; + db::DPoint m_current_position; lay::LayoutView *mp_view; lay::RubberBox *mp_box; unsigned int m_color; @@ -86,6 +89,8 @@ private: bool m_hover_wait; db::DPoint m_hover_point; bool m_mouse_in_window; + + void reset_box (); }; } diff --git a/src/laybasic/laybasic/laySnap.cc b/src/laybasic/laybasic/laySnap.cc index 04de10a99..841468c63 100644 --- a/src/laybasic/laybasic/laySnap.cc +++ b/src/laybasic/laybasic/laySnap.cc @@ -726,7 +726,7 @@ private: bool m_directed; }; -static std::pair +static PointSnapToObjectResult do_obj_snap (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, double snap_range, const std::vector &cutlines) { db::DPoint dp (pt); @@ -753,25 +753,45 @@ do_obj_snap (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &gri } } - if (finder.any () && anyp) { - // if both the projection and the finder are sucessful, decide by a heuristic criterion which to take - // (the projection gets a penalty (of the snap range) to make it count less than the finder's choice) - // This avoids extreme distortions of the ruler due to projection on long edges. - if ((dp.distance (closest) + snap_range) * 5.0 < dp.distance (finder.get_found ())) { - return std::make_pair (false, closest); - } else { - return std::make_pair (true, finder.get_found ()); - } + // if both the projection and the finder are sucessful, decide by a heuristic criterion which to take + // (the projection gets a penalty (of the snap range) to make it count less than the finder's choice) + // This avoids extreme distortions of the ruler due to projection on long edges. + if (finder.any () && anyp && (dp.distance (closest) + snap_range) * 5.0 < dp.distance (finder.get_found ())) { + + PointSnapToObjectResult res; + res.snapped_point = closest; + return res; + } else if (finder.any ()) { - return std::make_pair (true, finder.get_found ()); + + PointSnapToObjectResult res; + res.snapped_point = finder.get_found (); + res.object_ref = finder.get_found_edge (); + if (finder.is_vertex ()) { + res.object_snap = PointSnapToObjectResult::ObjectVertex; + } else if (finder.has_found_edge ()) { + res.object_snap = PointSnapToObjectResult::ObjectEdge; + } else { + res.object_snap = PointSnapToObjectResult::ObjectUnspecific; + } + return res; + } else if (anyp) { - return std::make_pair (false, closest); + + PointSnapToObjectResult res; + res.snapped_point = closest; + return res; + } else { - return std::make_pair (false, dp); + + PointSnapToObjectResult res; + res.snapped_point = dp; + return res; + } } -std::pair +static TwoPointSnapToObjectResult do_obj_snap2 (lay::LayoutView *view, const db::DPoint &pt1, const db::DPoint &pt2, const db::DVector &grid, double min_search_range, double max_search_range, const std::vector &cutlines) { db::DPoint dp1 (pt1); @@ -828,15 +848,41 @@ do_obj_snap2 (lay::LayoutView *view, const db::DPoint &pt1, const db::DPoint &pt finder2.find (view, sr2); if (finder2.any_exact ()) { + db::DPoint p2 = finder2.get_found (); - return std::make_pair (true, db::DEdge (p1, p2)); + + TwoPointSnapToObjectResult res; + res.any = true; + res.first = p1; + res.second = p2; + + res.object_ref_first = finder.get_found_edge (); + if (finder.is_vertex ()) { + res.object_snap_first = TwoPointSnapToObjectResult::ObjectVertex; + } else if (finder.has_found_edge ()) { + res.object_snap_first = TwoPointSnapToObjectResult::ObjectEdge; + } else { + res.object_snap_first = TwoPointSnapToObjectResult::ObjectUnspecific; + } + + res.object_ref_second = finder2.get_found_edge (); + if (finder2.is_vertex ()) { + res.object_snap_second = TwoPointSnapToObjectResult::ObjectVertex; + } else if (finder2.has_found_edge ()) { + res.object_snap_second = TwoPointSnapToObjectResult::ObjectEdge; + } else { + res.object_snap_second = TwoPointSnapToObjectResult::ObjectUnspecific; + } + + return res; + } sr2 *= 2.0; } - return std::make_pair (false, db::DEdge ()); + return TwoPointSnapToObjectResult (); } @@ -844,7 +890,7 @@ do_obj_snap2 (lay::LayoutView *view, const db::DPoint &pt1, const db::DPoint &pt } - return std::make_pair (false, db::DEdge ()); + return TwoPointSnapToObjectResult (); } static void @@ -868,13 +914,13 @@ make_cutlines (lay::angle_constraint_type snap_mode, const db::DPoint &p1, std:: } } -std::pair +PointSnapToObjectResult obj_snap (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, double snap_range) { return do_obj_snap (view, pt, grid, snap_range, std::vector ()); } -std::pair +PointSnapToObjectResult obj_snap (lay::LayoutView *view, const db::DPoint &p1, const db::DPoint &p2, const db::DVector &grid, lay::angle_constraint_type snap_mode, double snap_range) { std::vector cutlines; @@ -882,19 +928,19 @@ obj_snap (lay::LayoutView *view, const db::DPoint &p1, const db::DPoint &p2, con return do_obj_snap (view, p2, grid, snap_range, cutlines); } -std::pair +TwoPointSnapToObjectResult obj_snap2 (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, double min_search_range, double max_search_range) { return obj_snap2 (view, pt, pt, grid, min_search_range, max_search_range); } -std::pair +TwoPointSnapToObjectResult obj_snap2 (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, lay::angle_constraint_type ac, double min_search_range, double max_search_range) { return obj_snap2 (view, pt, pt, grid, ac, min_search_range, max_search_range); } -std::pair +TwoPointSnapToObjectResult obj_snap2 (lay::LayoutView *view, const db::DPoint &pt1, const db::DPoint &pt2, const db::DVector &grid, double min_search_range, double max_search_range) { db::DPoint dp1 = lay::snap_xy (pt1, grid); @@ -903,7 +949,7 @@ obj_snap2 (lay::LayoutView *view, const db::DPoint &pt1, const db::DPoint &pt2, return do_obj_snap2 (view, dp1, dp2, db::DVector (), min_search_range, max_search_range, std::vector ()); } -std::pair +TwoPointSnapToObjectResult obj_snap2 (lay::LayoutView *view, const db::DPoint &pt1, const db::DPoint &pt2, const db::DVector &grid, lay::angle_constraint_type snap_mode, double min_search_range, double max_search_range) { db::DPoint dp1 = lay::snap_xy (pt1, grid); diff --git a/src/laybasic/laybasic/laySnap.h b/src/laybasic/laybasic/laySnap.h index 8ac286b24..b2a1b3645 100644 --- a/src/laybasic/laybasic/laySnap.h +++ b/src/laybasic/laybasic/laySnap.h @@ -108,6 +108,36 @@ namespace lay */ LAYBASIC_PUBLIC std::pair snap (const db::DPoint &p1, const db::DPoint &p2, db::DCoord grid); + /** + * @brief A structure describing the snap result for a point-wise snap + */ + struct LAYBASIC_PUBLIC PointSnapToObjectResult + { + enum ObjectSnap { + NoObject = 0, + ObjectVertex, + ObjectEdge, + ObjectUnspecific + }; + + PointSnapToObjectResult () : object_snap (NoObject) { } + + /** + * @brief The result of the snap + */ + db::DPoint snapped_point; + + /** + * @brief Indicates whether and how the point was snapped to an object + */ + ObjectSnap object_snap; + + /** + * @brief Indicates the edge the point was snapped against unless in NoObject mode + */ + db::DEdge object_ref; + }; + /** * @brief combined grid-, projection- and object snapping provided to implementing "magnetic features" * @@ -115,17 +145,14 @@ namespace lay * to an object edge or point. It will use a heuristics to determine * what criterion is applied if a conflict is detected. * - * The function will return a pair of the "stiffness" flag and the resulting point. The - * "stiffness" is a measure if the result point can be moved if another snap will be applied. - * It is true if the point is not intended to be moved further, i.e. because it snapped to - * a grid point or a object vertex. + * The function will return a PointSnapToObjectResult object. * * @param view The layout view used for object snapping. Can be 0 to disable object snapping * @param pt The point to snap * @param grid Either (0,0) to disable grid snapping or a (gx,gy) value for the (potentially anisotropic grid) * @param snap_range The search range for objects */ - LAYBASIC_PUBLIC std::pair obj_snap (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, double snap_range); + LAYBASIC_PUBLIC PointSnapToObjectResult obj_snap (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, double snap_range); /** * @brief combined grid-, projection- and object snapping provided to implementing "magnetic features" @@ -133,7 +160,45 @@ namespace lay * This is a convenience method that creates the projection axes from a reference point and an angle mode. * "pr" is the reference point, "pt" is the point to snap. */ - LAYBASIC_PUBLIC std::pair obj_snap (lay::LayoutView *view, const db::DPoint &pr, const db::DPoint &pt, const db::DVector &grid, lay::angle_constraint_type ac, double snap_range); + LAYBASIC_PUBLIC PointSnapToObjectResult obj_snap (lay::LayoutView *view, const db::DPoint &pr, const db::DPoint &pt, const db::DVector &grid, lay::angle_constraint_type ac, double snap_range); + + /** + * @brief A structure describing the snap result for a two-sided object snap (distance measurement) + */ + struct LAYBASIC_PUBLIC TwoPointSnapToObjectResult + { + enum ObjectSnap { + NoObject = 0, + ObjectVertex, + ObjectEdge, + ObjectUnspecific + }; + + TwoPointSnapToObjectResult () : any (false), object_snap_first (NoObject), object_snap_second (NoObject) { } + + /** + * @brief Indicates whether the two-sided snap was successful + */ + bool any; + + /** + * @brief The result of the snap + * Two values are provided for the first and second snap. + */ + db::DPoint first, second; + + /** + * @brief Indicates whether and how the point was snapped to an object + * Two values are provided for the first and second snap. + */ + ObjectSnap object_snap_first, object_snap_second; + + /** + * @brief Indicates the edge the point was snapped against unless in NoObject mode + * Two values are provided for the first and second snap. + */ + db::DEdge object_ref_first, object_ref_second; + }; /** * @brief Same than obj_snap, but delivers two points on two opposite sides of the initial point @@ -141,7 +206,7 @@ namespace lay * This method basically implements "auto measure". The first value of the returned pair * is true if such an edge could be found. Otherwise it's false. */ - LAYBASIC_PUBLIC std::pair obj_snap2 (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, double min_search_range, double max_search_range); + LAYBASIC_PUBLIC TwoPointSnapToObjectResult obj_snap2 (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, double min_search_range, double max_search_range); /** * @brief Same than obj_snap, but delivers two points on two opposite sides of the initial points @@ -151,14 +216,14 @@ namespace lay * * This version accepts two points defining different search regions for first and second edge. */ - LAYBASIC_PUBLIC std::pair obj_snap2 (lay::LayoutView *view, const db::DPoint &pt1, const db::DPoint &pt2, const db::DVector &grid, double min_search_range, double max_search_range); + LAYBASIC_PUBLIC TwoPointSnapToObjectResult obj_snap2 (lay::LayoutView *view, const db::DPoint &pt1, const db::DPoint &pt2, const db::DVector &grid, double min_search_range, double max_search_range); /** * @brief Same than the previous obj_snap2, but allows specification of an angle constraint * * Measurements will be confined to the direction specified. */ - LAYBASIC_PUBLIC std::pair obj_snap2 (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, lay::angle_constraint_type ac, double min_search_range, double max_search_range); + LAYBASIC_PUBLIC TwoPointSnapToObjectResult obj_snap2 (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, lay::angle_constraint_type ac, double min_search_range, double max_search_range); /** * @brief Same than the previous obj_snap2, but allows specification of an angle constraint @@ -167,7 +232,7 @@ namespace lay * * This version accepts two points defining different search regions for first and second edge. */ - LAYBASIC_PUBLIC std::pair obj_snap2 (lay::LayoutView *view, const db::DPoint &pt1, const db::DPoint &pt2, const db::DVector &grid, lay::angle_constraint_type ac, double min_search_range, double max_search_range); + LAYBASIC_PUBLIC TwoPointSnapToObjectResult obj_snap2 (lay::LayoutView *view, const db::DPoint &pt1, const db::DPoint &pt2, const db::DVector &grid, lay::angle_constraint_type ac, double min_search_range, double max_search_range); /** * @brief Reduce a given vector according to the angle constraint diff --git a/src/laybasic/laybasic/layViewObject.cc b/src/laybasic/laybasic/layViewObject.cc index cc68a61f8..1ba1e5dfd 100644 --- a/src/laybasic/laybasic/layViewObject.cc +++ b/src/laybasic/laybasic/layViewObject.cc @@ -73,6 +73,10 @@ CellDragDropData::serialized () const stream << (quintptr) mp_library; stream << m_cell_index; stream << m_is_pcell; + stream << int (m_pcell_params.size ()); + for (std::vector::const_iterator i = m_pcell_params.begin (); i != m_pcell_params.end (); ++i) { + stream << tl::to_qstring (i->to_parsable_string ()); + } return data; } @@ -94,6 +98,19 @@ CellDragDropData::deserialize (const QByteArray &ba) mp_library = reinterpret_cast (p); stream >> m_cell_index; stream >> m_is_pcell; + + m_pcell_params.clear (); + int n = 0; + stream >> n; + while (n-- > 0) { + QString s; + stream >> s; + std::string stl_s = tl::to_string (s); + tl::Extractor ex (stl_s.c_str ()); + m_pcell_params.push_back (tl::Variant ()); + ex.read (m_pcell_params.back ()); + } + return true; } else { @@ -272,6 +289,7 @@ ViewObjectWidget::ViewObjectWidget (QWidget *parent, const char *name) m_mouse_pressed_state (false), m_mouse_buttons (0), m_in_mouse_move (false), + m_mouse_inside (false), m_cursor (lay::Cursor::none), m_default_cursor (lay::Cursor::none) { @@ -363,6 +381,14 @@ ViewObjectWidget::set_default_cursor (lay::Cursor::cursor_shape cursor) } } +void +ViewObjectWidget::ensure_entered () +{ + if (! m_mouse_inside) { + enterEvent (0); + } +} + void ViewObjectWidget::begin_mouse_event (lay::Cursor::cursor_shape cursor) { @@ -434,7 +460,7 @@ BEGIN_PROTECTED const DragDropDataBase *dd = get_drag_drop_data (event->mimeData ()); if (dd) { - db::DPoint p = m_trans.inverted () * db::DPoint (event->pos ().x (), height () - 1 - event->pos ().y ()); + db::DPoint p = pixel_to_um (event->pos ()); bool done = drag_enter_event (p, dd); service_iterator svc = begin_services (); @@ -479,7 +505,7 @@ BEGIN_PROTECTED const DragDropDataBase *dd = get_drag_drop_data (event->mimeData ()); if (dd) { - db::DPoint p = m_trans.inverted () * db::DPoint (event->pos ().x (), height () - 1 - event->pos ().y ()); + db::DPoint p = pixel_to_um (event->pos ()); bool done = drag_move_event (p, dd); service_iterator svc = begin_services (); @@ -503,7 +529,7 @@ BEGIN_PROTECTED const DragDropDataBase *dd = get_drag_drop_data (event->mimeData ()); if (dd) { - db::DPoint p = m_trans.inverted () * db::DPoint (event->pos ().x (), height () - 1 - event->pos ().y ()); + db::DPoint p = pixel_to_um (event->pos ()); bool done = drop_event (p, dd); service_iterator svc = begin_services (); @@ -523,6 +549,7 @@ void ViewObjectWidget::mouseMoveEvent (QMouseEvent *e) { BEGIN_PROTECTED + ensure_entered (); m_mouse_pos = e->pos (); m_mouse_buttons = qt_to_buttons (e->buttons (), e->modifiers ()); do_mouse_move (); @@ -543,7 +570,7 @@ ViewObjectWidget::do_mouse_move () bool done = false; - db::DPoint p = m_trans.inverted () * db::DPoint (m_mouse_pressed.x (), height () - 1 - m_mouse_pressed.y ()); + db::DPoint p = pixel_to_um (m_mouse_pressed); for (std::list::iterator g = m_grabbed.begin (); !done && g != m_grabbed.end (); ) { std::list::iterator gg = g; @@ -578,7 +605,7 @@ ViewObjectWidget::do_mouse_move () bool done = false; - db::DPoint p = m_trans.inverted () * db::DPoint (m_mouse_pos.x (), height () - 1 - m_mouse_pos.y ()); + db::DPoint p = pixel_to_um (m_mouse_pos); for (std::list::iterator g = m_grabbed.begin (); !done && g != m_grabbed.end (); ) { std::list::iterator gg = g; @@ -614,6 +641,7 @@ void ViewObjectWidget::mouseDoubleClickEvent (QMouseEvent *e) { BEGIN_PROTECTED + ensure_entered (); begin_mouse_event (lay::Cursor::none); setFocus (); @@ -626,7 +654,7 @@ BEGIN_PROTECTED unsigned int buttons = qt_to_buttons (e->buttons (), e->modifiers ()); - db::DPoint p = m_trans.inverted () * db::DPoint (e->pos ().x (), height () - 1 - e->pos ().y ()); + db::DPoint p = pixel_to_um (e->pos ()); for (std::list::iterator g = m_grabbed.begin (); !done && g != m_grabbed.end (); ) { std::list::iterator gg = g; @@ -659,6 +687,8 @@ void ViewObjectWidget::enterEvent (QEvent * /*event*/) { BEGIN_PROTECTED + m_mouse_inside = true; + begin_mouse_event (); bool done = false; @@ -723,12 +753,15 @@ BEGIN_PROTECTED end_mouse_event (); END_PROTECTED + + m_mouse_inside = false; } void ViewObjectWidget::wheelEvent (QWheelEvent *e) { BEGIN_PROTECTED + ensure_entered (); begin_mouse_event (); e->ignore (); @@ -738,7 +771,7 @@ BEGIN_PROTECTED unsigned int buttons = qt_to_buttons (e->buttons (), e->modifiers ()); bool horizonal = (e->orientation () == Qt::Horizontal); - db::DPoint p = m_trans.inverted () * db::DPoint (e->pos ().x (), height () - 1 - e->pos ().y ()); + db::DPoint p = pixel_to_um (e->pos ()); for (std::list::iterator g = m_grabbed.begin (); !done && g != m_grabbed.end (); ) { std::list::iterator gg = g; @@ -770,6 +803,7 @@ END_PROTECTED void ViewObjectWidget::mousePressEvent (QMouseEvent *e) { + ensure_entered (); setFocus (); m_mouse_pos = e->pos (); @@ -784,12 +818,13 @@ void ViewObjectWidget::mouseReleaseEvent (QMouseEvent *e) { BEGIN_PROTECTED + ensure_entered (); begin_mouse_event (); bool done = false; m_mouse_pos = e->pos (); - db::DPoint p = m_trans.inverted () * db::DPoint (e->pos ().x (), height () - 1 - e->pos ().y ()); + db::DPoint p = pixel_to_um (e->pos ()); for (std::list::iterator g = m_grabbed.begin (); !done && g != m_grabbed.end (); ) { std::list::iterator gg = g; @@ -838,6 +873,12 @@ END_PROTECTED m_mouse_pressed_state = false; } +db::DPoint +ViewObjectWidget::pixel_to_um (const QPoint &pt) const +{ + return m_trans.inverted () * db::DPoint (pt.x (), height () - 1 - pt.y ()); +} + void ViewObjectWidget::mouse_event_trans (const db::DCplxTrans &trans) { diff --git a/src/laybasic/laybasic/layViewObject.h b/src/laybasic/laybasic/layViewObject.h index 816241d2b..c89fb65cd 100644 --- a/src/laybasic/laybasic/layViewObject.h +++ b/src/laybasic/laybasic/layViewObject.h @@ -130,8 +130,8 @@ public: * @param layout the layout where the cell lives in * @param cell_index The index of the cell */ - CellDragDropData (const db::Layout *layout, const db::Library *library, db::cell_index_type cell_or_pcell_index, bool is_pcell) - : mp_layout (layout), mp_library (library), m_cell_index (cell_or_pcell_index), m_is_pcell (is_pcell) + CellDragDropData (const db::Layout *layout, const db::Library *library, db::cell_index_type cell_or_pcell_index, bool is_pcell, const std::vector &pcell_params = std::vector ()) + : mp_layout (layout), mp_library (library), m_cell_index (cell_or_pcell_index), m_is_pcell (is_pcell), m_pcell_params (pcell_params) { // .. nothing yet .. } @@ -152,6 +152,14 @@ public: return mp_library; } + /** + * @brief PCell parameters + */ + const std::vector &pcell_params () const + { + return m_pcell_params; + } + /** * @brief Gets the index of the cell */ @@ -185,6 +193,7 @@ private: const db::Library *mp_library; db::cell_index_type m_cell_index; bool m_is_pcell; + std::vector m_pcell_params; }; /** @@ -962,6 +971,35 @@ public: return m_view_objects_dismissed; } + /** + * @brief Gets the current mouse position + */ + QPoint mouse_position () const + { + return m_mouse_pos; + } + + /** + * @brief Gets the current mouse position in micrometer units + */ + db::DPoint mouse_position_um () const + { + return pixel_to_um (m_mouse_pos); + } + + /** + * @brief Translates a screen coordinate in micrometer coordinates + */ + db::DPoint pixel_to_um (const QPoint &pt) const; + + /** + * @brief Gets a flag indicating whether the mouse is inside the window + */ + bool mouse_in_window () const + { + return m_mouse_inside; + } + protected: /** * @brief Qt focus event handler @@ -1052,8 +1090,10 @@ private: bool m_mouse_pressed_state; unsigned int m_mouse_buttons; bool m_in_mouse_move; + bool m_mouse_inside; lay::Cursor::cursor_shape m_cursor, m_default_cursor; + void ensure_entered (); void do_mouse_move (); void begin_mouse_event (lay::Cursor::cursor_shape cursor = lay::Cursor::keep); void end_mouse_event (); diff --git a/src/laybasic/laybasic/layWidgets.cc b/src/laybasic/laybasic/layWidgets.cc index aee5a598f..6ea947b02 100644 --- a/src/laybasic/laybasic/layWidgets.cc +++ b/src/laybasic/laybasic/layWidgets.cc @@ -590,7 +590,7 @@ LibrarySelectionComboBox::set_technology_filter (const std::string &tech, bool e void LibrarySelectionComboBox::update_list () { - blockSignals (true); + bool wasBlocked = blockSignals (true); db::Library *lib = current_library (); @@ -620,7 +620,7 @@ LibrarySelectionComboBox::update_list () set_current_library (lib); - blockSignals (false); + blockSignals (wasBlocked); } LibrarySelectionComboBox::~LibrarySelectionComboBox () diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index e2fe22498..319bf4e89 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -31,6 +31,7 @@ FORMS = \ LayoutViewConfigPage2a.ui \ LayoutViewConfigPage2b.ui \ LayoutViewConfigPage2c.ui \ + LayoutViewConfigPage2d.ui \ LayoutViewConfigPage3a.ui \ LayoutViewConfigPage3b.ui \ LayoutViewConfigPage3c.ui \ @@ -116,6 +117,10 @@ SOURCES = \ layEditable.cc \ layEditStipplesForm.cc \ layEditStippleWidget.cc \ + layEditorOptionsFrame.cc \ + layEditorOptionsPage.cc \ + layEditorOptionsPages.cc \ + layEditorServiceBase.cc \ layFileDialog.cc \ layFinder.cc \ layFixedFont.cc \ @@ -185,8 +190,9 @@ SOURCES = \ layDispatcher.cc \ laySelectCellViewForm.cc \ layLayoutStatisticsForm.cc \ + gsiDeclLayNetlistBrowserDialog.cc \ layD25TechnologyComponent.cc \ - gsiDeclLayNetlistBrowserDialog.cc + layLayoutViewFunctions.cc HEADERS = \ gtf.h \ @@ -217,6 +223,10 @@ HEADERS = \ layEditable.h \ layEditStipplesForm.h \ layEditStippleWidget.h \ + layEditorOptionsFrame.h \ + layEditorOptionsPage.h \ + layEditorOptionsPages.h \ + layEditorServiceBase.h \ layFileDialog.h \ layFinder.h \ layFixedFont.h \ @@ -289,6 +299,7 @@ HEADERS = \ laySelectCellViewForm.h \ layLayoutStatisticsForm.h \ layD25TechnologyComponent.h + layLayoutViewFunctions.h INCLUDEPATH += $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC $$LYM_INC DEPENDPATH += $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC $$LYM_INC diff --git a/src/laybasic/laybasic/laybasicConfig.h b/src/laybasic/laybasic/laybasicConfig.h index 71a5a2019..dc772dea0 100644 --- a/src/laybasic/laybasic/laybasicConfig.h +++ b/src/laybasic/laybasic/laybasicConfig.h @@ -75,6 +75,9 @@ static const std::string cfg_sel_line_style ("sel-line-style"); static const std::string cfg_sel_transient_mode ("sel-transient-mode"); static const std::string cfg_sel_inside_pcells_mode ("sel-inside-pcells-mode"); +static const std::string cfg_tracking_cursor_color ("tracking-cursor-color"); +static const std::string cfg_tracking_cursor_enabled ("tracking-cursor-enabled"); + static const std::string cfg_markers_visible ("markers-visible"); static const std::string cfg_min_inst_label_size ("min-inst-label-size"); diff --git a/src/laybasic/unit_tests/laySnap.cc b/src/laybasic/unit_tests/laySnapTests.cc similarity index 55% rename from src/laybasic/unit_tests/laySnap.cc rename to src/laybasic/unit_tests/laySnapTests.cc index 39a177a1f..9b2c37d03 100644 --- a/src/laybasic/unit_tests/laySnap.cc +++ b/src/laybasic/unit_tests/laySnapTests.cc @@ -51,95 +51,101 @@ TEST(1) view.set_max_hier_levels (1); - std::pair res; + lay::PointSnapToObjectResult res; // not hit res = lay::obj_snap (&view, db::DPoint (1.505, 1.505), db::DVector (), 0.1); - EXPECT_EQ (res.first, false); - EXPECT_EQ (res.second.to_string (), "1.505,1.505"); + EXPECT_EQ (res.object_snap, lay::PointSnapToObjectResult::NoObject); + EXPECT_EQ (res.snapped_point.to_string (), "1.505,1.505"); res = lay::obj_snap (&view, db::DPoint (0.505, 0.505), db::DVector (), 0.1); - EXPECT_EQ (res.first, true); - EXPECT_EQ (res.second.to_string (), "0.5,0.5"); + EXPECT_EQ (res.object_snap, lay::PointSnapToObjectResult::ObjectEdge); + EXPECT_EQ (res.snapped_point.to_string (), "0.5,0.5"); res = lay::obj_snap (&view, db::DPoint (0.485, 0.505), db::DVector (0.01, 0.01), 0.1); - EXPECT_EQ (res.first, true); - EXPECT_EQ (res.second.to_string (), "0.49,0.51"); + EXPECT_EQ (res.object_snap, lay::PointSnapToObjectResult::ObjectEdge); + EXPECT_EQ (res.snapped_point.to_string (), "0.49,0.51"); + EXPECT_EQ (res.object_ref.to_string (), "(0,1;1,0)"); res = lay::obj_snap (&view, db::DPoint (0.205, 0.215), db::DVector (0.01, 0.025), 0.1); - EXPECT_EQ (res.first, false); - EXPECT_EQ (res.second.to_string (), "0.21,0.225"); + EXPECT_EQ (res.object_snap, lay::PointSnapToObjectResult::NoObject); + EXPECT_EQ (res.snapped_point.to_string (), "0.21,0.225"); res = lay::obj_snap (&view, db::DPoint (0.505, 1.005), db::DVector (), 0.1); - EXPECT_EQ (res.first, false); - EXPECT_EQ (res.second.to_string (), "0.505,1.005"); + EXPECT_EQ (res.object_snap, lay::PointSnapToObjectResult::NoObject); + EXPECT_EQ (res.snapped_point.to_string (), "0.505,1.005"); res = lay::obj_snap (&view, db::DPoint (0.005, 1.005), db::DVector (), 0.1); - EXPECT_EQ (res.first, true); - EXPECT_EQ (res.second.to_string (), "0,1"); + EXPECT_EQ (res.object_snap, lay::PointSnapToObjectResult::ObjectVertex); + EXPECT_EQ (res.snapped_point.to_string (), "0,1"); + + res = lay::obj_snap (&view, db::DPoint (0.0, 1.005), db::DVector (), 0.1); + EXPECT_EQ (res.object_snap, lay::PointSnapToObjectResult::ObjectVertex); + EXPECT_EQ (res.snapped_point.to_string (), "0,1"); res = lay::obj_snap (&view, db::DPoint (1.000, 0.505), db::DPoint (0.505, 0.500), db::DVector (), lay::AC_Horizontal, 0.1); - EXPECT_EQ (res.first, true); - EXPECT_EQ (res.second.to_string (), "0.495,0.505"); + EXPECT_EQ (res.object_snap, lay::PointSnapToObjectResult::ObjectEdge); + EXPECT_EQ (res.snapped_point.to_string (), "0.495,0.505"); // projected snapping res = lay::obj_snap (&view, db::DPoint (1.000, 0.505), db::DPoint (0.005, 1.005), db::DVector (), lay::AC_Horizontal, 0.1); - EXPECT_EQ (res.first, true); - EXPECT_EQ (res.second.to_string (), "0,0.505"); + EXPECT_EQ (res.object_snap, lay::PointSnapToObjectResult::ObjectUnspecific); + EXPECT_EQ (res.snapped_point.to_string (), "0,0.505"); + EXPECT_EQ (res.object_ref.to_string (), "(0,1;0,1)"); - std::pair res2; + lay::TwoPointSnapToObjectResult res2; res2 = lay::obj_snap2 (&view, db::DPoint (1.5, 1.5), db::DVector (), 0.005, 1.0); - EXPECT_EQ (res2.first, false); - EXPECT_EQ (res2.second.to_string (), "(0,0;0,0)"); + EXPECT_EQ (res2.any, false); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0,0;0,0)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.205, 0.5), db::DVector (), 0.005, 1.0); - EXPECT_EQ (res2.first, true); - EXPECT_EQ (res2.second.to_string (), "(0.3525,0.6475;0,0.295)"); + EXPECT_EQ (res2.any, true); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0.3525,0.6475;0,0.295)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.205, 0.5), db::DVector (), lay::AC_Horizontal, 0.005, 1.0); - EXPECT_EQ (res2.first, true); - EXPECT_EQ (res2.second.to_string (), "(0,0.5;0.5,0.5)"); + EXPECT_EQ (res2.any, true); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0,0.5;0.5,0.5)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.205, 0.5), db::DVector (0.03, 0.03), lay::AC_Horizontal, 0.005, 1.0); - EXPECT_EQ (res2.first, true); - EXPECT_EQ (res2.second.to_string (), "(0,0.51;0.49,0.51)"); + EXPECT_EQ (res2.any, true); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0,0.51;0.49,0.51)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.205, 0.5), db::DVector (), lay::AC_Vertical, 0.005, 1.0); - EXPECT_EQ (res2.first, true); - EXPECT_EQ (res2.second.to_string (), "(0.205,0.795;0.205,0)"); + EXPECT_EQ (res2.any, true); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0.205,0.795;0.205,0)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.205, 0.5), db::DVector (), lay::AC_Diagonal, 0.005, 1.0); - EXPECT_EQ (res2.first, true); - EXPECT_EQ (res2.second.to_string (), "(0.3525,0.6475;0,0.295)"); + EXPECT_EQ (res2.any, true); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0.3525,0.6475;0,0.295)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.205, 0.505), db::DVector (), lay::AC_Ortho, 0.005, 1.0); - EXPECT_EQ (res2.first, true); - EXPECT_EQ (res2.second.to_string (), "(0,0.505;0.495,0.505)"); + EXPECT_EQ (res2.any, true); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0,0.505;0.495,0.505)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.205, 0.5), db::DVector (), lay::AC_Any, 0.005, 1.0); - EXPECT_EQ (res2.first, true); - EXPECT_EQ (res2.second.to_string (), "(0.3525,0.6475;0,0.295)"); + EXPECT_EQ (res2.any, true); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0.3525,0.6475;0,0.295)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.205, 0.495), db::DVector (0.01, 0.01), 0.005, 1.0); - EXPECT_EQ (res2.first, true); - EXPECT_EQ (res2.second.to_string (), "(0.355,0.645;0,0.29)"); + EXPECT_EQ (res2.any, true); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0.355,0.645;0,0.29)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.5, 0.5), db::DVector (), 0.005, 1.0); - EXPECT_EQ (res2.first, false); - EXPECT_EQ (res2.second.to_string (), "(0,0;0,0)"); + EXPECT_EQ (res2.any, false); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0,0;0,0)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.005, 0.5), db::DVector (), 0.005, 1.0); - EXPECT_EQ (res2.first, true); - EXPECT_EQ (res2.second.to_string (), "(0,0.5;0.5,0.5)"); + EXPECT_EQ (res2.any, true); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0,0.5;0.5,0.5)"); res2 = lay::obj_snap2 (&view, db::DPoint (0.0, 0.5), db::DVector (), 0.005, 1.0); - EXPECT_EQ (res2.first, false); - EXPECT_EQ (res2.second.to_string (), "(0,0;0,0)"); + EXPECT_EQ (res2.any, false); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0,0;0,0)"); res2 = lay::obj_snap2 (&view, db::DPoint (-0.2, 0.5), db::DVector (), 0.005, 1.0); - EXPECT_EQ (res2.first, false); - EXPECT_EQ (res2.second.to_string (), "(0,0;0,0)"); + EXPECT_EQ (res2.any, false); + EXPECT_EQ (db::DEdge (res2.first, res2.second).to_string (), "(0,0;0,0)"); } diff --git a/src/laybasic/unit_tests/unit_tests.pro b/src/laybasic/unit_tests/unit_tests.pro index 0aeab4b3d..d948a1d87 100644 --- a/src/laybasic/unit_tests/unit_tests.pro +++ b/src/laybasic/unit_tests/unit_tests.pro @@ -13,10 +13,10 @@ SOURCES = \ layLayerProperties.cc \ layParsedLayerSource.cc \ layRenderer.cc \ - laySnap.cc \ layNetlistBrowserModelTests.cc \ layNetlistBrowserTreeModelTests.cc \ - layAbstractMenuTests.cc + layAbstractMenuTests.cc \ + laySnapTests.cc INCLUDEPATH += $$TL_INC $$LAYBASIC_INC $$DB_INC $$GSI_INC $$OUT_PWD/../laybasic DEPENDPATH += $$TL_INC $$LAYBASIC_INC $$DB_INC $$GSI_INC $$OUT_PWD/../laybasic diff --git a/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc b/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc index 1b0e6d2c2..765640e43 100644 --- a/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc +++ b/src/plugins/streamers/lefdef/lay_plugin/layLEFDEFImportDialogs.cc @@ -340,26 +340,6 @@ END_PROTECTED // ----------------------------------------------------------------------------------------------- // LEFDEF technology components editor -static void -indicate_error (QWidget *le, const tl::Exception *ex) -{ - // by the way, update the foreground color of the cell edit box as well (red, if not valid) - QPalette pl = le->palette (); - if (ex) { - pl.setColor (QPalette::Active, QPalette::Text, Qt::red); - pl.setColor (QPalette::Active, QPalette::Base, QColor (Qt::red).lighter (180)); - le->setToolTip (tl::to_qstring (ex->msg ())); - } else { - QWidget *pw = dynamic_cast (le->parent ()); - tl_assert (pw != 0); - pl.setColor (QPalette::Active, QPalette::Text, pw->palette ().color (QPalette::Text)); - pl.setColor (QPalette::Active, QPalette::Base, pw->palette ().color (QPalette::Base)); - le->setToolTip (QString ()); - } - le->setPalette (pl); -} - - LEFDEFReaderOptionsEditor::LEFDEFReaderOptionsEditor (QWidget *parent) : lay::StreamReaderOptionsPage (parent), mp_tech (0) { diff --git a/src/tl/tl/tlIntervalMap.h b/src/tl/tl/tlIntervalMap.h index edfb819d9..0f84b29b9 100644 --- a/src/tl/tl/tlIntervalMap.h +++ b/src/tl/tl/tlIntervalMap.h @@ -270,6 +270,23 @@ public: } } + /** + * @brief Returns the iterator for a given index + * + * This will return the iterator for the interval which contains the index. If there is no such interval, this method + * will return end(). + * + * If there is no interval for the given index (not set), the returned iterator will not be end(), but it's + * start value will not be less or equal to the index. + * + * @param i The index to search for + * @return The iterator to the corresponding interval or end() if there is no such interval + */ + const_iterator find (I i) const + { + return std::lower_bound (m_index_map.begin (), m_index_map.end (), i, iv_compare_f ()); + } + /** * @brief Do a brief check if the structure is sorted */