Merge pull request #2146 from KLayout/devel

Devel
This commit is contained in:
Matthias Köfferlein 2025-09-10 16:02:48 +02:00 committed by GitHub
commit 0f739bcbef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
128 changed files with 6558 additions and 2523 deletions

View File

@ -1431,7 +1431,7 @@ Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::ang
const ant::Object *robj = dynamic_cast <const ant::Object *> ((*ri).ptr ());
if (robj && (! robj_min || robj == robj_min)) {
if (dragging_what (robj, search_dbox, m_move_mode, m_p1, m_seg_index) && m_move_mode != MoveRuler) {
if (dragging_what (robj, search_dbox, m_move_mode, m_p1, m_seg_index)) {
// found anything: make the moved ruler the selection
clear_selection ();
@ -1516,28 +1516,70 @@ Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::ang
}
void
Service::move_transform (const db::DPoint &p, db::DFTrans tr, lay::angle_constraint_type /*ac*/)
Service::snap_rulers (lay::angle_constraint_type ac)
{
if (m_rulers.empty ()) {
return;
}
lay::PointSnapToObjectResult min_snp;
double min_dist = -1.0;
db::DVector min_delta;
for (auto r = m_rulers.begin (); r != m_rulers.end (); ++r) {
const ant::Object *ruler = (*r)->ruler ();
db::DPoint p1 = m_trans * ruler->p1 ();
db::DPoint p2 = m_trans * ruler->p2 ();
auto tr = db::DTrans ((m_p1 - db::DPoint ()) - m_trans.disp ()) * m_trans * db::DTrans (db::DPoint () - m_p1);
db::DPoint org1 = tr * ruler->p1 ();
db::DPoint org2 = tr * ruler->p2 ();
auto snp = snap2_details (org1, p1, ruler, ac);
double dist = p1.distance (snp.snapped_point);
if (min_dist < 0 || dist < min_dist) {
min_snp = snp;
min_dist = dist;
min_delta = snp.snapped_point - p1;
}
snp = snap2_details (org2, p2, ruler, ac);
dist = p2.distance (snp.snapped_point);
if (min_dist < 0 || dist < min_dist) {
min_snp = snp;
min_dist = dist;
min_delta = snp.snapped_point - p2;
}
}
if (min_snp.object_snap != lay::PointSnapToObjectResult::NoObject) {
mouse_cursor_from_snap_details (min_snp);
}
m_trans = db::DTrans (min_delta) * m_trans;
}
void
Service::move_transform (const db::DPoint & /*p*/, db::DFTrans tr, lay::angle_constraint_type ac)
{
if (m_rulers.empty () || m_selected.empty ()) {
return;
}
if (m_move_mode == MoveRuler) {
auto ac_eff = ac == lay::AC_Global ? m_snap_mode : ac;
clear_mouse_cursors ();
db::DVector dp = p - db::DPoint ();
m_original.transform (db::DTrans (m_p1 - db::DPoint ()) * db::DTrans (tr) * db::DTrans (db::DPoint () - m_p1));
m_current.transform (db::DTrans (dp) * db::DTrans (tr) * db::DTrans (-dp));
// display current rulers' parameters
show_message ();
m_rulers [0]->redraw ();
} else if (m_move_mode == MoveSelected) {
if (m_move_mode == MoveSelected) {
m_trans *= db::DTrans (m_p1 - db::DPoint ()) * db::DTrans (tr) * db::DTrans (db::DPoint () - m_p1);
snap_rulers (ac_eff);
for (std::vector<ant::View *>::iterator r = m_rulers.begin (); r != m_rulers.end (); ++r) {
(*r)->transform_by (db::DCplxTrans (m_trans));
}
@ -1553,90 +1595,66 @@ Service::move (const db::DPoint &p, lay::angle_constraint_type ac)
return;
}
auto ac_eff = ac == lay::AC_Global ? m_snap_mode : ac;
clear_mouse_cursors ();
if (m_move_mode == MoveP1) {
m_current.seg_p1 (m_seg_index, snap2 (m_p1, p, &m_current, ac).second);
m_current.seg_p1 (m_seg_index, snap2_visual (m_p1, p, &m_current, ac));
m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP2) {
m_current.seg_p2 (m_seg_index, snap2 (m_p1, p, &m_current, ac).second);
m_current.seg_p2 (m_seg_index, snap2_visual (m_p1, p, &m_current, ac));
m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP12) {
db::DPoint p12 = snap2 (m_p1, p, &m_current, ac).second;
db::DPoint p12 = snap2_visual (m_p1, p, &m_current, ac);
m_current.seg_p1 (m_seg_index, db::DPoint (m_current.seg_p1 (m_seg_index).x(), p12.y ()));
m_current.seg_p2 (m_seg_index, db::DPoint (p12.x (), m_current.seg_p2 (m_seg_index).y ()));
m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP21) {
db::DPoint p21 = snap2 (m_p1, p, &m_current, ac).second;
db::DPoint p21 = snap2_visual (m_p1, p, &m_current, ac);
m_current.seg_p1 (m_seg_index, db::DPoint (p21.x (), m_current.seg_p1 (m_seg_index).y ()));
m_current.seg_p2 (m_seg_index, db::DPoint (m_current.seg_p2 (m_seg_index).x(), p21.y ()));
m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP1X) {
db::DPoint pc = snap2 (m_p1, p, &m_current, ac).second;
db::DPoint pc = snap2_visual (m_p1, p, &m_current, ac);
m_current.seg_p1 (m_seg_index, db::DPoint (pc.x (), m_current.seg_p1 (m_seg_index).y ()));
m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP2X) {
db::DPoint pc = snap2 (m_p1, p, &m_current, ac).second;
db::DPoint pc = snap2_visual (m_p1, p, &m_current, ac);
m_current.seg_p2 (m_seg_index, db::DPoint (pc.x (), m_current.seg_p2 (m_seg_index).y ()));
m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP1Y) {
db::DPoint pc = snap2 (m_p1, p, &m_current, ac).second;
db::DPoint pc = snap2_visual (m_p1, p, &m_current, ac);
m_current.seg_p1 (m_seg_index, db::DPoint (m_current.seg_p1 (m_seg_index).x (), pc.y ()));
m_rulers [0]->redraw ();
} else if (m_move_mode == MoveP2Y) {
db::DPoint pc = snap2 (m_p1, p, &m_current, ac).second;
db::DPoint pc = snap2_visual (m_p1, p, &m_current, ac);
m_current.seg_p2 (m_seg_index, db::DPoint (m_current.seg_p2 (m_seg_index).x (), pc.y ()));
m_rulers [0]->redraw ();
} else if (m_move_mode == MoveRuler) {
// try two ways of snapping
db::DVector dp = lay::snap_angle (p - m_p1, ac == lay::AC_Global ? m_snap_mode : ac);
db::DPoint p1 = m_original.p1 () + dp;
db::DPoint p2 = m_original.p2 () + dp;
std::pair<bool, db::DPoint> r1 = snap1 (p1, m_obj_snap && m_original.snap ());
db::DPoint q1 = r1.second;
std::pair<bool, db::DPoint> r2 = snap1 (p2, m_obj_snap && m_original.snap ());
db::DPoint q2 = r2.second;
if ((!r2.first && r1.first) || ((r1.first || (!r1.first && !r2.first)) && q1.distance (p1) < q2.distance (p2))) {
q2 = q1 + (m_original.p2 () - m_original.p1 ());
} else {
q1 = q2 + (m_original.p1 () - m_original.p2 ());
}
m_current.p1 (q1);
m_current.p2 (q2);
m_rulers [0]->redraw ();
} else if (m_move_mode == MoveSelected) {
db::DVector dp = p - m_p1;
// round the drag distance to grid if required: this is the least we can do in this case
if (m_grid_snap) {
dp = db::DVector (lay::snap (dp.x (), m_grid), lay::snap (dp.y (), m_grid));
}
dp = lay::snap_angle (dp, ac == lay::AC_Global ? m_snap_mode : ac);
dp = lay::snap_angle (dp, ac_eff);
m_trans = db::DTrans (dp + (m_p1 - db::DPoint ()) - m_trans.disp ()) * m_trans * db::DTrans (db::DPoint () - m_p1);
snap_rulers (ac_eff);
for (std::vector<ant::View *>::iterator r = m_rulers.begin (); r != m_rulers.end (); ++r) {
(*r)->transform_by (db::DCplxTrans (m_trans));
}
@ -1646,7 +1664,6 @@ Service::move (const db::DPoint &p, lay::angle_constraint_type ac)
if (m_move_mode != MoveSelected) {
show_message ();
}
}
void
@ -1703,6 +1720,7 @@ Service::end_move (const db::DPoint &, lay::angle_constraint_type)
// termine the operation
m_move_mode = MoveNone;
clear_mouse_cursors ();
}
void
@ -1766,6 +1784,7 @@ Service::edit_cancel ()
if (m_move_mode != MoveNone) {
m_move_mode = MoveNone;
m_selected.clear ();
selection_to_view ();
}
@ -2039,7 +2058,7 @@ Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio)
// otherwise we risk manipulating p1 too.
ant::Object::point_list pts = m_current.points ();
if (! pts.empty ()) {
pts.back () = snap2 (m_p1, p, mp_active_ruler->ruler (), ac_from_buttons (buttons)).second;
pts.back () = snap_details.snapped_point;
}
m_current.set_points_exact (pts);
@ -2094,11 +2113,16 @@ Service::snap2_details (const db::DPoint &p1, const db::DPoint &p2, const ant::O
return lay::obj_snap (m_obj_snap && obj->snap () ? mp_view : 0, p1, p2, g, snap_mode, snap_range);
}
std::pair <bool, db::DPoint>
Service::snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac)
db::DPoint
Service::snap2_visual (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);
if (res.object_snap != lay::PointSnapToObjectResult::NoObject) {
mouse_cursor_from_snap_details (res);
}
return res.snapped_point;
}

View File

@ -208,10 +208,9 @@ public:
* MoveP2X - dragging P2.x (if box-like)
* MoveP1Y - dragging P1.y (if box-like)
* MoveP2Y - dragging P2.y (if box-like)
* MoveRuler - dragging a whole ruler (one)
* MoveSelection - dragging a whole ruler (many)
*/
enum MoveMode { MoveNone, MoveP1, MoveP2, MoveP12, MoveP21, MoveP1X, MoveP2X, MoveP1Y, MoveP2Y, MoveRuler, MoveSelected };
enum MoveMode { MoveNone, MoveP1, MoveP2, MoveP12, MoveP21, MoveP1X, MoveP2X, MoveP1Y, MoveP2Y, MoveSelected };
Service (db::Manager *manager, lay::LayoutViewBase *view);
@ -601,7 +600,7 @@ private:
std::pair<bool, db::DPoint> snap1 (const db::DPoint &p, bool obj_snap);
lay::PointSnapToObjectResult snap1_details (const db::DPoint &p, bool obj_snap);
std::pair<bool, db::DPoint> snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac);
db::DPoint snap2_visual (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);
lay::TwoPointSnapToObjectResult auto_measure (const db::DPoint &p, lay::angle_constraint_type ac, const ant::Template &tpl);
@ -620,6 +619,8 @@ private:
virtual bool mouse_double_click_event (const db::DPoint &p, unsigned int buttons, bool prio);
virtual void deactivated ();
void snap_rulers (lay::angle_constraint_type ac);
/**
* @brief Select a certain ruler
*

View File

@ -42,10 +42,18 @@ public:
// .. nothing yet ..
}
GenericRasterizer (const db::Polygon &fp, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, const db::Vector &dim)
GenericRasterizer (const std::vector<db::Polygon> &fr, const db::Box &rasterized_area, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, const db::Vector &dim)
: m_row_step (row_step), m_column_step (column_step), m_row_steps (0), m_column_steps (0), m_origin (origin), m_dim (dim)
{
rasterize (fp);
rasterize (rasterized_area, fr);
}
GenericRasterizer (const db::Polygon &fp, const db::Box &rasterized_area, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, const db::Vector &dim)
: m_row_step (row_step), m_column_step (column_step), m_row_steps (0), m_column_steps (0), m_origin (origin), m_dim (dim)
{
std::vector<db::Polygon> fr;
fr.push_back (fp);
rasterize (rasterized_area, fr);
}
void move (const db::Vector &d)
@ -59,7 +67,44 @@ public:
m_area_maps.clear ();
}
void rasterize (const db::Polygon &fp)
const db::Point &p0 () const { return m_origin; }
unsigned int row_steps () const { return m_row_steps; }
unsigned int column_steps () const { return m_column_steps; }
unsigned int area_maps () const
{
return (unsigned int) m_area_maps.size ();
}
int index_for_p0 (const db::Point &p0) const
{
for (auto i = m_area_maps.begin (); i != m_area_maps.end (); ++i) {
if (i->p0 () == p0) {
return int (i - m_area_maps.begin ());
}
}
return -1;
}
const db::AreaMap &area_map (unsigned int i) const
{
return m_area_maps [i];
}
db::AreaMap &area_map (unsigned int i)
{
return m_area_maps [i];
}
private:
std::vector<db::AreaMap> m_area_maps;
db::Vector m_row_step, m_column_step;
unsigned int m_row_steps, m_column_steps;
db::Point m_origin;
db::Vector m_dim;
void rasterize (const db::Box &rasterized_area, const std::vector<db::Polygon> &fr)
{
db::Coord dx = m_row_step.x ();
db::Coord dy = m_column_step.y ();
@ -81,12 +126,12 @@ public:
m_row_steps *= (m_dim.x () - 1) / (m_row_steps * m_row_step.x ()) + 1;
m_column_steps *= (m_dim.y () - 1) / (m_column_steps * m_column_step.y ()) + 1;
db::Box fp_bbox = fp.box ();
db::Box ra_org = rasterized_area;
// compensate for distortion by sheared kernel
db::Coord ex = std::max (std::abs (db::Coord (m_column_step.x () * m_column_steps)), std::abs (db::Coord (m_row_step.x () * m_row_steps)));
db::Coord ey = std::max (std::abs (db::Coord (m_column_step.y () * m_column_steps)), std::abs (db::Coord (m_row_step.y () * m_row_steps)));
fp_bbox.enlarge (db::Vector (ex, ey));
ra_org.enlarge (db::Vector (ex, ey));
int columns_per_rows = (int (m_row_steps) * m_row_step.y ()) / dy;
int rows_per_columns = (int (m_column_steps) * m_column_step.x ()) / dx;
@ -95,16 +140,16 @@ public:
db::Coord ddy = dy * db::Coord (m_column_steps) - m_row_step.y () * rows_per_columns;
// round polygon bbox
db::Coord fp_left = db::Coord (tl::round_down (fp_bbox.left () - m_origin.x (), ddx)) + m_origin.x ();
db::Coord fp_bottom = db::Coord (tl::round_down (fp_bbox.bottom () - m_origin.y (), ddy)) + m_origin.y ();
db::Coord fp_right = db::Coord (tl::round_up (fp_bbox.right () - m_origin.x (), ddx)) + m_origin.x ();
db::Coord fp_top = db::Coord (tl::round_up (fp_bbox.top () - m_origin.y (), ddy)) + m_origin.y ();
fp_bbox = db::Box (fp_left, fp_bottom, fp_right, fp_top);
db::Coord ra_left = db::Coord (tl::round_down (ra_org.left () - m_origin.x (), ddx)) + m_origin.x ();
db::Coord ra_bottom = db::Coord (tl::round_down (ra_org.bottom () - m_origin.y (), ddy)) + m_origin.y ();
db::Coord ra_right = db::Coord (tl::round_up (ra_org.right () - m_origin.x (), ddx)) + m_origin.x ();
db::Coord ra_top = db::Coord (tl::round_up (ra_org.top () - m_origin.y (), ddy)) + m_origin.y ();
db::Box ra = db::Box (ra_left, ra_bottom, ra_right, ra_top);
size_t nx = fp_bbox.width () / ddx;
size_t ny = fp_bbox.height () / ddy;
size_t nx = ra.width () / ddx;
size_t ny = ra.height () / ddy;
tl_assert (fp.box ().inside (fp_bbox));
tl_assert (ra_org.inside (ra));
if (nx == 0 || ny == 0) {
// nothing to rasterize:
@ -122,9 +167,15 @@ public:
db::Vector dr = m_row_step * long (ir);
db::Vector dc = m_column_step * long (ic);
am.reinitialize (db::Point (fp_left, fp_bottom) + dr + dc, db::Vector (ddx, ddy), m_dim, nx, ny);
am.reinitialize (db::Point (ra_left, ra_bottom) + dr + dc, db::Vector (ddx, ddy), m_dim, nx, ny);
if (db::rasterize (fp, am)) {
bool any = false;
for (auto i = fr.begin (); i != fr.end (); ++i) {
if (db::rasterize (*i, am)) {
any = true;
}
}
if (any) {
m_area_maps.push_back (db::AreaMap ());
m_area_maps.back ().swap (am);
}
@ -142,9 +193,15 @@ public:
db::Vector dr = m_row_step * long ((rows_per_columns > 0 ? -int (ir + 1) : ir) + m_row_steps);
db::Vector dc = m_column_step * long ((columns_per_rows > 0 ? -int (ic + 1) : ic) + m_column_steps);
am.reinitialize (db::Point (fp_left, fp_bottom) + dr + dc, db::Vector (ddx, ddy), m_dim, nx, ny);
am.reinitialize (db::Point (ra_left, ra_bottom) + dr + dc, db::Vector (ddx, ddy), m_dim, nx, ny);
if (db::rasterize (fp, am)) {
bool any = false;
for (auto i = fr.begin (); i != fr.end (); ++i) {
if (db::rasterize (*i, am)) {
any = true;
}
}
if (any) {
m_area_maps.push_back (db::AreaMap ());
m_area_maps.back ().swap (am);
}
@ -153,39 +210,107 @@ public:
}
}
const db::Point &p0 () const { return m_origin; }
unsigned int row_steps () const { return m_row_steps; }
unsigned int column_steps () const { return m_column_steps; }
unsigned int area_maps () const
{
return (unsigned int) m_area_maps.size ();
}
const db::AreaMap &area_map (unsigned int i) const
{
return m_area_maps [i];
}
db::AreaMap &area_map (unsigned int i)
{
return m_area_maps [i];
}
private:
std::vector<db::AreaMap> m_area_maps;
db::Vector m_row_step, m_column_step;
unsigned int m_row_steps, m_column_steps;
db::Point m_origin;
db::Vector m_dim;
};
static size_t
create_instances (GenericRasterizer &am, db::Cell *cell, db::cell_index_type fill_cell_index, const db::Vector &kernel_origin, const db::Vector &fill_margin, const GenericRasterizer *exclude_rasterized, std::vector<db::Polygon> *filled_regions)
{
size_t ninsts = 0;
for (unsigned int i = 0; i < am.area_maps (); ++i) {
db::AreaMap &am1 = am.area_map (i);
const db::AreaMap *am1_excl = 0;
if (exclude_rasterized) {
int ie = exclude_rasterized->index_for_p0 (am1.p0 ());
if (ie >= 0) {
am1_excl = &exclude_rasterized->area_map (ie);
}
}
size_t nx = am1.nx ();
size_t ny = am1.ny ();
// Create the fill cell instances
for (size_t i = 0; i < nx; ++i) {
for (size_t j = 0; j < ny; ) {
size_t jj = j + 1;
if (am1.get (i, j) == am1.pixel_area () && (!am1_excl || am1_excl->get (i, j) == 0)) {
while (jj != ny && am1.get (i, jj) == am1.pixel_area () && (!am1_excl || am1_excl->get (i, jj) == 0)) {
++jj;
}
db::Vector p0 = (am1.p0 () - db::Point ()) - kernel_origin;
p0 += db::Vector (i * am1.d ().x (), j * am1.d ().y ());
db::CellInstArray array;
// try to expand the array in x direction
size_t ii = i + 1;
for ( ; ii < nx; ++ii) {
bool all = true;
for (size_t k = j; k < jj && all; ++k) {
all = (am1.get (ii, k) == am1.pixel_area () && (!am1_excl || am1_excl->get (ii, k) == 0));
}
if (all) {
for (size_t k = j; k < jj; ++k) {
// disable pixel, so we do not see it again in the following columns
am1.get (ii, k) = 0;
}
} else {
break;
}
}
ninsts += (jj - j) * (ii - i);
if (jj > j + 1 || ii > i + 1) {
array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0), db::Vector (0, am1.d ().y ()), db::Vector (am1.d ().x (), 0), (unsigned long) (jj - j), (unsigned long) (ii - i));
} else {
array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0));
}
{
// In case we run this from a tiling processor we need to lock against multithread races
tl_assert (cell->layout () != 0);
tl::MutexLocker locker (&cell->layout ()->lock ());
cell->insert (array);
}
if (filled_regions) {
if (am1.d ().y () == am1.p ().y () && am1.d ().x () == am1.p ().x ()) {
db::Box fill_box (db::Point (), db::Point (am1.p ().x () * db::Coord (ii - i), am1.p ().y () * db::Coord (jj - j)));
filled_regions->push_back (db::Polygon (fill_box.enlarged (fill_margin).moved (kernel_origin + p0)));
} else {
db::Box fill_box (db::Point (), db::Point () + am1.p ());
fill_box.enlarge (fill_margin);
for (size_t k = 0; k < jj - j; ++k) {
for (size_t l = 0; l < ii - i; ++l) {
filled_regions->push_back (db::Polygon (fill_box.moved (kernel_origin + p0 + db::Vector (am1.d ().x () * db::Coord (l), am1.d ().y () * db::Coord (k)))));
}
}
}
}
}
j = jj;
}
}
}
return ninsts;
}
static bool
fill_polygon_impl (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type fill_cell_index, const db::Box &fc_bbox, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill,
std::vector <db::Polygon> *remaining_parts, const db::Vector &fill_margin, const db::Box &glue_box)
std::vector <db::Polygon> *remaining_parts, const db::Vector &fill_margin, const db::Box &glue_box, const db::Region &exclude_area)
{
if (row_step.x () <= 0 || column_step.y () <= 0) {
throw tl::Exception (tl::to_string (tr ("Invalid row or column step vectors in fill_region: row step must have a positive x component while column step must have a positive y component")));
@ -197,156 +322,155 @@ fill_polygon_impl (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type f
db::Vector kernel_origin (fc_bbox.left (), fc_bbox.bottom ());
std::vector <db::Polygon> filled_regions;
db::EdgeProcessor ep;
// under- and oversize the polygon to remove slivers that cannot be filled.
db::Coord dx = fc_bbox.width () / 2 - 1, dy = fc_bbox.height () / 2 - 1;
std::vector <db::Polygon> fpa;
std::vector <db::Polygon> fpb;
fpa.push_back (fp0);
db::Region fr (fp0);
db::Box rasterized_area = fp0.box ();
ep.size (fpa, -dx, 0, fpb, 3 /*mode*/, false /*=don't resolve holes*/);
fpa.swap (fpb);
fpb.clear ();
std::unique_ptr<GenericRasterizer> exclude_rasterized;
bool has_exclude_area = false;
ep.size (fpa, dx, 0, fpb, 3 /*mode*/, false /*=don't resolve holes*/);
fpa.swap (fpb);
fpb.clear ();
if (! exclude_area.empty ()) {
ep.size (fpa, 0, -dy, fpb, 3 /*mode*/, false /*=don't resolve holes*/);
fpa.swap (fpb);
fpb.clear ();
auto it = exclude_area.begin_iter ();
it.first.confine_region (fp0.box ());
ep.size (fpa, 0, dy, fpb, 3 /*mode*/, false /*=don't resolve holes*/);
fpa.swap (fpb);
fpb.clear ();
// over- and undersize the polygons to fill gaps that cannot be filled.
db::Region excluded (it.first, it.second);
excluded.set_merged_semantics (false);
excluded.size (dx, 0);
excluded.set_merged_semantics (true);
excluded.size (-dx, 0);
excluded.set_merged_semantics (false);
excluded.size (dy, 0);
excluded.set_merged_semantics (true);
excluded.size (-dy, 0);
excluded.merge ();
ep.simple_merge (fpa, fpb, false /*=don't resolve holes*/);
if (! excluded.empty ()) {
filled_regions.clear ();
bool any_fill = false;
has_exclude_area = true;
for (std::vector <db::Polygon>::const_iterator fp = fpb.begin (); fp != fpb.end (); ++fp) {
if (enhanced_fill || remaining_parts != 0) {
if (fp->hull ().size () == 0) {
continue;
}
// In enhanced fill or if the remaining parts are requested, it is better to implement the
// exclude area by a boolean NOT
fr -= excluded;
// disable enhanced mode an obey the origin if the polygon is not entirely inside and not at the boundary of the glue box
bool ef = enhanced_fill;
if (ef && ! glue_box.empty () && ! fp->box ().enlarged (db::Vector (1, 1)).inside (glue_box)) {
ef = false;
}
} else {
// pick a heuristic "good" starting point in enhanced mode
// TODO: this is a pretty weak optimization.
db::Point o = origin;
if (ef) {
o = fp->hull () [0];
}
size_t ninsts = 0;
GenericRasterizer am (*fp, row_step, column_step, o, fc_bbox.p2 () - fc_bbox.p1 ());
for (unsigned int i = 0; i < am.area_maps (); ++i) {
db::AreaMap &am1 = am.area_map (i);
size_t nx = am1.nx ();
size_t ny = am1.ny ();
// Create the fill cell instances
for (size_t i = 0; i < nx; ++i) {
for (size_t j = 0; j < ny; ) {
size_t jj = j + 1;
if (am1.get (i, j) == am1.pixel_area ()) {
while (jj != ny && am1.get (i, jj) == am1.pixel_area ()) {
++jj;
}
db::Vector p0 = (am1.p0 () - db::Point ()) - kernel_origin;
p0 += db::Vector (i * am1.d ().x (), j * am1.d ().y ());
db::CellInstArray array;
// try to expand the array in x direction
size_t ii = i + 1;
for ( ; ii < nx; ++ii) {
bool all = true;
for (size_t k = j; k < jj && all; ++k) {
all = am1.get (ii, k) == am1.pixel_area ();
}
if (all) {
for (size_t k = j; k < jj; ++k) {
// disable pixel, so we do not see it again in the following columns
am1.get (ii, k) = 0;
}
} else {
break;
}
}
ninsts += (jj - j) * (ii - i);
if (jj > j + 1 || ii > i + 1) {
array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0), db::Vector (0, am1.d ().y ()), db::Vector (am1.d ().x (), 0), (unsigned long) (jj - j), (unsigned long) (ii - i));
} else {
array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0));
}
{
// In case we run this from a tiling processor we need to lock against multithread races
tl_assert (cell->layout () != 0);
tl::MutexLocker locker (&cell->layout ()->lock ());
cell->insert (array);
}
if (remaining_parts) {
if (am1.d ().y () == am1.p ().y () && am1.d ().x () == am1.p ().x ()) {
db::Box fill_box (db::Point (), db::Point (am1.p ().x () * db::Coord (ii - i), am1.p ().y () * db::Coord (jj - j)));
filled_regions.push_back (db::Polygon (fill_box.enlarged (fill_margin).moved (kernel_origin + p0)));
} else {
db::Box fill_box (db::Point (), db::Point () + am1.p ());
fill_box.enlarge (fill_margin);
for (size_t k = 0; k < jj - j; ++k) {
for (size_t l = 0; l < ii - i; ++l) {
filled_regions.push_back (db::Polygon (fill_box.moved (kernel_origin + p0 + db::Vector (am1.d ().x () * db::Coord (l), am1.d ().y () * db::Coord (k)))));
}
}
}
}
any_fill = true;
}
j = jj;
// Otherwise use a second rasterizer for the exclude polygons that must have a zero pixel coverage for the
// pixel to be filled.
std::vector<db::Polygon> excluded_poly;
excluded_poly.reserve (excluded.count ());
for (auto i = excluded.begin (); ! i.at_end (); ++i) {
excluded_poly.push_back (*i);
}
excluded.clear ();
exclude_rasterized.reset (new GenericRasterizer (excluded_poly, rasterized_area, row_step, column_step, origin, fc_bbox.p2 () - fc_bbox.p1 ()));
}
}
}
std::vector <db::Polygon> filled_poly, filled_poly_uncleaned;
// save the uncleaned polygons, so we subtract the filled parts to
// form the remaining parts
if (remaining_parts) {
filled_poly_uncleaned.reserve (fr.count ());
for (auto i = fr.begin (); ! i.at_end (); ++i) {
filled_poly_uncleaned.push_back (*i);
}
}
// under- and oversize the polygon to remove slivers that cannot be filled.
fr.set_merged_semantics (true);
fr.size (-dx, 0);
fr.set_merged_semantics (false);
fr.size (dx, 0);
fr.set_merged_semantics (true);
fr.size (0, -dy);
fr.set_merged_semantics (false);
fr.size (0, dy);
fr.set_merged_semantics (true);
fr.merge ();
filled_poly.reserve (fr.count ());
for (auto i = fr.begin (); ! i.at_end (); ++i) {
filled_poly.push_back (*i);
}
fr.clear ();
std::vector <db::Polygon> filled_regions;
bool any_fill = false;
if (filled_poly.empty ()) {
// not need to do anything
} else if (exclude_rasterized.get ()) {
tl_assert (remaining_parts == 0);
GenericRasterizer am (filled_poly, rasterized_area, row_step, column_step, origin, fc_bbox.p2 () - fc_bbox.p1 ());
size_t ninsts = create_instances (am, cell, fill_cell_index, kernel_origin, fill_margin, exclude_rasterized.get (), 0);
if (ninsts > 0) {
any_fill = true;
}
if (tl::verbosity () >= 30 && ninsts > 0) {
tl::info << "Part " << fp->to_string ();
tl::info << "Part " << fp0.to_string ();
tl::info << "Created " << ninsts << " instances";
}
} else {
for (auto fp = filled_poly.begin (); fp != filled_poly.end (); ++fp) {
if (fp->is_empty ()) {
continue;
}
// disable enhanced mode an obey the origin if the polygon is not entirely inside and not at the boundary of the glue box
bool ef = enhanced_fill;
if (ef && ! glue_box.empty () && ! fp->box ().enlarged (db::Vector (1, 1)).inside (glue_box)) {
ef = false;
}
// pick a heuristic "good" starting point in enhanced mode
// TODO: this is a pretty weak optimization.
db::Point o = origin;
if (ef) {
o = fp->hull () [0];
}
GenericRasterizer am (*fp, rasterized_area, row_step, column_step, o, fc_bbox.p2 () - fc_bbox.p1 ());
size_t ninsts = create_instances (am, cell, fill_cell_index, kernel_origin, fill_margin, 0, remaining_parts ? &filled_regions : 0);
if (ninsts > 0) {
any_fill = true;
}
if (tl::verbosity () >= 30 && ninsts > 0) {
tl::info << "Part " << fp->to_string ();
tl::info << "Created " << ninsts << " instances";
}
}
}
if (any_fill) {
if (any_fill || has_exclude_area) {
if (remaining_parts) {
std::vector <db::Polygon> fp1;
fp1.push_back (fp0);
ep.boolean (fp1, filled_regions, *remaining_parts, db::BooleanOp::ANotB, false /*=don't resolve holes*/);
db::EdgeProcessor ep;
ep.boolean (filled_poly_uncleaned, filled_regions, *remaining_parts, db::BooleanOp::ANotB, false /*=don't resolve holes*/);
}
return true;
@ -358,25 +482,25 @@ fill_polygon_impl (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type f
DB_PUBLIC bool
fill_region (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type fill_cell_index, const Box &fc_box, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill,
std::vector <db::Polygon> *remaining_parts, const db::Vector &fill_margin, const db::Box &glue_box)
std::vector <db::Polygon> *remaining_parts, const db::Vector &fill_margin, const db::Box &glue_box, const db::Region &exclude_area)
{
return fill_polygon_impl (cell, fp0, fill_cell_index, fc_box, row_step, column_step, origin, enhanced_fill, remaining_parts, fill_margin, glue_box);
return fill_polygon_impl (cell, fp0, fill_cell_index, fc_box, row_step, column_step, origin, enhanced_fill, remaining_parts, fill_margin, glue_box, exclude_area);
}
DB_PUBLIC bool
fill_region (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type fill_cell_index, const db::Box &fc_bbox, const db::Point &origin, bool enhanced_fill,
std::vector <db::Polygon> *remaining_parts, const db::Vector &fill_margin, const db::Box &glue_box)
std::vector <db::Polygon> *remaining_parts, const db::Vector &fill_margin, const db::Box &glue_box, const db::Region &exclude_area)
{
if (fc_bbox.empty () || fc_bbox.width () == 0 || fc_bbox.height () == 0) {
throw tl::Exception (tl::to_string (tr ("Invalid fill cell footprint (empty or zero width/height)")));
}
return fill_polygon_impl (cell, fp0, fill_cell_index, fc_bbox, db::Vector (fc_bbox.width (), 0), db::Vector (0, fc_bbox.height ()), origin, enhanced_fill, remaining_parts, fill_margin, glue_box);
return fill_polygon_impl (cell, fp0, fill_cell_index, fc_bbox, db::Vector (fc_bbox.width (), 0), db::Vector (0, fc_bbox.height ()), origin, enhanced_fill, remaining_parts, fill_margin, glue_box, exclude_area);
}
static void
fill_region_impl (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_bbox, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill,
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, int iteration, const db::Box &glue_box)
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, int iteration, const db::Box &glue_box, const db::Region &exclude_area)
{
if (row_step.x () <= 0 || column_step.y () <= 0) {
throw tl::Exception (tl::to_string (tr ("Invalid row or column step vectors in fill_region: row step must have a positive x component while column step must have a positive y component")));
@ -403,7 +527,7 @@ fill_region_impl (db::Cell *cell, const db::Region &fr, db::cell_index_type fill
tl::RelativeProgress progress (progress_title, n);
for (db::Region::const_iterator p = fr.begin_merged (); !p.at_end (); ++p) {
if (! fill_polygon_impl (cell, *p, fill_cell_index, fc_bbox, row_step, column_step, origin, enhanced_fill, remaining_parts ? &rem_pp : 0, fill_margin, glue_box)) {
if (! fill_polygon_impl (cell, *p, fill_cell_index, fc_bbox, row_step, column_step, origin, enhanced_fill, remaining_parts ? &rem_pp : 0, fill_margin, glue_box, exclude_area)) {
if (remaining_polygons) {
rem_poly.push_back (*p);
}
@ -433,27 +557,27 @@ fill_region_impl (db::Cell *cell, const db::Region &fr, db::cell_index_type fill
DB_PUBLIC void
fill_region (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_bbox, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill,
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box)
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box, const db::Region &exclude_area)
{
fill_region_impl (cell, fr, fill_cell_index, fc_bbox, row_step, column_step, origin, enhanced_fill, remaining_parts, fill_margin, remaining_polygons, 0, glue_box);
fill_region_impl (cell, fr, fill_cell_index, fc_bbox, row_step, column_step, origin, enhanced_fill, remaining_parts, fill_margin, remaining_polygons, 0, glue_box, exclude_area);
}
DB_PUBLIC void
fill_region (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_bbox, const db::Point &origin, bool enhanced_fill,
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box)
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box, const db::Region &exclude_area)
{
if (fc_bbox.empty () || fc_bbox.width () == 0 || fc_bbox.height () == 0) {
throw tl::Exception (tl::to_string (tr ("Invalid fill cell footprint (empty or zero width/height)")));
}
fill_region_impl (cell, fr, fill_cell_index, fc_bbox, db::Vector (fc_bbox.width (), 0), db::Vector (0, fc_bbox.height ()),
origin, enhanced_fill, remaining_parts, fill_margin, remaining_polygons, 0, glue_box);
origin, enhanced_fill, remaining_parts, fill_margin, remaining_polygons, 0, glue_box, exclude_area);
}
DB_PUBLIC void
fill_region_repeat (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index,
const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step,
const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box)
const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box, const db::Region &exclude_area)
{
const db::Region *fill_region = &fr;
@ -467,7 +591,7 @@ fill_region_repeat (db::Cell *cell, const db::Region &fr, db::cell_index_type fi
++iteration;
remaining.clear ();
fill_region_impl (cell, *fill_region, fill_cell_index, fc_box, row_step, column_step, db::Point (), true, &remaining, fill_margin, remaining_polygons, iteration, glue_box);
fill_region_impl (cell, *fill_region, fill_cell_index, fc_box, row_step, column_step, db::Point (), true, &remaining, fill_margin, remaining_polygons, iteration, glue_box, exclude_area);
new_fill_region.swap (remaining);
fill_region = &new_fill_region;

View File

@ -23,12 +23,12 @@
#include "dbTypes.h"
#include "dbPolygon.h"
#include "dbRegion.h"
namespace db
{
class Cell;
class Region;
/**
* @brief Creates a tiling pattern for a single polygon using a fill cell which is repeated periodically
@ -41,6 +41,7 @@ class Region;
* @param column_step (some_versions) The column advance vector of the fill cell. By default this is (0, fc_bbox.height())
* @param origin Specifies the origin of the fill raster if enhanced_fill is false
* @param enhanced_fill If set, the tiling offset will be optimized such that as much tiling cells fit into each polygon
* @param exclude_area The region which fill cells must not overlap
*
* Optional parameters:
*
@ -79,12 +80,16 @@ class Region;
*/
DB_PUBLIC bool
fill_region (db::Cell *cell, const db::Polygon &fp, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Point &origin, bool enhanced_fill,
std::vector <db::Polygon> *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (), const db::Box &glue_box = db::Box ());
fill_region (db::Cell *cell, const db::Polygon &fp, db::cell_index_type fill_cell_index, const db::Box &fc_box,
const db::Point &origin, bool enhanced_fill,
std::vector <db::Polygon> *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (),
const db::Box &glue_box = db::Box ());
DB_PUBLIC bool
fill_region (db::Cell *cell, const db::Polygon &fp, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill,
std::vector <db::Polygon> *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (), const db::Box &glue_box = db::Box ());
fill_region (db::Cell *cell, const db::Polygon &fp, db::cell_index_type fill_cell_index, const db::Box &fc_box,
const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill,
std::vector <db::Polygon> *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (),
const db::Box &glue_box = db::Box (), const db::Region &exclude_area = db::Region ());
/**
@ -98,12 +103,16 @@ fill_region (db::Cell *cell, const db::Polygon &fp, db::cell_index_type fill_cel
*/
DB_PUBLIC void
fill_region (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Point &origin, bool enhanced_fill,
db::Region *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (), db::Region *remaining_polygons = 0, const db::Box &glue_box = db::Box ());
fill_region (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_box,
const db::Point &origin, bool enhanced_fill,
db::Region *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (), db::Region *remaining_polygons = 0,
const db::Box &glue_box = db::Box (), const db::Region &exclude_area = db::Region ());
DB_PUBLIC void
fill_region (db::Cell *cell, const db::Region &fp, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill,
db::Region *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (), db::Region *remaining_polygons = 0, const db::Box &glue_box = db::Box ());
fill_region (db::Cell *cell, const db::Region &fp, db::cell_index_type fill_cell_index, const db::Box &fc_box,
const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill,
db::Region *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (), db::Region *remaining_polygons = 0,
const db::Box &glue_box = db::Box (), const db::Region &exclude_area = db::Region ());
/**
* @brief An iterative version for enhanced fill
@ -118,6 +127,7 @@ fill_region (db::Cell *cell, const db::Region &fp, db::cell_index_type fill_cell
DB_PUBLIC void
fill_region_repeat (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index,
const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step,
const db::Vector &fill_margin, db::Region *remaining_polygons = 0, const db::Box &glue_box = db::Box ());
const db::Vector &fill_margin, db::Region *remaining_polygons = 0,
const db::Box &glue_box = db::Box (), const db::Region &exclude_area = db::Region ());
}

View File

@ -1780,7 +1780,7 @@ Layout::force_update ()
// NOTE: the assumption is that either one thread is writing or
// multiple threads are reading. Hence, we do not need to lock hier_dirty() or bboxes_dirty().
// We still do double checking as another thread might do the update as well.
if (! hier_dirty () && ! bboxes_dirty ()) {
if (! update_needed ()) {
return;
}
@ -1829,10 +1829,16 @@ Layout::update () const
}
}
bool
Layout::update_needed () const
{
return hier_dirty () || bboxes_dirty ();
}
void
Layout::do_update ()
{
if (! hier_dirty () && ! bboxes_dirty ()) {
if (! update_needed ()) {
return;
}

View File

@ -1619,6 +1619,14 @@ public:
*/
top_down_const_iterator end_top_cells () const;
/**
* @brief Gets a value indicating whether an update is needed
*
* If this value is false, update or force_update will not
* do anything.
*/
bool update_needed () const;
/**
* @brief Provide a const version of the update method
*

View File

@ -511,7 +511,7 @@ MeasureNetEval::put_func (const tl::Variant &name, const tl::Variant &value) con
MeasureNetEval::AreaAndPerimeter
MeasureNetEval::compute_area_and_perimeter (int layer_index) const
{
if (layer_index < 0 || layer_index >= (unsigned int) m_layers.size ()) {
if (layer_index < 0 || layer_index >= (int) m_layers.size ()) {
return AreaAndPerimeter ();
}

View File

@ -1512,16 +1512,17 @@ public:
* @param tr The transformation to apply on assignment
* @param compress True, if the contours shall be compressed
* @param remove_reflected True, if reflecting spikes shall be removed on compression
* @param normalize If true, the orientation is normalized
*/
template <class D, class T>
polygon (const db::polygon<D> &p, const T &tr, bool compress = default_compression<C> (), bool remove_reflected = false)
polygon (const db::polygon<D> &p, const T &tr, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
// create an entry for the hull contour
m_bbox = box_type (tr (p.box ().p1 ()), tr (p.box ().p2 ()));
m_ctrs.resize (p.holes () + 1);
m_ctrs [0].assign (p.begin_hull (), p.end_hull (), tr, false, compress, true /*normalize*/, remove_reflected);
m_ctrs [0].assign (p.begin_hull (), p.end_hull (), tr, false, compress, normalize, remove_reflected);
for (unsigned int i = 0; i < m_ctrs.size () - 1; ++i) {
m_ctrs [i + 1].assign (p.begin_hole (i), p.end_hole (i), tr, true, compress, true /*normalize*/, remove_reflected);
m_ctrs [i + 1].assign (p.begin_hole (i), p.end_hole (i), tr, true, compress, normalize, remove_reflected);
}
}
@ -1533,16 +1534,16 @@ public:
* @param remove_reflected True, if reflecting spikes shall be removed on compression
*/
template <class D>
explicit polygon (const db::polygon<D> &p, bool compress = default_compression<C> (), bool remove_reflected = false)
explicit polygon (const db::polygon<D> &p, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
db::point_coord_converter<C, D> tr;
// create an entry for the hull contour
m_bbox = box_type (tr (p.box ().p1 ()), tr (p.box ().p2 ()));
m_ctrs.resize (p.holes () + 1);
m_ctrs [0].assign (p.begin_hull (), p.end_hull (), tr, false, compress, true /*normalize*/, remove_reflected);
m_ctrs [0].assign (p.begin_hull (), p.end_hull (), tr, false, compress, normalize, remove_reflected);
for (unsigned int i = 0; i < m_ctrs.size () - 1; ++i) {
m_ctrs [i + 1].assign (p.begin_hole (i), p.end_hole (i), tr, true, compress, true /*normalize*/, remove_reflected);
m_ctrs [i + 1].assign (p.begin_hole (i), p.end_hole (i), tr, true, compress, normalize, remove_reflected);
}
}
@ -2072,11 +2073,12 @@ public:
* @param end The end of the sequence of points for the contour
* @param compress true, if the sequence shall be compressed (colinear points removed)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
* @param normalize If true, the orientation is normalized
*/
template <class I>
void assign_hull (I start, I end, bool compress = default_compression<C> (), bool remove_reflected = false)
void assign_hull (I start, I end, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
m_ctrs [0].assign (start, end, false, compress, true /*normalize*/, remove_reflected);
m_ctrs [0].assign (start, end, false, compress, normalize, remove_reflected);
m_bbox = m_ctrs [0].bbox ();
}
@ -2089,14 +2091,15 @@ public:
* so it is oriented properly.
* @param compress true, if the sequence shall be compressed (colinear points removed)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
* @param normalize If true, the orientation is normalized
*
* @param start The start of the sequence of points for the contour
* @param end The end of the sequence of points for the contour
*/
template <class I, class T>
void assign_hull (I start, I end, T op, bool compress = default_compression<C> (), bool remove_reflected = false)
void assign_hull (I start, I end, T op, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
m_ctrs [0].assign (start, end, op, false, compress, true /*normalize*/, remove_reflected);
m_ctrs [0].assign (start, end, op, false, compress, normalize, remove_reflected);
m_bbox = m_ctrs [0].bbox ();
}
@ -2128,11 +2131,12 @@ public:
* @param end The end of the sequence of points for the contour
* @param compress true, if the sequence shall be compressed (colinear points removed)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
* @param normalize If true, the orientation is normalized
*/
template <class I>
void assign_hole (unsigned int h, I start, I end, bool compress = default_compression<C> (), bool remove_reflected = false)
void assign_hole (unsigned int h, I start, I end, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
m_ctrs [h + 1].assign (start, end, true, compress, true /*normalize*/, remove_reflected);
m_ctrs [h + 1].assign (start, end, true, compress, normalize, remove_reflected);
}
/**
@ -2144,14 +2148,15 @@ public:
* so it is oriented properly.
* @param compress true, if the sequence shall be compressed (colinear points removed)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
* @param normalize If true, the orientation is normalized
*
* @param start The start of the sequence of points for the contour
* @param end The end of the sequence of points for the contour
*/
template <class I, class T>
void assign_hole (unsigned int h, I start, I end, T op, bool compress = default_compression<C> (), bool remove_reflected = false)
void assign_hole (unsigned int h, I start, I end, T op, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
m_ctrs [h + 1].assign (start, end, op, true, compress, true /*normalize*/, remove_reflected);
m_ctrs [h + 1].assign (start, end, op, true, compress, normalize, remove_reflected);
}
/**
@ -2183,11 +2188,12 @@ public:
* @param end The end of the sequence of points for the contour
* @param compress true, if the sequence shall be compressed (colinear points removed)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
* @param normalize If true, the orientation is normalized
*/
template <class I>
void insert_hole (I start, I end, bool compress = default_compression<C> (), bool remove_reflected = false)
void insert_hole (I start, I end, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
insert_hole (start, end, db::unit_trans<C> (), compress, remove_reflected);
insert_hole (start, end, db::unit_trans<C> (), compress, remove_reflected, normalize);
}
/**
@ -2204,13 +2210,14 @@ public:
* @param end The end of the sequence of points for the contour
* @param compress true, if the sequence shall be compressed (colinear points removed)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
* @param normalize If true, the orientation is normalized
*/
template <class I, class T>
void insert_hole (I start, I end, T op, bool compress = default_compression<C> (), bool remove_reflected = false)
void insert_hole (I start, I end, T op, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
// add the hole
contour_type &h = add_hole ();
h.assign (start, end, op, true, compress, true /*normalize*/, remove_reflected);
h.assign (start, end, op, true, compress, normalize, remove_reflected);
}
/**
@ -2637,13 +2644,14 @@ public:
* @param tr The transformation to apply
* @param compress true, if the sequence shall be compressed (colinear points removed)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
* @param normalize If true, the orientation is normalized
*/
template <class D, class T>
simple_polygon (const db::simple_polygon<D> &p, const T &tr, bool compress = default_compression<C> (), bool remove_reflected = false)
simple_polygon (const db::simple_polygon<D> &p, const T &tr, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
// create an entry for the hull contour
m_bbox = box_type (tr (p.box ().p1 ()), tr (p.box ().p2 ()));
m_hull.assign (p.begin_hull (), p.end_hull (), tr, false, compress, true /*normalize*/, remove_reflected);
m_hull.assign (p.begin_hull (), p.end_hull (), tr, false, compress, normalize, remove_reflected);
}
/**
@ -2652,15 +2660,16 @@ public:
* @param p The source polygon
* @param compress true, if the sequence shall be compressed (colinear points removed)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
* @param normalize If true, the orientation is normalized
*/
template <class D>
explicit simple_polygon (const db::simple_polygon<D> &p, bool compress = default_compression<C> (), bool remove_reflected = false)
explicit simple_polygon (const db::simple_polygon<D> &p, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
db::point_coord_converter<C, D> tr;
// create an entry for the hull contour
m_bbox = box_type (tr (p.box ().p1 ()), tr (p.box ().p2 ()));
m_hull.assign (p.begin_hull (), p.end_hull (), tr, false, compress, true /*normalize*/, remove_reflected);
m_hull.assign (p.begin_hull (), p.end_hull (), tr, false, compress, normalize, remove_reflected);
}
/**
@ -2992,11 +3001,12 @@ public:
* @param end The end of the sequence of points for the contour
* @param compress true, if the sequence shall be compressed (colinear segments joined)
* @param remove_reflected True, if reflecting spikes shall be removed on compression
* @param normalize If true, the orientation is normalized
*/
template <class I, class T>
void assign_hull (I start, I end, T op, bool compress = default_compression<C> (), bool remove_reflected = false)
void assign_hull (I start, I end, T op, bool compress = default_compression<C> (), bool remove_reflected = false, bool normalize = true)
{
m_hull.assign (start, end, op, false, compress, true /*normalize*/, remove_reflected);
m_hull.assign (start, end, op, false, compress, normalize, remove_reflected);
m_bbox = m_hull.bbox ();
}

View File

@ -1441,8 +1441,10 @@ Shapes::replace_member_with_props (typename db::object_tag<Sh> tag, const shape_
if (! layout ()) {
if (needs_translate (tag)) {
return reinsert_member_with_props (tag, ref, sh);
} else {
} else if (! ref.has_prop_id ()) {
// simple replace case
@ -1459,7 +1461,21 @@ Shapes::replace_member_with_props (typename db::object_tag<Sh> tag, const shape_
db::layer_op<Sh, db::stable_layer_tag>::queue_or_append (manager (), this, true /*insert*/, sh);
}
return ref;
} else {
if (manager () && manager ()->transacting ()) {
check_is_editable_for_undo_redo ();
db::layer_op<db::object_with_properties<Sh>, db::stable_layer_tag>::queue_or_append (manager (), this, false /*not insert*/, *ref.basic_ptr (typename db::object_with_properties<Sh>::tag ()));
}
invalidate_state (); // HINT: must come before the change is done!
db::object_with_properties<Sh> swp (sh, ref.prop_id ());
get_layer<db::object_with_properties<Sh>, db::stable_layer_tag> ().replace (ref.basic_iter (typename db::object_with_properties<Sh>::tag ()), swp);
if (manager () && manager ()->transacting ()) {
db::layer_op<db::object_with_properties<Sh>, db::stable_layer_tag>::queue_or_append (manager (), this, true /*insert*/, swp);
}
}
@ -1514,9 +1530,9 @@ Shapes::replace_member_with_props (typename db::object_tag<Sh> tag, const shape_
}
return ref;
}
return ref;
}
// explicit instantiations

View File

@ -58,7 +58,7 @@ Writer::write (db::Layout &layout, tl::OutputStream &stream)
{
tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Writing file: ")) + stream.path ());
if (layout.under_construction ()) {
if (layout.under_construction () && layout.update_needed ()) {
tl::warn << tl::to_string (tr ("Cannot properly write a layout that is under construction - forcing update."));
layout.force_update ();
}

View File

@ -1510,23 +1510,23 @@ static void move_tree_shapes3 (db::Cell *cell, db::Cell &source_cell, const db::
static void
fill_region (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Point *origin,
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box)
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box, const db::Region &exclude_area)
{
db::fill_region (cell, fr, fill_cell_index, fc_box, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons, glue_box);
db::fill_region (cell, fr, fill_cell_index, fc_box, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons, glue_box, exclude_area);
}
static void
fill_region_skew (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step, const db::Point *origin,
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box)
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box, const db::Region &exclude_area)
{
db::fill_region (cell, fr, fill_cell_index, fc_box, row_step, column_step, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons, glue_box);
db::fill_region (cell, fr, fill_cell_index, fc_box, row_step, column_step, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons, glue_box, exclude_area);
}
static void
fill_region_multi (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step,
const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box)
const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box, const db::Region &exclude_area)
{
db::fill_region_repeat (cell, fr, fill_cell_index, fc_box, row_step, column_step, fill_margin, remaining_polygons, glue_box);
db::fill_region_repeat (cell, fr, fill_cell_index, fc_box, row_step, column_step, fill_margin, remaining_polygons, glue_box, exclude_area);
}
static db::Instance cell_inst_dtransform_simple (db::Cell *cell, const db::Instance &inst, const db::DTrans &t)
@ -2215,6 +2215,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
gsi::arg ("fill_margin", db::Vector ()),
gsi::arg ("remaining_polygons", (db::Region *)0, "nil"),
gsi::arg ("glue_box", db::Box ()),
gsi::arg ("exclude_area", db::Region (), "empty"),
"@brief Fills the given region with cells of the given type (extended version)\n"
"@param region The region to fill\n"
"@param fill_cell_index The fill cell to place\n"
@ -2224,6 +2225,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"@param fill_margin See explanation below\n"
"@param remaining_polygons See explanation below\n"
"@param glue_box Guarantees fill cell compatibility to neighbor regions in enhanced mode\n"
"@param exclude_area A region that defines the areas which are not be filled\n"
"\n"
"This method creates a regular pattern of fill cells to cover the interior of the given region as far as possible. "
"This process is also known as tiling. This implementation supports rectangular (not necessarily square) tile cells. "
@ -2236,6 +2238,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"\n"
"The implementation will basically try to find a repetition pattern of the tile cell's footprint "
"and produce instances which fit entirely into the fill region.\n"
"If an exclude area is given, the fill cells also must not overlap that region.\n"
"\n"
"There is also a version available which offers skew step vectors as a generalization of the orthogonal ones.\n"
"\n"
@ -2246,8 +2249,11 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"If the 'remaining_polygons' argument is non-nil, the corresponding region will receive all polygons from the input region "
"which could not be filled and where there is no chance of filling because not a single tile will fit into them.\n"
"\n"
"'remaining_parts' and 'remaining_polygons' can be identical with the input. In that case the input will be overwritten with "
"'remaining_parts' and 'remaining_polygons' can point to the same Region object.\n"
"They can also be identical with the input. In that case the input will be overwritten with "
"the respective output. Otherwise, the respective polygons are added to these regions.\n"
"'remaining_polygons' is not used if 'exclude_area' is present and non-empty. In that case, the\n"
"original polygons, which cannot be filled at all, are copied to 'remaining_parts'.\n"
"\n"
"This allows setting up a more elaborate fill scheme using multiple iterations and local origin-optimization ('origin' is nil):\n"
"\n"
@ -2260,7 +2266,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"fill_margin = RBA::Point::new(0, 0) # x/y distance between tile cells with different origin\n"
"\n"
"# Iteration: fill a region and fill the remaining parts as long as there is anything left.\n"
"# Polygons not worth being considered further are dropped (last argument is nil).\n"
"# Polygons not worth being considered further are dropped ('remaining_polygons' argument is nil).\n"
"while !r.is_empty?\n"
" c.fill_region(r, fc_index, fc_box, nil, r, fill_margin, nil)\n"
"end\n"
@ -2275,7 +2281,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"at the raster implied by origin at the glue box border and beyond. To ensure fill cell compatibility inside the tiling processor, it is sufficient to use the tile "
"box as the glue box.\n"
"\n"
"This method has been introduced in version 0.23 and enhanced in version 0.27.\n"
"This method has been introduced in version 0.23 and enhanced in version 0.27. The 'exclude_area' argument has been added in version 0.30.4.\n"
) +
gsi::method_ext ("fill_region", &fill_region_skew, gsi::arg ("region"),
gsi::arg ("fill_cell_index"),
@ -2287,16 +2293,18 @@ Class<db::Cell> decl_Cell ("db", "Cell",
gsi::arg ("fill_margin", db::Vector ()),
gsi::arg ("remaining_polygons", (db::Region *)0, "nil"),
gsi::arg ("glue_box", db::Box ()),
gsi::arg ("exclude_area", db::Region (), "empty"),
"@brief Fills the given region with cells of the given type (skew step version)\n"
"@param region The region to fill\n"
"@param fill_cell_index The fill cell to place\n"
"@param fc_bbox The fill cell's box to place\n"
"@param fc_bbox The fill cell's box, defining the box that needs to be inside the fill region\n"
"@param row_step The 'rows' step vector\n"
"@param column_step The 'columns' step vector\n"
"@param origin The global origin of the fill pattern or nil to allow local (per-polygon) optimization\n"
"@param remaining_parts See explanation in other version\n"
"@param fill_margin See explanation in other version\n"
"@param remaining_polygons See explanation in other version\n"
"@param exclude_area A region that defines the areas which are not be filled\n"
"\n"
"This version is similar to the version providing an orthogonal fill, but it offers more generic stepping of the fill cell.\n"
"The step pattern is defined by an origin and two vectors (row_step and column_step) which span the axes of the fill cell pattern.\n"
@ -2305,7 +2313,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"be overlapping and there can be space between the fill box instances. Fill boxes are placed where they fit entirely into a polygon of the region. "
"The fill boxes lower left corner is the reference for the fill pattern and aligns with the origin if given.\n"
"\n"
"This variant has been introduced in version 0.27.\n"
"This variant has been introduced in version 0.27. The 'exclude_area' argument has been added in version 0.30.4.\n"
) +
gsi::method_ext ("fill_region_multi", &fill_region_multi, gsi::arg ("region"),
gsi::arg ("fill_cell_index"),
@ -2315,6 +2323,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
gsi::arg ("fill_margin", db::Vector ()),
gsi::arg ("remaining_polygons", (db::Region *)0, "nil"),
gsi::arg ("glue_box", db::Box ()),
gsi::arg ("exclude_area", db::Region (), "empty"),
"@brief Fills the given region with cells of the given type in enhanced mode with iterations\n"
"This version operates like \\fill_region, but repeats the fill generation until no further fill cells can be placed. "
"As the fill pattern origin changes between the iterations, narrow regions can be filled which cannot with a fixed fill pattern origin. "
@ -2323,7 +2332,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"\n"
"The origin is ignored unless a glue box is given. See \\fill_region for a description of this concept.\n"
"\n"
"This method has been introduced in version 0.27.\n"
"This method has been introduced in version 0.27. The 'exclude_area' argument has been added in version 0.30.4.\n"
) +
gsi::method_ext ("begin_shapes_rec", &begin_shapes_rec, gsi::arg ("layer"),
"@brief Delivers a recursive shape iterator for the shapes below the cell on the given layer\n"

View File

@ -1758,6 +1758,13 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"is ongoing or the layout is brought into invalid state by\n"
"\"start_changes\".\n"
) +
gsi::method ("update_needed", &db::Layout::update_needed,
"@brief Gets a value indicating whether the Layout object needs an update\n"
"If this method returns false, \\update will not do anything. This is useful to force an update at "
"specific times during 'under_construction' conditions.\n"
"\n"
"This method has been introduced in version 0.30.4."
) +
gsi::method ("update", (void (db::Layout::*) ()) &db::Layout::force_update,
"@brief Updates the internals of the layout\n"
"This method updates the internal state of the layout. Usually this is done automatically\n"

View File

@ -868,12 +868,12 @@ static db::SimplePolygon transformed_icplx_sp (const db::SimplePolygon *p, const
static db::SimplePolygon *spolygon_from_dspolygon (const db::DSimplePolygon &p)
{
return new db::SimplePolygon (p, false);
return new db::SimplePolygon (p, false, false /*don't remove reflected*/, false /*no normalize*/);
}
static db::DSimplePolygon spolygon_to_dspolygon (const db::SimplePolygon *p, double dbu)
{
return db::DSimplePolygon (*p * dbu, false);
return db::DSimplePolygon (*p, db::CplxTrans (dbu), false, false /*don't remove reflected*/, false /*no normalize*/);
}
Class<db::SimplePolygon> decl_SimplePolygon ("db", "SimplePolygon",
@ -1031,12 +1031,12 @@ Class<db::SimplePolygonWithProperties> decl_SimplePolygonWithProperties (decl_Si
static db::DSimplePolygon *dspolygon_from_ispolygon (const db::SimplePolygon &p)
{
return new db::DSimplePolygon (p, false);
return new db::DSimplePolygon (p, false, false /*don't remove reflected*/, false /*no normalize*/);
}
static db::SimplePolygon dspolygon_to_spolygon (const db::DSimplePolygon *p, double dbu)
{
return db::SimplePolygon (*p * (1.0 / dbu), false);
return db::SimplePolygon (*p, db::VCplxTrans (1.0 / dbu), false, false /*don't remove reflected*/, false /*no normalize*/);
}
static db::SimplePolygon transformed_vplx_sp (const db::DSimplePolygon *p, const db::VCplxTrans &t)
@ -2036,12 +2036,12 @@ static db::Polygon minkowski_sum_pc (const db::Polygon *p, const std::vector<db:
static db::Polygon *polygon_from_dpolygon (const db::DPolygon &p)
{
return new db::Polygon (p, false);
return new db::Polygon (p, false, false /*don't remove reflected*/, false /*no normalize*/);
}
static db::DPolygon polygon_to_dpolygon (const db::Polygon *p, double dbu)
{
return db::DPolygon (*p * dbu, false);
return db::DPolygon (*p, db::CplxTrans (dbu), false, false /*don't remove reflected*/, false /*no normalize*/);
}
static bool is_convex (const db::Polygon *p)
@ -2380,12 +2380,12 @@ Class<db::PolygonWithProperties> decl_PolygonWithProperties (decl_Polygon, "db",
static db::DPolygon *dpolygon_from_ipolygon (const db::Polygon &p)
{
return new db::DPolygon (p, false);
return new db::DPolygon (p, false, false /*don't remove reflected*/, false /*no normalize*/);
}
static db::Polygon dpolygon_to_polygon (const db::DPolygon *p, double dbu)
{
return db::Polygon (*p * (1.0 / dbu), false);
return db::Polygon (*p, db::VCplxTrans (1.0 / dbu), false, false /*don't remove reflected*/, false /*no normalize*/);
}
static db::Polygon transformed_vcplx_dp (const db::DPolygon *p, const db::VCplxTrans &t)

View File

@ -1309,23 +1309,23 @@ tl::Variant complex_op (db::Region *region, db::CompoundRegionOperationNode *nod
static void
fill_region (const db::Region *fr, db::Cell *cell, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Point *origin,
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box)
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box, const db::Region &exclude_area)
{
db::fill_region (cell, *fr, fill_cell_index, fc_box, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons, glue_box);
db::fill_region (cell, *fr, fill_cell_index, fc_box, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons, glue_box, exclude_area);
}
static void
fill_region_skew (const db::Region *fr, db::Cell *cell, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step, const db::Point *origin,
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box)
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box, const db::Region &exclude_area)
{
db::fill_region (cell, *fr, fill_cell_index, fc_box, row_step, column_step, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons, glue_box);
db::fill_region (cell, *fr, fill_cell_index, fc_box, row_step, column_step, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons, glue_box, exclude_area);
}
static void
fill_region_multi (const db::Region *fr, db::Cell *cell, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step,
const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box)
const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box, const db::Region &exclude_area)
{
db::fill_region_repeat (cell, *fr, fill_cell_index, fc_box, row_step, column_step, fill_margin, remaining_polygons, glue_box);
db::fill_region_repeat (cell, *fr, fill_cell_index, fc_box, row_step, column_step, fill_margin, remaining_polygons, glue_box, exclude_area);
}
static db::Region
@ -4340,41 +4340,44 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
gsi::arg ("fill_margin", db::Vector ()),
gsi::arg ("remaining_polygons", (db::Region *)0, "nil"),
gsi::arg ("glue_box", db::Box ()),
gsi::arg ("exclude_area", db::Region (), "empty"),
"@brief A mapping of \\Cell#fill_region to the Region class\n"
"\n"
"This method is equivalent to \\Cell#fill_region, but is based on Region (with the cell being the first parameter).\n"
"\n"
"This method has been introduced in version 0.27.\n"
"This method has been introduced in version 0.27. The 'exclude_area' argument has been added in version 0.30.4.\n"
) +
gsi::method_ext ("fill", &fill_region_skew, gsi::arg ("in_cell"),
gsi::arg ("fill_cell_index"),
gsi::arg ("fc_origin"),
gsi::arg ("fc_bbox"),
gsi::arg ("row_step"),
gsi::arg ("column_step"),
gsi::arg ("origin", &default_origin, "(0, 0)"),
gsi::arg ("remaining_parts", (db::Region *)0, "nil"),
gsi::arg ("fill_margin", db::Vector ()),
gsi::arg ("remaining_polygons", (db::Region *)0, "nil"),
gsi::arg ("glue_box", db::Box ()),
gsi::arg ("glue_box", db::Box ()),
gsi::arg ("exclude_area", db::Region (), "empty"),
"@brief A mapping of \\Cell#fill_region to the Region class\n"
"\n"
"This method is equivalent to \\Cell#fill_region, but is based on Region (with the cell being the first parameter).\n"
"\n"
"This method has been introduced in version 0.27.\n"
"This method has been introduced in version 0.27. The 'exclude_area' argument has been added in version 0.30.4.\n"
) +
gsi::method_ext ("fill_multi", &fill_region_multi, gsi::arg ("in_cell"),
gsi::arg ("fill_cell_index"),
gsi::arg ("fc_origin"),
gsi::arg ("fc_bbox"),
gsi::arg ("row_step"),
gsi::arg ("column_step"),
gsi::arg ("fill_margin", db::Vector ()),
gsi::arg ("remaining_polygons", (db::Region *)0, "nil"),
gsi::arg ("glue_box", db::Box ()),
gsi::arg ("exclude_area", db::Region (), "empty"),
"@brief A mapping of \\Cell#fill_region to the Region class\n"
"\n"
"This method is equivalent to \\Cell#fill_region, but is based on Region (with the cell being the first parameter).\n"
"\n"
"This method has been introduced in version 0.27.\n"
"This method has been introduced in version 0.27. The 'exclude_area' argument has been added in version 0.30.4.\n"
) +
gsi::method_ext ("nets", &nets, gsi::arg ("extracted"), gsi::arg ("net_prop_name", tl::Variant (), "nil"), gsi::arg ("net_filter", (const std::vector<const db::Net *> *) (0), "nil"),
"@brief Pulls the net shapes from a LayoutToNetlist database\n"

View File

@ -156,7 +156,7 @@ Class<db::ViaType> decl_dbViaType ("db", "ViaType",
"@brief If non-zero, the top layer's dimensions will be rounded to this grid.\n"
),
"@brief Describes a via type\n"
"These objects are used by PCellDeclaration#via_types to specify the via types a "
"These objects are used by \\PCellDeclaration#via_types to specify the via types a "
"via PCell is able to provide.\n"
"\n"
"The basic parameters of a via type are bottom and top layers (the layers that are "

View File

@ -377,3 +377,98 @@ TEST(6)
CHECKPOINT();
db::compare_layouts (_this, ly, tl::testdata () + "/algo/fill_tool_au6.oas", db::WriteOAS);
}
// exclude_area
TEST(7)
{
db::Layout ly;
{
std::string fn (tl::testdata ());
fn += "/algo/fill_tool7.gds";
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly);
}
db::cell_index_type fill_cell = ly.cell_by_name ("FILL_CELL").second;
db::cell_index_type top_cell = ly.cell_by_name ("TOP").second;
unsigned int fill_layer = ly.get_layer (db::LayerProperties (1, 0));
db::Region fill_region (db::RecursiveShapeIterator (ly, ly.cell (top_cell), fill_layer));
unsigned int excl_layer = ly.get_layer (db::LayerProperties (2, 0));
db::Region excl_region (db::RecursiveShapeIterator (ly, ly.cell (top_cell), excl_layer));
db::Region remaining_polygons;
db::Vector rs (2500, 0);
db::Vector cs (650, 2500);
db::Box fc_box = ly.cell (fill_cell).bbox ();
db::fill_region (&ly.cell (top_cell), fill_region, fill_cell, fc_box, rs, cs, db::Point (), false, &remaining_polygons, db::Vector (), 0, db::Box (), excl_region);
unsigned int l100 = ly.insert_layer (db::LayerProperties (100, 0));
remaining_polygons.insert_into (&ly, top_cell, l100);
CHECKPOINT();
db::compare_layouts (_this, ly, tl::testdata () + "/algo/fill_tool_au7.oas", db::WriteOAS);
}
// exclude_area
TEST(8)
{
db::Layout ly;
{
std::string fn (tl::testdata ());
fn += "/algo/fill_tool8.gds";
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly);
}
db::cell_index_type fill_cell = ly.cell_by_name ("FILL_CELL").second;
db::cell_index_type top_cell = ly.cell_by_name ("TOP").second;
unsigned int fill_layer = ly.get_layer (db::LayerProperties (1, 0));
db::Region fill_region (db::RecursiveShapeIterator (ly, ly.cell (top_cell), fill_layer));
unsigned int excl_layer = ly.get_layer (db::LayerProperties (2, 0));
db::Region excl_region (db::RecursiveShapeIterator (ly, ly.cell (top_cell), excl_layer));
db::Vector rs (2500, 0);
db::Vector cs (650, 2500);
db::Box fc_box = ly.cell (fill_cell).bbox ();
db::fill_region (&ly.cell (top_cell), fill_region, fill_cell, fc_box, rs, cs, db::Point (), false, 0, db::Vector (), 0, db::Box (), excl_region);
CHECKPOINT();
db::compare_layouts (_this, ly, tl::testdata () + "/algo/fill_tool_au8.oas", db::WriteOAS);
}
// exclude_area
TEST(9)
{
db::Layout ly;
{
std::string fn (tl::testdata ());
fn += "/algo/fill_tool9.gds";
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly);
}
db::cell_index_type fill_cell = ly.cell_by_name ("FILL_CELL").second;
db::cell_index_type top_cell = ly.cell_by_name ("TOP").second;
unsigned int fill_layer = ly.get_layer (db::LayerProperties (1, 0));
db::Region fill_region (db::RecursiveShapeIterator (ly, ly.cell (top_cell), fill_layer));
unsigned int excl_layer = ly.get_layer (db::LayerProperties (2, 0));
db::Region excl_region (db::RecursiveShapeIterator (ly, ly.cell (top_cell), excl_layer));
db::Vector rs (2500, 0);
db::Vector cs (650, 2500);
db::Box fc_box = ly.cell (fill_cell).bbox ();
db::fill_region (&ly.cell (top_cell), fill_region, fill_cell, fc_box, rs, cs, db::Point (), true, 0, db::Vector (), 0, db::Box (), excl_region);
CHECKPOINT();
db::compare_layouts (_this, ly, tl::testdata () + "/algo/fill_tool_au9.oas", db::WriteOAS);
}

View File

@ -294,7 +294,9 @@ TEST(2)
EXPECT_EQ (g.hier_generation_id (), size_t (3));
g.clear ();
EXPECT_EQ (g.update_needed (), true);
g.update ();
EXPECT_EQ (g.update_needed (), false);
el.reset ();
EXPECT_EQ (g.hier_generation_id (), size_t (4));
@ -387,7 +389,9 @@ TEST(3)
EXPECT_EQ (el.hier_dirty, true);
el.reset ();
EXPECT_EQ (g.update_needed (), true);
g.update ();
EXPECT_EQ (g.update_needed (), false);
top->shapes (0).insert (db::Box (0, 0, 10, 20));
top->shapes (1).insert (db::Box (0, 0, 10, 20));

View File

@ -2404,6 +2404,10 @@ TEST(12A)
db::Cell &topcell = layout.cell (*layout.begin_top_down ());
// standalone copy
db::Shapes copy (true);
copy = topcell.shapes (lindex);
db::Shapes::shape_iterator shape = topcell.shapes (lindex).begin (db::Shapes::shape_iterator::All);
while (! shape.at_end ()) {
topcell.shapes (lindex).replace (*shape, db::Box (shape->box ().transformed (db::Trans (1))));
@ -2436,6 +2440,70 @@ TEST(12A)
"box (-1050,150;-150,2150) #112\n"
);
shape = topcell.shapes (lindex).begin (db::Shapes::shape_iterator::All);
while (! shape.at_end ()) {
topcell.shapes (lindex).replace (*shape, db::Box (shape->box ().transformed (db::Trans (1))));
++shape;
}
EXPECT_EQ (shapes_to_string (_this, topcell.shapes (lindex)),
"box (-2000,-1000;0,-100) #0\n"
"box (-2100,-1100;-100,-200) #0\n"
"box (-2150,-1050;-150,-150) #0\n"
"box (-2000,-1000;0,-100) #110\n"
"box (-2100,-1100;-100,-200) #111\n"
"box (-2150,-1050;-150,-150) #112\n"
);
// on standalone shapes
shape = copy.begin (db::Shapes::shape_iterator::All);
while (! shape.at_end ()) {
copy.replace (*shape, db::Box (shape->box ().transformed (db::Trans (1))));
++shape;
}
EXPECT_EQ (shapes_to_string (_this, copy),
"box (-1000,0;-100,2000) #0\n"
"box (-1100,100;-200,2100) #0\n"
"box (-1050,150;-150,2150) #0\n"
"box (-1000,0;-100,2000) #10\n"
"box (-1100,100;-200,2100) #11\n"
"box (-1050,150;-150,2150) #12\n"
);
shape = copy.begin (db::Shapes::shape_iterator::All);
while (! shape.at_end ()) {
if (shape->has_prop_id ()) {
copy.replace_prop_id (*shape, shape->prop_id () + 100);
}
++shape;
}
EXPECT_EQ (shapes_to_string (_this, copy),
"box (-1000,0;-100,2000) #0\n"
"box (-1100,100;-200,2100) #0\n"
"box (-1050,150;-150,2150) #0\n"
"box (-1000,0;-100,2000) #110\n"
"box (-1100,100;-200,2100) #111\n"
"box (-1050,150;-150,2150) #112\n"
);
shape = copy.begin (db::Shapes::shape_iterator::All);
while (! shape.at_end ()) {
copy.replace (*shape, db::Box (shape->box ().transformed (db::Trans (1))));
++shape;
}
EXPECT_EQ (shapes_to_string (_this, copy),
"box (-2000,-1000;0,-100) #0\n"
"box (-2100,-1100;-100,-200) #0\n"
"box (-2150,-1050;-150,-150) #0\n"
"box (-2000,-1000;0,-100) #110\n"
"box (-2100,-1100;-100,-200) #111\n"
"box (-2150,-1050;-150,-150) #112\n"
);
}
}

View File

@ -10,16 +10,16 @@
<p>
Select "Path" mode from the toolbar. The editor options dialog will open that additionally prompts for
basic path parameters, such as width and extension scheme. When a path is being drawn, it will receive
the settings entered into this dialog. The path properties can even be changed, while the path is
being drawn. Don't forget to click "Apply" to take over the current entries. If the dialog has been
closed unintentionally, it can be reopened with the F3 shortcut.
the settings entered into this dialog. The path properties can be changed, while the path is
being drawn. If the option page has been closed unintentionally, it can be reopened with the F3 shortcut.
</p>
<p>
To actually draw a path,
choose a layer from the layer panel in which to create a new box.
choose a layer from the layer panel in which to create a new path.
Left click at the first vertex, move the mouse to the second vertex, click to place this one and continue
to the last vertex. Double-click at the last vertex to finish the path. Press the ESC key to cancel the operation.
Use the backspace key to remove the current segment and go back to the previous segment.
</p>
<p>
@ -30,5 +30,21 @@
also during editing.
</p>
<p>
<keyword name="Via"/>
<keyword name="Vias"/>
Paths are often used for wires. KLayout can be configured to provide
vias that allow switching from one layer to another and place a via
between the layers where the paths overlap. Before you can use the via feature,
KLayout has to be equipped with via definitions. Vias are basically PCells which
need to support a specific set of parameters. Via PCells need to expose a number
of descriptors to define the options supported by a particular PCell (e.g. the
layers which are connected or the type of via if multiple flavors are available).
This scheme allows for a lot of flexibility, but needs some coding skills.
In the macro IDE, a code sample is provided which implements two simple vias
for a three-layer metal stack. For more details see <class_doc href="PCellDeclaration"/>,
method "via_types" and <class_doc href="ViaType"/>.
</p>
</doc>

View File

@ -15,6 +15,7 @@
<topic href="/manual/create_cells.xml"/>
<topic href="/manual/create_polygon.xml"/>
<topic href="/manual/create_box.xml"/>
<topic href="/manual/create_path.xml"/>
<topic href="/manual/create_text.xml"/>
<topic href="/manual/create_instance.xml"/>
<topic href="/manual/move_sel.xml"/>

View File

@ -976,7 +976,7 @@ marker.destroy</pre>
<p>
The PluginFactory itself acts as a singleton per plugin class and provides not only the ability to create
Plugin objects but also a couple of configuration options and a global handler for configuration and menu
events. The configuration includes:
events. The PluginFactory provides:
</p>
<ul>
@ -991,6 +991,10 @@ marker.destroy</pre>
After an option is configured, the individual Plugin objects and the PluginFactory receives "configure" calls
when a configuration option changes or for the initial configuration.
</li>
<li>Widgets: The plugin factory can provide widgets for the configuration dialog ('File/Setup') and the
editor options dock. Respective callbacks are <class_doc href="PluginFactory#create_config_pages"/>
and <class_doc href="PluginFactory#create_editor_options_pages"/>.
</li>
</ul>
<p>
@ -1048,5 +1052,29 @@ marker.destroy</pre>
over the mouse in certain circumstances and is supposed to put the plugin into a "watching" instead of "dragging" state.
</p>
<p>
A plugin may also create markers for visual feedback and highlights. This can be done explicitly
using marker objects (<class_doc href="Marker"/>) or in a application-defined fashion by generating
mouse cursors. The API functions for this purpose are <class_doc href="Plugin#clear_mouse_cursors"/>,
<class_doc href="Plugin#add_mouse_cursor"/> and <class_doc href="Plugin#add_edge_marker"/>. These
functions provide cursors and highlights that match the visual effects of other plugins and
interface with the mouse tracking feature of the application.
</p>
<p>
Another service the Plugin class provides is snapping:
there exist a number of global configuration options which control snapping (grids, snapping to
objects, angle constraints). The plugin offers a number of snap functions that follow the
application's current configuration and implement snapping accordingly. These methods are
<class_doc href="Plugin#snap"/> and <class_doc href="Plugin#snap2"/>. While the first
method provides grid and angle snapping, the second also implements snapping to layout objects.
</p>
<p>
The "drag box" sample macro demonstrates many of these features.
The sample is available from the macro templates when you create a new
macro in the macro IDE.
</p>
</doc>

View File

@ -644,6 +644,10 @@ module DRC
DRCFillStep::new(false, x, y)
end
def fill_exclude(excl)
DRCFillExclude::new(excl)
end
def auto_origin
DRCFillOrigin::new
end

View File

@ -5755,6 +5755,8 @@ CODE
# @li @b multi_origin @/b: lets the algorithm choose the origin and repeats the fill with different origins
# until no further fill cell can be fitted. @/li
# @li @b fill_pattern(..) @/b: specifies the fill pattern. @/li
# @li @b fill_exclude(excl) @/b: specifies a fill exclude region ('excl' is a polygon layer). This is conceptually
# equivalent to subtracting that layer from the fill region, but usually more efficient. @/li
# @/ul
#
# "fill_pattern" generates a fill pattern object. This object is used for configuring the fill pattern
@ -5895,6 +5897,7 @@ CODE
pattern = nil
origin = RBA::DPoint::new
repeat = false
excl = nil
args.each_with_index do |a,ai|
if a.is_a?(DRCSource)
@ -5907,6 +5910,11 @@ CODE
raise("Duplicate fill pattern specification for '#{m}' at argument ##{ai+1}")
end
pattern = a
elsif a.is_a?(DRCFillExclude)
if excl
raise("Duplicate exclude region specification for '#{m}' at argument ##{ai+1}")
end
excl = a.excl
elsif a.is_a?(DRCFillStep)
if a.for_row
if row_step
@ -5938,6 +5946,10 @@ CODE
column_step = RBA::DVector::new(0, pattern.default_ypitch)
end
if !excl
excl = RBA::Region::new
end
dbu_trans = RBA::VCplxTrans::new(1.0 / @engine.dbu)
result = nil
@ -5988,6 +6000,7 @@ CODE
tp.var("fc_index", fc_index)
tp.var("repeat", repeat)
tp.var("with_left", with_left)
tp.var("excl", excl)
tp.queue(<<"END")
var tc_box = _frame.bbox;
@ -5997,8 +6010,8 @@ CODE
tile_box = tile_box & tc_box;
var left = with_left ? Region.new : nil;
repeat ?
(region & tile_box).fill_multi(top_cell, fc_index, fc_box, rs, cs, fill_margin, left, _tile.bbox) :
(region & tile_box).fill(top_cell, fc_index, fc_box, rs, cs, origin, left, fill_margin, left, _tile.bbox);
(region & tile_box).fill_multi(top_cell, fc_index, fc_box, rs, cs, fill_margin, left, _tile.bbox, excl) :
(region & tile_box).fill(top_cell, fc_index, fc_box, rs, cs, origin, left, fill_margin, left, _tile.bbox, excl);
with_left && _output(#{result_arg}, left)
)
END
@ -6020,9 +6033,9 @@ END
@engine.run_timed("\"#{m}\" in: #{@engine.src_line}", self.data) do
if repeat
self.data.fill_multi(top_cell, fc_index, fc_box, rs, cs, fill_margin, result)
self.data.fill_multi(top_cell, fc_index, fc_box, rs, cs, fill_margin, result, RBA::Box::new, excl)
else
self.data.fill(top_cell, fc_index, fc_box, rs, cs, origin, result, fill_margin, result)
self.data.fill(top_cell, fc_index, fc_box, rs, cs, origin, result, fill_margin, result, RBA::Box::new, excl)
end
end

View File

@ -475,6 +475,18 @@ module DRC
end
# A wrapper for the fill step definition
class DRCFillExclude
def initialize(excl)
excl.is_a?(DRCLayer) || raise("Exclude layer argument needs to be a DRC layer")
excl.requires_region("Exclude layer")
@excl = excl.data
end
def excl
@excl
end
end
# A wrapper for the fill step definition
class DRCFillStep
def initialize(for_row, x, y = nil)

View File

@ -1453,6 +1453,16 @@ TEST(47bd_fillWithUsingOutputDeep)
run_test (_this, "47b", true);
}
TEST(47c_fillWithExcludeArea)
{
run_test (_this, "47c", false);
}
TEST(47cd_fillWithExcludeAreaDeep)
{
run_test (_this, "47c", true);
}
TEST(48_drcWithFragments)
{
run_test (_this, "48", false);

View File

@ -419,8 +419,6 @@
</layout>
</widget>
<tabstops>
<tabstop>all_layers_rb</tabstop>
<tabstop>visible_layers_rb</tabstop>
<tabstop>h_none_rb</tabstop>
<tabstop>h_left_rb</tabstop>
<tabstop>h_center_rb</tabstop>
@ -429,7 +427,8 @@
<tabstop>v_top_rb</tabstop>
<tabstop>v_center_rb</tabstop>
<tabstop>v_bottom_rb</tabstop>
<tabstop>buttonBox</tabstop>
<tabstop>all_layers_rb</tabstop>
<tabstop>visible_layers_rb</tabstop>
</tabstops>
<resources>
<include location="../../icons/icons.qrc"/>

View File

@ -108,6 +108,10 @@ The perimeter calculation only takes true outside edges into account. Internal e
</item>
</layout>
</widget>
<tabstops>
<tabstop>area_le</tabstop>
<tabstop>perimeter_le</tabstop>
</tabstops>
<resources/>
<connections>
<connection>

View File

@ -1,7 +1,8 @@
<ui version="4.0" >
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BoxPropertiesPage</class>
<widget class="QWidget" name="BoxPropertiesPage" >
<property name="geometry" >
<widget class="QWidget" name="BoxPropertiesPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
@ -9,63 +10,60 @@
<height>370</height>
</rect>
</property>
<property name="windowTitle" >
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin" stdset="0">
<number>9</number>
</property>
<item>
<widget class="QFrame" name="frame1" >
<property name="frameShape" >
<widget class="QFrame" name="frame1">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow" >
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin" stdset="0">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_12" >
<property name="font" >
<widget class="QLabel" name="label_12">
<property name="font">
<font>
<family>Sans Serif</family>
<pointsize>12</pointsize>
<weight>75</weight>
<italic>false</italic>
<bold>true</bold>
<underline>false</underline>
<strikeout>false</strikeout>
</font>
</property>
<property name="text" >
<property name="text">
<string>Box Properties</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="layer_lbl" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>5</vsizetype>
<widget class="QLabel" name="layer_lbl">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<property name="text">
<string/>
</property>
<property name="alignment" >
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
@ -75,13 +73,13 @@
</item>
<item>
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType" >
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
@ -90,420 +88,396 @@
</spacer>
</item>
<item>
<widget class="QTabWidget" name="mode_tab" >
<property name="currentIndex" >
<widget class="QTabWidget" name="mode_tab">
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="tab_4" >
<attribute name="title" >
<widget class="QWidget" name="tab_4">
<attribute name="title">
<string>Corners</string>
</attribute>
<layout class="QGridLayout" >
<property name="margin" >
<layout class="QGridLayout">
<property name="margin" stdset="0">
<number>9</number>
</property>
<property name="spacing" >
<property name="spacing">
<number>6</number>
</property>
<item row="1" column="1" >
<widget class="QLabel" name="label_16" >
<property name="text" >
<item row="1" column="1">
<widget class="QLabel" name="label_16">
<property name="text">
<string>x = </string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="5" >
<widget class="Line" name="line_2" >
<property name="orientation" >
<item row="2" column="0" colspan="5">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="4" >
<widget class="QLineEdit" name="y2_le_1" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>0</vsizetype>
<item row="1" column="4">
<widget class="QLineEdit" name="y2_le_1">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="2" >
<widget class="QLineEdit" name="x1_le_1" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>0</vsizetype>
<item row="0" column="2">
<widget class="QLineEdit" name="x1_le_1">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="4" column="3" >
<widget class="QLabel" name="label_13" >
<property name="text" >
<item row="4" column="3">
<widget class="QLabel" name="label_13">
<property name="text">
<string>h = </string>
</property>
</widget>
</item>
<item row="1" column="2" >
<widget class="QLineEdit" name="x2_le_1" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>0</vsizetype>
<item row="1" column="2">
<widget class="QLineEdit" name="x2_le_1">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="4" column="0" >
<widget class="QLabel" name="label_17" >
<property name="text" >
<item row="4" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Width/height</string>
</property>
</widget>
</item>
<item row="0" column="4" >
<widget class="QLineEdit" name="y1_le_1" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>0</vsizetype>
<item row="0" column="4">
<widget class="QLineEdit" name="y1_le_1">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="4" column="1" >
<widget class="QLabel" name="label_18" >
<property name="text" >
<item row="4" column="1">
<widget class="QLabel" name="label_18">
<property name="text">
<string>w = </string>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="label_15" >
<property name="text" >
<item row="1" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Upper right (x/y) </string>
</property>
</widget>
</item>
<item row="4" column="2" >
<widget class="QLineEdit" name="w_le_1" >
<property name="enabled" >
<item row="4" column="2">
<widget class="QLineEdit" name="w_le_1">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>0</vsizetype>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly" >
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0" >
<widget class="QLabel" name="label_4" >
<property name="text" >
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Lower left (x/y)</string>
</property>
</widget>
</item>
<item row="1" column="3" >
<widget class="QLabel" name="label_11" >
<property name="text" >
<item row="1" column="3">
<widget class="QLabel" name="label_11">
<property name="text">
<string>y = </string>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="QLabel" name="label_14" >
<property name="text" >
<item row="0" column="1">
<widget class="QLabel" name="label_14">
<property name="text">
<string>x = </string>
</property>
</widget>
</item>
<item row="4" column="4" >
<widget class="QLineEdit" name="h_le_1" >
<property name="enabled" >
<item row="4" column="4">
<widget class="QLineEdit" name="h_le_1">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>0</vsizetype>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly" >
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="3" >
<widget class="QLabel" name="label_19" >
<property name="text" >
<item row="0" column="3">
<widget class="QLabel" name="label_19">
<property name="text">
<string>y = </string>
</property>
</widget>
</item>
<item row="3" column="0" >
<widget class="QLabel" name="label_20" >
<property name="text" >
<item row="3" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Center (x/y)</string>
</property>
</widget>
</item>
<item row="3" column="1" >
<widget class="QLabel" name="label_21" >
<property name="text" >
<item row="3" column="1">
<widget class="QLabel" name="label_21">
<property name="text">
<string>x = </string>
</property>
</widget>
</item>
<item row="3" column="3" >
<widget class="QLabel" name="label_22" >
<property name="text" >
<item row="3" column="3">
<widget class="QLabel" name="label_22">
<property name="text">
<string>y = </string>
</property>
</widget>
</item>
<item row="3" column="2" >
<widget class="QLineEdit" name="cx_le_1" >
<property name="enabled" >
<item row="3" column="2">
<widget class="QLineEdit" name="cx_le_1">
<property name="enabled">
<bool>false</bool>
</property>
<property name="readOnly" >
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="4" >
<widget class="QLineEdit" name="cy_le_1" >
<property name="enabled" >
<item row="3" column="4">
<widget class="QLineEdit" name="cy_le_1">
<property name="enabled">
<bool>false</bool>
</property>
<property name="readOnly" >
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_3" >
<attribute name="title" >
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>Center/Size</string>
</attribute>
<layout class="QGridLayout" >
<property name="margin" >
<layout class="QGridLayout">
<property name="margin" stdset="0">
<number>9</number>
</property>
<property name="spacing" >
<property name="spacing">
<number>6</number>
</property>
<item row="3" column="4" >
<widget class="QLineEdit" name="y1_le_2" >
<property name="enabled" >
<item row="3" column="4">
<widget class="QLineEdit" name="y1_le_2">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>0</vsizetype>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly" >
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="4" >
<widget class="QLineEdit" name="cy_le_2" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>0</vsizetype>
<item row="0" column="4">
<widget class="QLineEdit" name="cy_le_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="label_2" >
<property name="text" >
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Size (w/h)</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="5" >
<widget class="Line" name="line" >
<property name="orientation" >
<item row="2" column="0" colspan="5">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="0" column="2" >
<widget class="QLineEdit" name="cx_le_2" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>0</vsizetype>
<item row="0" column="2">
<widget class="QLineEdit" name="cx_le_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="QLabel" name="label_6" >
<property name="text" >
<item row="1" column="1">
<widget class="QLabel" name="label_6">
<property name="text">
<string>w = </string>
</property>
</widget>
</item>
<item row="0" column="0" >
<widget class="QLabel" name="label" >
<property name="text" >
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Center (x/y)</string>
</property>
</widget>
</item>
<item row="1" column="4" >
<widget class="QLineEdit" name="h_le_2" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>0</vsizetype>
<item row="1" column="4">
<widget class="QLineEdit" name="h_le_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="3" column="0" >
<widget class="QLabel" name="label_3" >
<property name="text" >
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Lower left (x/y)</string>
</property>
</widget>
</item>
<item row="3" column="3" >
<widget class="QLabel" name="label_10" >
<property name="text" >
<item row="3" column="3">
<widget class="QLabel" name="label_10">
<property name="text">
<string>y = </string>
</property>
</widget>
</item>
<item row="0" column="3" >
<widget class="QLabel" name="label_7" >
<property name="text" >
<item row="0" column="3">
<widget class="QLabel" name="label_7">
<property name="text">
<string>y = </string>
</property>
</widget>
</item>
<item row="1" column="2" >
<widget class="QLineEdit" name="w_le_2" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>0</vsizetype>
<item row="1" column="2">
<widget class="QLineEdit" name="w_le_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="3" >
<widget class="QLabel" name="label_8" >
<property name="text" >
<item row="1" column="3">
<widget class="QLabel" name="label_8">
<property name="text">
<string>h = </string>
</property>
</widget>
</item>
<item row="3" column="2" >
<widget class="QLineEdit" name="x1_le_2" >
<property name="enabled" >
<item row="3" column="2">
<widget class="QLineEdit" name="x1_le_2">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>0</vsizetype>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly" >
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="1" >
<widget class="QLabel" name="label_9" >
<property name="text" >
<item row="3" column="1">
<widget class="QLabel" name="label_9">
<property name="text">
<string>x = </string>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="QLabel" name="label_5" >
<property name="text" >
<item row="0" column="1">
<widget class="QLabel" name="label_5">
<property name="text">
<string>x = </string>
</property>
</widget>
</item>
<item row="4" column="2" >
<widget class="QLineEdit" name="x2_le_2" >
<property name="enabled" >
<item row="4" column="2">
<widget class="QLineEdit" name="x2_le_2">
<property name="enabled">
<bool>false</bool>
</property>
<property name="readOnly" >
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="4" >
<widget class="QLineEdit" name="y2_le_2" >
<property name="enabled" >
<item row="4" column="4">
<widget class="QLineEdit" name="y2_le_2">
<property name="enabled">
<bool>false</bool>
</property>
<property name="readOnly" >
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0" >
<widget class="QLabel" name="label_23" >
<property name="text" >
<item row="4" column="0">
<widget class="QLabel" name="label_23">
<property name="text">
<string>Upper right (x/y) </string>
</property>
</widget>
</item>
<item row="4" column="1" >
<widget class="QLabel" name="label_24" >
<property name="text" >
<item row="4" column="1">
<widget class="QLabel" name="label_24">
<property name="text">
<string>x = </string>
</property>
</widget>
</item>
<item row="4" column="3" >
<widget class="QLabel" name="label_25" >
<property name="text" >
<item row="4" column="3">
<widget class="QLabel" name="label_25">
<property name="text">
<string>y = </string>
</property>
</widget>
@ -514,13 +488,13 @@
</item>
<item>
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType" >
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>478</width>
<height>10</height>
@ -529,25 +503,25 @@
</spacer>
</item>
<item>
<widget class="QCheckBox" name="dbu_cb" >
<property name="text" >
<widget class="QCheckBox" name="dbu_cb">
<property name="text">
<string>Coordinates in database units</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="abs_cb" >
<property name="text" >
<widget class="QCheckBox" name="abs_cb">
<property name="text">
<string>Absolute (accumulated) transformations</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>478</width>
<height>16</height>
@ -556,26 +530,26 @@
</spacer>
</item>
<item>
<widget class="QFrame" name="frame2" >
<property name="frameShape" >
<widget class="QFrame" name="frame2">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow" >
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin" stdset="0">
<number>0</number>
</property>
<item>
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>211</width>
<height>20</height>
@ -584,15 +558,15 @@
</spacer>
</item>
<item>
<widget class="QPushButton" name="prop_pb" >
<property name="text" >
<widget class="QPushButton" name="prop_pb">
<property name="text">
<string>User Properties</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="inst_pb" >
<property name="text" >
<widget class="QPushButton" name="inst_pb">
<property name="text">
<string>Instantiation</string>
</property>
</widget>

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>164</height>
<width>464</width>
<height>180</height>
</rect>
</property>
<property name="windowTitle">
@ -99,6 +99,11 @@
</item>
</layout>
</widget>
<tabstops>
<tabstop>shallow_rb</tabstop>
<tabstop>deep_rb</tabstop>
<tabstop>dont_ask_cbx</tabstop>
</tabstops>
<resources/>
<connections>
<connection>

View File

@ -671,9 +671,22 @@
</layout>
</widget>
<tabstops>
<tabstop>h_distribute</tabstop>
<tabstop>h_pitch</tabstop>
<tabstop>h_space</tabstop>
<tabstop>v_distribute</tabstop>
<tabstop>v_pitch</tabstop>
<tabstop>v_space</tabstop>
<tabstop>h_none_rb</tabstop>
<tabstop>h_left_rb</tabstop>
<tabstop>h_center_rb</tabstop>
<tabstop>h_right_rb</tabstop>
<tabstop>v_none_rb</tabstop>
<tabstop>v_top_rb</tabstop>
<tabstop>v_center_rb</tabstop>
<tabstop>v_bottom_rb</tabstop>
<tabstop>all_layers_rb</tabstop>
<tabstop>visible_layers_rb</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources>
<include location="../../icons/icons.qrc"/>

View File

@ -470,6 +470,7 @@
<tabstop>grid_cb</tabstop>
<tabstop>edit_grid_le</tabstop>
<tabstop>snap_objects_cbx</tabstop>
<tabstop>snap_objects_to_grid_cbx</tabstop>
<tabstop>conn_angle_cb</tabstop>
<tabstop>move_angle_cb</tabstop>
<tabstop>hier_sel_cbx</tabstop>

View File

@ -268,6 +268,13 @@
</item>
</layout>
</widget>
<tabstops>
<tabstop>scrollArea</tabstop>
<tabstop>width_le</tabstop>
<tabstop>type_cb</tabstop>
<tabstop>start_ext_le</tabstop>
<tabstop>end_ext_le</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -42,8 +42,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>542</width>
<height>243</height>
<width>528</width>
<height>248</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
@ -292,6 +292,13 @@
</item>
</layout>
</widget>
<tabstops>
<tabstop>scrollArea</tabstop>
<tabstop>text_le</tabstop>
<tabstop>halign_cbx</tabstop>
<tabstop>valign_cbx</tabstop>
<tabstop>size_le</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>666</width>
<height>649</height>
<height>668</height>
</rect>
</property>
<property name="windowTitle">
@ -182,7 +182,7 @@
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
<number>1</number>
</property>
<widget class="QWidget" name="tab">
<property name="sizePolicy">

View File

@ -365,6 +365,19 @@
</item>
</layout>
</widget>
<tabstops>
<tabstop>cell_name_le</tabstop>
<tabstop>origin_groupbox</tabstop>
<tabstop>lt</tabstop>
<tabstop>ct</tabstop>
<tabstop>rt</tabstop>
<tabstop>lc</tabstop>
<tabstop>cc</tabstop>
<tabstop>rc</tabstop>
<tabstop>lb</tabstop>
<tabstop>cb</tabstop>
<tabstop>rb</tabstop>
</tabstops>
<resources>
<include location="../../icons/icons.qrc"/>
</resources>

View File

@ -272,6 +272,8 @@
</layout>
</widget>
<tabstops>
<tabstop>x_le</tabstop>
<tabstop>y_le</tabstop>
<tabstop>dbu_cb</tabstop>
<tabstop>abs_cb</tabstop>
<tabstop>prop_pb</tabstop>

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>469</width>
<width>472</width>
<height>271</height>
</rect>
</property>
@ -138,6 +138,12 @@ Leave empty to get the same radius than for outer corners)</string>
</item>
</layout>
</widget>
<tabstops>
<tabstop>amend_cb</tabstop>
<tabstop>router_le</tabstop>
<tabstop>rinner_le</tabstop>
<tabstop>points_le</tabstop>
</tabstops>
<resources/>
<connections>
<connection>

View File

@ -1,52 +1,51 @@
<ui version="4.0" >
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TextPropertiesPage</class>
<widget class="QWidget" name="TextPropertiesPage" >
<property name="geometry" >
<widget class="QWidget" name="TextPropertiesPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>555</width>
<height>366</height>
<width>568</width>
<height>375</height>
</rect>
</property>
<property name="windowTitle" >
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" >
<property name="margin" >
<layout class="QGridLayout">
<property name="margin" stdset="0">
<number>9</number>
</property>
<property name="spacing" >
<property name="spacing">
<number>6</number>
</property>
<item row="2" column="3" >
<widget class="QLabel" name="label_7" >
<property name="text" >
<item row="2" column="3">
<widget class="QLabel" name="label_7">
<property name="text">
<string>y = </string>
</property>
</widget>
</item>
<item row="2" column="2" >
<widget class="QLineEdit" name="x_le" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>0</vsizetype>
<item row="2" column="2">
<widget class="QLineEdit" name="x_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="3" column="0" colspan="5" >
<item row="3" column="0" colspan="5">
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType" >
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>8</height>
@ -54,19 +53,19 @@
</property>
</spacer>
</item>
<item row="6" column="0" >
<widget class="QLabel" name="Text size" >
<property name="text" >
<item row="6" column="0">
<widget class="QLabel" name="Text size">
<property name="text">
<string>Text size</string>
</property>
</widget>
</item>
<item row="11" column="0" colspan="5" >
<item row="11" column="0" colspan="5">
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>457</width>
<height>51</height>
@ -74,22 +73,22 @@
</property>
</spacer>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="label_2" >
<property name="text" >
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Text</string>
</property>
</widget>
</item>
<item row="8" column="0" colspan="5" >
<item row="8" column="0" colspan="5">
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType" >
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>431</width>
<height>8</height>
@ -97,34 +96,34 @@
</property>
</spacer>
</item>
<item row="2" column="1" >
<widget class="QLabel" name="label_5" >
<property name="text" >
<item row="2" column="1">
<widget class="QLabel" name="label_5">
<property name="text">
<string>x = </string>
</property>
</widget>
</item>
<item row="12" column="0" colspan="5" >
<widget class="QFrame" name="button_frame" >
<property name="frameShape" >
<item row="12" column="0" colspan="5">
<widget class="QFrame" name="button_frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow" >
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin" stdset="0">
<number>0</number>
</property>
<item>
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>211</width>
<height>20</height>
@ -133,15 +132,15 @@
</spacer>
</item>
<item>
<widget class="QPushButton" name="prop_pb" >
<property name="text" >
<widget class="QPushButton" name="prop_pb">
<property name="text">
<string>User Properties</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="inst_pb" >
<property name="text" >
<widget class="QPushButton" name="inst_pb">
<property name="text">
<string>Instantiation</string>
</property>
</widget>
@ -149,121 +148,114 @@
</layout>
</widget>
</item>
<item row="7" column="1" colspan="4" >
<widget class="QLabel" name="label_3" >
<property name="text" >
<item row="7" column="1" colspan="4">
<widget class="QLabel" name="label_3">
<property name="text">
<string>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</string>
</property>
</widget>
</item>
<item row="4" column="0" >
<widget class="QLabel" name="Orientation" >
<property name="text" >
<item row="4" column="0">
<widget class="QLabel" name="Orientation">
<property name="text">
<string>Orientation</string>
</property>
</widget>
</item>
<item row="2" column="0" >
<widget class="QLabel" name="label" >
<property name="text" >
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Position (x/y)</string>
</property>
</widget>
</item>
<item row="6" column="3" colspan="2" >
<widget class="QLabel" name="label_6" >
<property name="text" >
<item row="6" column="3" colspan="2">
<widget class="QLabel" name="label_6">
<property name="text">
<string>(Leave empty for default)</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="4" >
<widget class="QLineEdit" name="text_le" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>0</vsizetype>
<item row="1" column="1" colspan="4">
<widget class="QLineEdit" name="text_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="10" column="2" colspan="3" >
<widget class="QCheckBox" name="abs_cb" >
<property name="text" >
<item row="10" column="2" colspan="3">
<widget class="QCheckBox" name="abs_cb">
<property name="text">
<string>Absolute (accumulated) transformations</string>
</property>
</widget>
</item>
<item row="2" column="4" >
<widget class="QLineEdit" name="y_le" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>0</vsizetype>
<item row="2" column="4">
<widget class="QLineEdit" name="y_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="9" column="2" colspan="3" >
<widget class="QCheckBox" name="dbu_cb" >
<property name="text" >
<item row="9" column="2" colspan="3">
<widget class="QCheckBox" name="dbu_cb">
<property name="text">
<string>Coordinates in database units</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="5" >
<widget class="QFrame" name="frame" >
<property name="frameShape" >
<item row="0" column="0" colspan="5">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow" >
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin" stdset="0">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_12" >
<property name="font" >
<widget class="QLabel" name="label_12">
<property name="font">
<font>
<family>Sans Serif</family>
<pointsize>12</pointsize>
<weight>75</weight>
<italic>false</italic>
<bold>true</bold>
<underline>false</underline>
<strikeout>false</strikeout>
</font>
</property>
<property name="text" >
<property name="text">
<string>Text Properties</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="layer_lbl" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>5</vsizetype>
<widget class="QLabel" name="layer_lbl">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<property name="text">
<string/>
</property>
<property name="alignment" >
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
@ -271,141 +263,149 @@ to show text objects scaled and rotated</string>
</layout>
</widget>
</item>
<item row="4" column="1" colspan="2" >
<widget class="QComboBox" name="orient_cbx" >
<item row="4" column="1" colspan="2">
<widget class="QComboBox" name="orient_cbx">
<item>
<property name="text" >
<property name="text">
<string>(r0)</string>
</property>
<property name="icon" >
<iconset resource="layResources.qrc" >:/r0_24px.png</iconset>
<property name="icon">
<iconset resource="../../icons/icons.qrc">
<normaloff>:/r0_24px.png</normaloff>:/r0_24px.png</iconset>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>(r90)</string>
</property>
<property name="icon" >
<iconset resource="layResources.qrc" >:/r90_24px.png</iconset>
<property name="icon">
<iconset resource="../../icons/icons.qrc">
<normaloff>:/r90_24px.png</normaloff>:/r90_24px.png</iconset>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>(r180)</string>
</property>
<property name="icon" >
<iconset resource="layResources.qrc" >:/r180_24px.png</iconset>
<property name="icon">
<iconset resource="../../icons/icons.qrc">
<normaloff>:/r180_24px.png</normaloff>:/r180_24px.png</iconset>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>(r270)</string>
</property>
<property name="icon" >
<iconset resource="layResources.qrc" >:/r270_24px.png</iconset>
<property name="icon">
<iconset resource="../../icons/icons.qrc">
<normaloff>:/r270_24px.png</normaloff>:/r270_24px.png</iconset>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>(m0)</string>
</property>
<property name="icon" >
<iconset resource="layResources.qrc" >:/m0_24px.png</iconset>
<property name="icon">
<iconset resource="../../icons/icons.qrc">
<normaloff>:/m0_24px.png</normaloff>:/m0_24px.png</iconset>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>(m45)</string>
</property>
<property name="icon" >
<iconset resource="layResources.qrc" >:/m45_24px.png</iconset>
<property name="icon">
<iconset resource="../../icons/icons.qrc">
<normaloff>:/m45_24px.png</normaloff>:/m45_24px.png</iconset>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>(m90)</string>
</property>
<property name="icon" >
<iconset resource="layResources.qrc" >:/m90_24px.png</iconset>
<property name="icon">
<iconset resource="../../icons/icons.qrc">
<normaloff>:/m90_24px.png</normaloff>:/m90_24px.png</iconset>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>(m135)</string>
</property>
<property name="icon" >
<iconset resource="layResources.qrc" >:/m135_24px.png</iconset>
<property name="icon">
<iconset resource="../../icons/icons.qrc">
<normaloff>:/m135_24px.png</normaloff>:/m135_24px.png</iconset>
</property>
</item>
</widget>
</item>
<item row="6" column="1" colspan="2" >
<widget class="QLineEdit" name="size_le" />
<item row="6" column="1" colspan="2">
<widget class="QLineEdit" name="size_le"/>
</item>
<item row="5" column="2" >
<widget class="QComboBox" name="halign_cbx" >
<item row="5" column="2">
<widget class="QComboBox" name="halign_cbx">
<item>
<property name="text" >
<property name="text">
<string>(Default)</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>Left</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>Center</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>Right</string>
</property>
</item>
</widget>
</item>
<item row="5" column="0" >
<widget class="QLabel" name="label_4" >
<property name="text" >
<item row="5" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Alignment</string>
</property>
</widget>
</item>
<item row="5" column="1" >
<widget class="QLabel" name="label_8" >
<property name="text" >
<item row="5" column="1">
<widget class="QLabel" name="label_8">
<property name="text">
<string>h =</string>
</property>
</widget>
</item>
<item row="5" column="3" >
<widget class="QLabel" name="label_9" >
<property name="text" >
<item row="5" column="3">
<widget class="QLabel" name="label_9">
<property name="text">
<string>v =</string>
</property>
</widget>
</item>
<item row="5" column="4" >
<widget class="QComboBox" name="valign_cbx" >
<item row="5" column="4">
<widget class="QComboBox" name="valign_cbx">
<item>
<property name="text" >
<property name="text">
<string>(Default)</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>Top</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>Center</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>Bottom</string>
</property>
</item>
@ -427,7 +427,7 @@ to show text objects scaled and rotated</string>
<tabstop>inst_pb</tabstop>
</tabstops>
<resources>
<include location="layResources.qrc" />
<include location="../../icons/icons.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -80,7 +80,6 @@ HEADERS += \
edtPartialService.h \
edtPlugin.h \
edtService.h \
edtUtils.h \
edtCommon.h \
edtDistribute.h \
@ -91,7 +90,6 @@ SOURCES += \
edtPartialService.cc \
edtPlugin.cc \
edtService.cc \
edtUtils.cc \
gsiDeclEdt.cc \
edtDistribute.cc \

View File

@ -108,10 +108,37 @@ BoxService::do_mouse_move_inactive (const db::DPoint &p)
void
BoxService::do_mouse_move (const db::DPoint &p)
{
do_mouse_move_inactive (p);
lay::PointSnapToObjectResult snap_details = snap2_details (p);
db::DPoint ps = snap_details.snapped_point;
if (snap_details.object_snap == lay::PointSnapToObjectResult::NoObject) {
clear_mouse_cursors ();
db::DPoint px (p.x (), m_p1.y ());
lay::PointSnapToObjectResult snap_details_x = snap2_details (px);
db::DPoint py (m_p1.x (), p.y ());
lay::PointSnapToObjectResult snap_details_y = snap2_details (py);
if (snap_details_x.object_snap != lay::PointSnapToObjectResult::NoObject) {
ps = db::DPoint (snap_details_x.snapped_point.x (), ps.y ());
mouse_cursor_from_snap_details (snap_details_x, true /*add*/);
}
if (snap_details_y.object_snap != lay::PointSnapToObjectResult::NoObject) {
ps = db::DPoint (ps.x (), snap_details_y.snapped_point.y ());
mouse_cursor_from_snap_details (snap_details_y, true /*add*/);
}
add_mouse_cursor (ps);
} else {
mouse_cursor_from_snap_details (snap_details);
}
set_cursor (lay::Cursor::cross);
m_p2 = snap2 (p);
m_p2 = ps;
update_marker ();
}

View File

@ -26,7 +26,7 @@
#include "dbLayout.h"
#include "edtDialogs.h"
#include "edtUtils.h"
#include "layEditorUtils.h"
#include "layObjectInstPath.h"
#include "layCellView.h"
#include "layLayoutViewBase.h"

View File

@ -725,7 +725,7 @@ EditorOptionsInstPCellParam::apply (lay::Dispatcher *root)
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)));
param = lay::pcell_parameters_to_string (pc_decl->named_parameters (mp_pcell_parameters->get_parameters (&ok)));
}
}
}

View File

@ -197,7 +197,7 @@ InstService::sync_to_config ()
dispatcher ()->config_set (cfg_edit_inst_lib_name, m_lib_name);
dispatcher ()->config_set (cfg_edit_inst_cell_name, m_cell_or_pcell_name);
if (m_is_pcell) {
dispatcher ()->config_set (cfg_edit_inst_pcell_parameters, pcell_parameters_to_string (m_pcell_parameters));
dispatcher ()->config_set (cfg_edit_inst_pcell_parameters, lay::pcell_parameters_to_string (m_pcell_parameters));
} else {
dispatcher ()->config_set (cfg_edit_inst_pcell_parameters, std::string ());
}
@ -531,7 +531,7 @@ InstService::configure (const std::string &name, const std::string &value)
if (name == cfg_edit_inst_pcell_parameters) {
std::map<std::string, tl::Variant> pcp = pcell_parameters_from_string (value);
std::map<std::string, tl::Variant> pcp = lay::pcell_parameters_from_string (value);
if (pcp != m_pcell_parameters) {
m_pcell_parameters = pcp;
@ -772,7 +772,7 @@ InstService::config_finalize ()
// 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));
dispatcher ()->config_set (cfg_edit_inst_pcell_parameters, lay::pcell_parameters_to_string (m_pcell_parameters));
} else {
dispatcher ()->config_set (cfg_edit_inst_pcell_parameters, std::string ());
}

View File

@ -29,10 +29,10 @@
#include "layPlugin.h"
#include "layViewObject.h"
#include "layMarker.h"
#include "layEditorUtils.h"
#include "dbLayout.h"
#include "dbShape.h"
#include "dbClipboard.h"
#include "edtUtils.h"
#include <set>
#include <vector>

View File

@ -61,7 +61,7 @@ MoveTrackerService::issue_edit_events ()
call_editor_hooks (m_editor_hooks, &edt::EditorHooks::begin_edits);
// build the transformation variants cache
TransformationVariants tv (view ());
lay::TransformationVariants tv (view ());
std::vector<edt::Service *> services = view ()->get_plugins<edt::Service> ();
std::vector<lay::ObjectInstPath> sel;

View File

@ -1213,7 +1213,7 @@ PartialService::timeout ()
partial_objects::const_iterator r = transient_selection.begin ();
// build the transformation variants cache
TransformationVariants tv (view ());
lay::TransformationVariants tv (view ());
const lay::CellView &cv = view ()->cellview (r->first.cv_index ());
@ -1468,7 +1468,7 @@ PartialService::issue_editor_hook_calls (const tl::weak_collection<edt::EditorHo
db::DTrans move_trans = db::DTrans (m_current - m_start);
// build the transformation variants cache
TransformationVariants tv (view ());
lay::TransformationVariants tv (view ());
// Issue editor hook calls for the shape modification events
@ -1559,7 +1559,7 @@ PartialService::issue_editor_hook_calls (const tl::weak_collection<edt::EditorHo
}
db::Shape
PartialService::modify_shape (TransformationVariants &tv, const db::Shape &shape_in, const lay::ObjectInstPath &path, const std::set <EdgeWithIndex> &sel, const db::DTrans &move_trans, std::map <EdgeWithIndex, db::Edge> &new_edges, std::map <PointWithIndex, db::Point> &new_points)
PartialService::modify_shape (lay::TransformationVariants &tv, const db::Shape &shape_in, const lay::ObjectInstPath &path, const std::set <EdgeWithIndex> &sel, const db::DTrans &move_trans, std::map <EdgeWithIndex, db::Edge> &new_edges, std::map <PointWithIndex, db::Point> &new_points)
{
tl_assert (shape_in.shapes () != 0);
db::Shape shape = shape_in;
@ -1642,7 +1642,7 @@ void
PartialService::transform_selection (const db::DTrans &move_trans)
{
// build the transformation variants cache
TransformationVariants tv (view ());
lay::TransformationVariants tv (view ());
// since a shape reference may become invalid while moving it and
// because it creates ambiguities, we treat each shape separately:
@ -1798,7 +1798,7 @@ PartialService::mouse_move_event (const db::DPoint &p, unsigned int buttons, boo
set_cursor (lay::Cursor::size_all);
m_alt_ac = ac_from_buttons (buttons);
m_alt_ac = lay::ac_from_buttons (buttons);
// drag the vertex or edge/segment
if (is_single_point_selection () || is_single_edge_selection ()) {
@ -1862,7 +1862,7 @@ PartialService::mouse_move_event (const db::DPoint &p, unsigned int buttons, boo
if (mp_box) {
m_alt_ac = ac_from_buttons (buttons);
m_alt_ac = lay::ac_from_buttons (buttons);
m_p2 = p;
mp_box->set_points (m_p1, m_p2);
@ -1922,7 +1922,7 @@ PartialService::mouse_press_event (const db::DPoint &p, unsigned int buttons, bo
} else if (! mp_box) {
m_alt_ac = ac_from_buttons (buttons);
m_alt_ac = lay::ac_from_buttons (buttons);
if (m_selection.empty ()) {
@ -2015,7 +2015,7 @@ PartialService::mouse_click_event (const db::DPoint &p, unsigned int buttons, bo
if (m_dragging) {
m_alt_ac = ac_from_buttons (buttons);
m_alt_ac = lay::ac_from_buttons (buttons);
if (m_current != m_start) {
@ -2059,7 +2059,7 @@ PartialService::mouse_click_event (const db::DPoint &p, unsigned int buttons, bo
view ()->clear_selection ();
m_selection = selection;
m_alt_ac = ac_from_buttons (buttons);
m_alt_ac = lay::ac_from_buttons (buttons);
lay::Editable::SelectionMode mode = lay::Editable::Replace;
bool shift = ((buttons & lay::ShiftButton) != 0);
@ -2192,7 +2192,7 @@ PartialService::mouse_double_click_event (const db::DPoint &p, unsigned int butt
if ((buttons & lay::LeftButton) != 0 && prio) {
m_alt_ac = ac_from_buttons (buttons);
m_alt_ac = lay::ac_from_buttons (buttons);
close_editor_hooks (false);
@ -2215,7 +2215,7 @@ PartialService::mouse_double_click_event (const db::DPoint &p, unsigned int butt
db::DPoint new_point_d = snap (p);
// build the transformation variants cache
TransformationVariants tv (view (), true /*per cv and layer*/, false /*per cv*/);
lay::TransformationVariants tv (view (), true /*per cv and layer*/, false /*per cv*/);
const std::vector<db::DCplxTrans> *tv_list = tv.per_cv_and_layer (r->first.cv_index (), r->first.layer ());
if (tv_list && ! tv_list->empty ()) {
@ -2304,7 +2304,7 @@ PartialService::mouse_release_event (const db::DPoint &p, unsigned int buttons,
if (prio && mp_box) {
m_alt_ac = ac_from_buttons (buttons);
m_alt_ac = lay::ac_from_buttons (buttons);
ui ()->ungrab_mouse (this);
@ -2402,7 +2402,7 @@ PartialService::snap_marker_to_grid (const db::DVector &v, bool &snapped) const
db::DVector snapped_to (1.0, 1.0);
db::DVector vv = lay::snap_angle (v, move_ac (), &snapped_to);
TransformationVariants tv (view ());
lay::TransformationVariants tv (view ());
for (auto r = m_selection.begin (); r != m_selection.end (); ++r) {
@ -2548,7 +2548,7 @@ PartialService::selection_bbox ()
{
// build the transformation variants cache
// TODO: this is done multiple times - once for each service!
TransformationVariants tv (view ());
lay::TransformationVariants tv (view ());
const db::DCplxTrans &vp = view ()->viewport ().trans ();
lay::TextInfo text_info (view ());
@ -2938,7 +2938,7 @@ PartialService::single_selected_point () const
// build the transformation variants cache and
// use only the first one of the explicit transformations
// TODO: clarify how this can be implemented in a more generic form or leave it thus.
TransformationVariants tv (view ());
lay::TransformationVariants tv (view ());
const std::vector<db::DCplxTrans> *tv_list = tv.per_cv_and_layer (m_selection.begin ()->first.cv_index (), m_selection.begin ()->first.layer ());
const lay::CellView &cv = view ()->cellview (m_selection.begin ()->first.cv_index ());
@ -2960,7 +2960,7 @@ PartialService::single_selected_edge () const
// build the transformation variants cache and
// use only the first one of the explicit transformations
// TODO: clarify how this can be implemented in a more generic form or leave it thus.
TransformationVariants tv (view ());
lay::TransformationVariants tv (view ());
const std::vector<db::DCplxTrans> *tv_list = tv.per_cv_and_layer (m_selection.begin ()->first.cv_index (), m_selection.begin ()->first.layer ());
const lay::CellView &cv = view ()->cellview (m_selection.begin ()->first.cv_index ());
@ -3042,7 +3042,7 @@ PartialService::do_selection_to_view ()
if (! m_selection.empty ()) {
// build the transformation variants cache
TransformationVariants tv (view ());
lay::TransformationVariants tv (view ());
for (partial_objects::const_iterator r = m_selection.begin (); r != m_selection.end (); ++r) {
@ -3418,7 +3418,7 @@ PartialService::handle_guiding_shape_changes ()
// Hint: get_parameters_from_pcell_and_guiding_shapes invalidates the shapes because it resets the changed
// guiding shapes. We must not access s->shape after that.
if (! get_parameters_from_pcell_and_guiding_shapes (layout, s->first.cell_index (), parameters_for_pcell)) {
if (! lay::get_parameters_from_pcell_and_guiding_shapes (layout, s->first.cell_index (), parameters_for_pcell)) {
return false;
}

View File

@ -30,9 +30,9 @@
#include "layViewObject.h"
#include "layRubberBox.h"
#include "laySnap.h"
#include "layEditorUtils.h"
#include "tlAssert.h"
#include "tlDeferredExecution.h"
#include "edtUtils.h"
#include "edtConfig.h"
#include "edtEditorHooks.h"
@ -400,7 +400,7 @@ private:
db::DEdge single_selected_edge () const;
bool handle_guiding_shape_changes ();
void transform_selection (const db::DTrans &move_trans);
db::Shape modify_shape (TransformationVariants &tv, const db::Shape &shape_in, const lay::ObjectInstPath &path, const std::set<EdgeWithIndex> &sel, const db::DTrans &move_trans, std::map <EdgeWithIndex, db::Edge> &new_edges, std::map <PointWithIndex, db::Point> &new_points);
db::Shape modify_shape (lay::TransformationVariants &tv, const db::Shape &shape_in, const lay::ObjectInstPath &path, const std::set<EdgeWithIndex> &sel, const db::DTrans &move_trans, std::map <EdgeWithIndex, db::Edge> &new_edges, std::map <PointWithIndex, db::Point> &new_points);
void open_editor_hooks ();
void close_editor_hooks (bool commit);

View File

@ -325,7 +325,7 @@ db::Instance
PathService::make_via (const db::SelectedViaDefinition &via_def, double w_bottom, double h_bottom, double w_top, double h_top, const db::DPoint &via_pos)
{
if (! via_def.via_type.cut.is_null ()) {
edt::set_or_request_current_layer (view (), via_def.via_type.cut, cv_index (), false /*don't make current*/);
lay::set_or_request_current_layer (view (), via_def.via_type.cut, cv_index (), false /*don't make current*/);
}
std::map<std::string, tl::Variant> params;

View File

@ -387,7 +387,7 @@ public:
menu_entries.push_back (lay::menu_item ("edt::combine_mode", "combine_mode:edit_mode", "@toolbar.end_modes", tl::to_string (tr ("Combine{Select background combination mode}"))));
// Key binding only
menu_entries.push_back (lay::menu_item ("edt::via", "via:edit_mode", "@secrets.end", tl::to_string (tr ("Via")) + "(V)"));
menu_entries.push_back (lay::menu_item ("edt::via", "via:edit_mode", "@secrets.end", tl::to_string (tr ("Via")) + "(O)"));
menu_entries.push_back (lay::menu_item ("edt::via_up", "via_up:edit_mode", "@secrets.end", tl::to_string (tr ("Via up"))));
menu_entries.push_back (lay::menu_item ("edt::via_down", "via_down:edit_mode", "@secrets.end", tl::to_string (tr ("Via down"))));
}

View File

@ -23,11 +23,11 @@
#if defined(HAVE_QT)
#include "edtRecentConfigurationPage.h"
#include "edtUtils.h"
#include "layDispatcher.h"
#include "layLayoutViewBase.h"
#include "layLayerTreeModel.h"
#include "layBusy.h"
#include "layEditorUtils.h"
#include "dbLibraryManager.h"
#include "dbLibrary.h"
#include "tlLog.h"
@ -267,7 +267,7 @@ RecentConfigurationPage::render_to (QTreeWidgetItem *item, int column, const std
std::map<std::string, tl::Variant> pcp;
for (std::list<ConfigurationDescriptor>::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]);
pcp = lay::pcell_parameters_from_string (values [pcp_column]);
break;
}
}
@ -296,7 +296,7 @@ RecentConfigurationPage::render_to (QTreeWidgetItem *item, int column, const std
{
std::map<std::string, tl::Variant> pcp;
try {
pcp = pcell_parameters_from_string (values [column]);
pcp = lay::pcell_parameters_from_string (values [column]);
} catch (tl::Exception &ex) {
tl::error << tl::to_string (tr ("Configuration error (PCellParameters): ")) << ex.msg ();
}
@ -384,7 +384,7 @@ RecentConfigurationPage::item_clicked (QTreeWidgetItem *item)
ex.read (cv_index);
}
edt::set_or_request_current_layer (view (), lp, cv_index);
lay::set_or_request_current_layer (view (), lp, cv_index);
} else {
dispatcher ()->config_set (c->cfg_name, v);

View File

@ -42,27 +42,6 @@
namespace edt
{
// -------------------------------------------------------------
// Convert buttons to an angle constraint
lay::angle_constraint_type
ac_from_buttons (unsigned int buttons)
{
if ((buttons & lay::ShiftButton) != 0) {
if ((buttons & lay::ControlButton) != 0) {
return lay::AC_Any;
} else {
return lay::AC_Ortho;
}
} else {
if ((buttons & lay::ControlButton) != 0) {
return lay::AC_Diagonal;
} else {
return lay::AC_Global;
}
}
}
// -------------------------------------------------------------
Service::Service (db::Manager *manager, lay::LayoutViewBase *view, db::ShapeIterator::flags_type flags)
@ -301,12 +280,10 @@ Service::snap (const db::DPoint &p, const db::DPoint &plast, bool connect) const
return snap (ps);
}
const int sr_pixels = 8; // TODO: make variable
lay::PointSnapToObjectResult
Service::snap2_details (const db::DPoint &p) const
{
double snap_range = ui ()->mouse_event_trans ().inverted ().ctrans (sr_pixels);
double snap_range = ui ()->mouse_event_trans ().inverted ().ctrans (lay::snap_range_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);
}
@ -319,7 +296,7 @@ Service::snap2 (const db::DPoint &p) const
db::DPoint
Service::snap2 (const db::DPoint &p, const db::DPoint &plast, bool connect) const
{
double snap_range = ui ()->mouse_event_trans ().inverted ().ctrans (sr_pixels);
double snap_range = ui ()->mouse_event_trans ().inverted ().ctrans (lay::snap_range_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).snapped_point;
}
@ -616,7 +593,7 @@ Service::selection_bbox ()
{
// build the transformation variants cache
// TODO: this is done multiple times - once for each service!
TransformationVariants tv (view ());
lay::TransformationVariants tv (view ());
const db::DCplxTrans &vp = view ()->viewport ().trans ();
lay::TextInfo text_info (view ());
@ -710,7 +687,7 @@ Service::transform (const db::DCplxTrans &trans, const std::vector<db::DCplxTran
}
// build the transformation variants cache
TransformationVariants tv (view ());
lay::TransformationVariants tv (view ());
// 1.) first transform all shapes
@ -891,7 +868,7 @@ Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio)
if (m_editing || m_immediate) {
m_alt_ac = ac_from_buttons (buttons);
m_alt_ac = lay::ac_from_buttons (buttons);
if (! m_editing) {
// in this mode, ignore exceptions here since it is rather annoying to have messages popping
@ -926,7 +903,7 @@ Service::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio
if ((buttons & lay::LeftButton) != 0) {
m_alt_ac = ac_from_buttons (buttons);
m_alt_ac = lay::ac_from_buttons (buttons);
if (! m_editing) {
@ -971,7 +948,7 @@ bool
Service::mouse_double_click_event (const db::DPoint & /*p*/, unsigned int buttons, bool prio)
{
if (m_editing && prio && (buttons & lay::LeftButton) != 0) {
m_alt_ac = ac_from_buttons (buttons);
m_alt_ac = lay::ac_from_buttons (buttons);
do_finish_edit ();
m_editing = false;
set_edit_marker (0);
@ -986,7 +963,7 @@ bool
Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio)
{
if (view ()->is_editable () && prio && (buttons & lay::RightButton) != 0 && m_editing) {
m_alt_ac = ac_from_buttons (buttons);
m_alt_ac = lay::ac_from_buttons (buttons);
do_mouse_transform (p, db::DFTrans (db::DFTrans::r90));
m_alt_ac = lay::AC_Global;
return true;
@ -1777,7 +1754,7 @@ Service::do_selection_to_view ()
m_markers.reserve (selection_size ());
// build the transformation variants cache
TransformationVariants tv (view ());
lay::TransformationVariants tv (view ());
// prepare a default transformation for empty variants
std::vector<db::DCplxTrans> empty_tv;
@ -1955,7 +1932,7 @@ Service::handle_guiding_shape_changes (const lay::ObjectInstPath &obj, bool comm
// Hint: get_parameters_from_pcell_and_guiding_shapes invalidates the shapes because it resets the changed
// guiding shapes. We must not access s->shape after that.
if (! get_parameters_from_pcell_and_guiding_shapes (layout, obj.cell_index (), parameters_for_pcell)) {
if (! lay::get_parameters_from_pcell_and_guiding_shapes (layout, obj.cell_index (), parameters_for_pcell)) {
return std::make_pair (false, lay::ObjectInstPath ());
}

View File

@ -33,10 +33,10 @@
#include "laySnap.h"
#include "layObjectInstPath.h"
#include "layTextInfo.h"
#include "layEditorUtils.h"
#include "tlColor.h"
#include "dbLayout.h"
#include "dbShape.h"
#include "edtUtils.h"
#include "edtConfig.h"
#include "tlAssert.h"
#include "tlException.h"
@ -55,19 +55,15 @@ class PluginDeclarationBase;
// -------------------------------------------------------------
extern lay::angle_constraint_type ac_from_buttons (unsigned int buttons);
// -------------------------------------------------------------
/**
* @brief Utility function: serialize PCell parameters into a string
* @brief A helper class that identifies clipboard data for edt::
*/
std::string pcell_parameters_to_string (const std::map<std::string, tl::Variant> &parameters);
/**
* @brief Utility: deserialize PCell parameters from a string
*/
std::map<std::string, tl::Variant> pcell_parameters_from_string (const std::string &s);
class EDT_PUBLIC ClipboardData
: public db::ClipboardData
{
public:
ClipboardData () { }
};
// -------------------------------------------------------------

View File

@ -167,7 +167,7 @@ ShapeEditService::change_edit_layer (const db::LayerProperties &lp)
}
m_layer = (unsigned int) layer;
edt::set_or_request_current_layer (view (), lp, m_cv_index);
lay::set_or_request_current_layer (view (), lp, m_cv_index);
if (editing ()) {
close_editor_hooks (false);

View File

@ -1358,6 +1358,9 @@ Object::from_string (const char *str, const char *base_dir)
color = true;
} else if (ex.test ("mono:")) {
color = false;
} else {
// unrecognized token
return;
}
size_t w = 0;
@ -2499,13 +2502,19 @@ Object::mem_stat (db::MemStatistics *stat, db::MemStatistics::purpose_t purpose,
const char *
Object::class_name () const
{
return "img::Object";
if (m_layer_binding != db::LayerProperties ()) {
// This makes old KLayout versions ignore these images and not crash
return "img::ObjectV2";
} else {
return "img::Object";
}
}
/**
* @brief Registration of the img::Object class in the DUserObject space
*/
static db::DUserObjectDeclaration class_registrar (new db::user_object_factory_impl<img::Object, db::DCoord> ("img::Object"));
static db::DUserObjectDeclaration class_registrar_v2 (new db::user_object_factory_impl<img::Object, db::DCoord> ("img::ObjectV2"));
} // namespace img

View File

@ -1080,6 +1080,7 @@ Service::edit_cancel ()
{
if (m_move_mode != move_none) {
m_move_mode = move_none;
m_selected.clear ();
selection_to_view ();
}
}
@ -1118,14 +1119,27 @@ Service::paste ()
{
if (db::Clipboard::instance ().begin () != db::Clipboard::instance ().end ()) {
std::vector<const db::DUserObject *> new_objects;
for (db::Clipboard::iterator c = db::Clipboard::instance ().begin (); c != db::Clipboard::instance ().end (); ++c) {
const db::ClipboardValue<img::Object> *value = dynamic_cast<const db::ClipboardValue<img::Object> *> (*c);
if (value) {
img::Object *image = new img::Object (value->get ());
mp_view->annotation_shapes ().insert (db::DUserObject (image));
new_objects.push_back (&mp_view->annotation_shapes ().insert (db::DUserObject (image)));
}
}
// make new objects selected
if (! new_objects.empty ()) {
for (auto r = new_objects.begin (); r != new_objects.end (); ++r) {
m_selected.insert (mp_view->annotation_shapes ().iterator_from_pointer (*r));
}
selection_to_view ();
}
}
}

View File

@ -0,0 +1,114 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2025 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
*/
#if defined(HAVE_QTBINDINGS)
#include "gsiDeclLayConfigPage.h"
#include "gsiQtGuiExternals.h"
#include "gsiQtWidgetsExternals.h" // for Qt5
namespace gsi
{
ConfigPageImpl::ConfigPageImpl (const std::string &title)
: lay::ConfigPage (0), m_title (title)
{
// .. nothing yet ..
}
void
ConfigPageImpl::commit_impl (lay::Dispatcher *root)
{
lay::ConfigPage::commit (root);
}
void
ConfigPageImpl::commit (lay::Dispatcher *root)
{
if (f_commit.can_issue ()) {
f_commit.issue<ConfigPageImpl, lay::Dispatcher *> (&ConfigPageImpl::commit_impl, root);
} else {
ConfigPageImpl::commit_impl (root);
}
}
void
ConfigPageImpl::setup_impl (lay::Dispatcher *root)
{
lay::ConfigPage::setup (root);
}
void
ConfigPageImpl::setup (lay::Dispatcher *root)
{
if (f_setup.can_issue ()) {
f_setup.issue<ConfigPageImpl, lay::Dispatcher *> (&ConfigPageImpl::setup_impl, root);
} else {
ConfigPageImpl::setup_impl (root);
}
}
ConfigPageImpl *new_config_page (const std::string &title)
{
return new ConfigPageImpl (title);
}
Class<ConfigPageImpl> decl_ConfigPage (QT_EXTERNAL_BASE (QFrame) "lay", "ConfigPage",
constructor ("new", &new_config_page, gsi::arg ("title"),
"@brief Creates a new ConfigPage object\n"
"@param title The title of the page and also the position in the configuration page tree\n"
"\n"
"The title has the form 'Group|Page' - e.g. 'Application|Macro Development IDE' will place "
"the configuration page in the 'Application' group and into the 'Macro Development IDE' page."
) +
callback ("apply", &ConfigPageImpl::commit, &ConfigPageImpl::f_commit, gsi::arg ("dispatcher"),
"@brief Reimplement this method to transfer data from the page to the configuration\n"
"In this method, you should transfer all widget data into corresponding configuration updates.\n"
"Use \\Dispatcher#set_config on the dispatcher object ('dispatcher' argument) to set a configuration parameter.\n"
) +
callback ("setup", &ConfigPageImpl::setup, &ConfigPageImpl::f_setup, gsi::arg ("dispatcher"),
"@brief Reimplement this method to transfer data from the configuration to the page\n"
"In this method, you should transfer all configuration data to the widgets.\n"
"Use \\Dispatcher#get_config on the dispatcher object ('dispatcher' argument) to get a configuration parameter "
"and set the editing widget's state accordingly.\n"
),
"@brief The plugin framework's configuration page\n"
"\n"
"This object provides a way to establish plugin-specific configuration pages.\n"
"\n"
"The only way of communication between the page and the plugin is through "
"configuration parameters. One advantage of this approach is that the current state is "
"automatically persisted. Configuration parameters can be obtained by the plugin "
"directly from the \\Dispatcher object) or by listening to 'configure' calls.\n"
"\n"
"For the purpose of data transfer, the configuration page has two methods: 'apply' which is supposed to transfer "
"the editor widget's state into configuration parameters. 'setup' does the inverse and transfer "
"configuration parameters into editor widget states. Both methods are called by the system when "
"some transfer is needed.\n"
"\n"
"This class has been introduced in version 0.30.4.\n"
);
}
#endif

View File

@ -0,0 +1,67 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2025 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
*/
#if !defined(_HDR_gsiDeclLayConfigPage)
#define _HDR_gsiDeclLayConfigPage
#if defined(HAVE_QTBINDINGS)
#include "gsiDecl.h"
#include "gsiDeclBasic.h"
#include "layPluginConfigPage.h"
#include "layLayoutViewBase.h"
namespace gsi
{
class ConfigPageImpl
: public lay::ConfigPage, public gsi::ObjectBase
{
public:
ConfigPageImpl (const std::string &title);
virtual std::string title () const
{
return m_title;
}
void commit_impl (lay::Dispatcher *root);
virtual void commit (lay::Dispatcher *root);
void setup_impl (lay::Dispatcher *root);
virtual void setup (lay::Dispatcher *root);
gsi::Callback f_commit;
gsi::Callback f_setup;
private:
tl::weak_ptr<lay::LayoutViewBase> mp_view;
tl::weak_ptr<lay::Dispatcher> mp_dispatcher;
std::string m_title;
std::string m_index;
};
}
#endif
#endif

View File

@ -0,0 +1,198 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2025 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
*/
#if defined(HAVE_QTBINDINGS)
#include "gsiDeclLayEditorOptionsPage.h"
#include "gsiQtGuiExternals.h"
#include "gsiQtWidgetsExternals.h" // for Qt5
namespace gsi
{
Class<lay::EditorOptionsPage> decl_EditorOptionsPageBase (QT_EXTERNAL_BASE (QWidget) "lay", "EditorOptionsPageBase",
method ("view", &lay::EditorOptionsPage::view,
"@brief Gets the view object this page is associated with\n"
) +
method ("title", &lay::EditorOptionsPage::title,
"@brief Gets the title string of the page\n"
) +
method ("order", &lay::EditorOptionsPage::order,
"@brief Gets the order index of the page\n"
) +
method ("is_focus_page?", &lay::EditorOptionsPage::is_focus_page,
"@brief Gets a flag indicating whether the page is a focus page\n"
"See \\focus_page= for a description is this attribute.\n"
) +
method ("focus_page=", &lay::EditorOptionsPage::set_focus_page, gsi::arg ("flag"),
"@brief Sets a flag indicating whether the page is a focus page\n"
"The focus page is the page that is selected when the tab key is pressed during some plugin action.\n"
) +
method ("is_modal_page?", &lay::EditorOptionsPage::is_modal_page,
"@brief Gets a flag indicating whether the page is a modal page\n"
"See \\modal_page= for a description is this attribute.\n"
) +
method ("modal_page=", &lay::EditorOptionsPage::set_modal_page, gsi::arg ("flag"),
"@brief Sets a flag indicating whether the page is a modal page\n"
"A modal page is shown in a modal dialog upon \\show. Non-modal pages are shown in the "
"editor options dock.\n"
) +
method ("show", &lay::EditorOptionsPage::show,
"@brief Shows the page\n"
"@return A value indicating whether the page was opened non-modal (-1), accepted (1) or rejected (0)\n"
"Provided the page is selected because the plugin is active, this method will "
"open a dialog to show the page if it is modal, or locate the page in the editor options "
"dock and bring it to the front if it is non-modal.\n"
"\n"
"Before the page is shown, \\setup is called. When the page is dismissed (accepted), \\apply is called. "
"You can overload these methods to transfer data to and from the configuration space or to perform other "
"actions, not related to configuration parameters."
) +
method ("apply", &lay::EditorOptionsPage::apply, gsi::arg ("dispatcher"),
"@brief Transfers data from the page to the configuration\n"
) +
method ("setup", &lay::EditorOptionsPage::setup, gsi::arg ("dispatcher"),
"@brief Transfers data from the configuration to the page\n"
),
"@brief The plugin framework's editor options page base class\n"
"\n"
"This class is provided as an interface to the base class implementation for various functions.\n"
"You can use these methods in order to pass down events to the original implementation or access\n"
"objects not created in script space.\n"
"\n"
"It features some useful methods such as 'view' and provides a slot to call for triggering a data "
"transfer ('edited').\n"
"\n"
"Note that even though the page class is derived from QWidget, you can call QWidget methods "
"but not overload virtual methods from QWidget.\n"
"\n"
"This class has been introduced in version 0.30.4.\n"
);
EditorOptionsPageImpl::EditorOptionsPageImpl (const std::string &title, int index)
: lay::EditorOptionsPage (), m_title (title), m_index (index)
{
// .. nothing yet ..
}
void
EditorOptionsPageImpl::call_edited ()
{
lay::EditorOptionsPage::edited ();
}
static void apply_fb (EditorOptionsPageImpl *ep, lay::Dispatcher *root)
{
ep->lay::EditorOptionsPage::apply (root);
}
static void setup_fb (EditorOptionsPageImpl *ep, lay::Dispatcher *root)
{
ep->lay::EditorOptionsPage::setup (root);
}
void
EditorOptionsPageImpl::apply_impl (lay::Dispatcher *root)
{
lay::EditorOptionsPage::apply (root);
}
void
EditorOptionsPageImpl::apply (lay::Dispatcher *root)
{
if (f_apply.can_issue ()) {
f_apply.issue<EditorOptionsPageImpl, lay::Dispatcher *> (&EditorOptionsPageImpl::apply_impl, root);
} else {
EditorOptionsPageImpl::apply_impl (root);
}
}
void
EditorOptionsPageImpl::setup_impl (lay::Dispatcher *root)
{
lay::EditorOptionsPage::setup (root);
}
void
EditorOptionsPageImpl::setup (lay::Dispatcher *root)
{
if (f_setup.can_issue ()) {
f_setup.issue<EditorOptionsPageImpl, lay::Dispatcher *> (&EditorOptionsPageImpl::setup_impl, root);
} else {
EditorOptionsPageImpl::setup_impl (root);
}
}
EditorOptionsPageImpl *new_editor_options_page (const std::string &title, int index)
{
return new EditorOptionsPageImpl (title, index);
}
Class<EditorOptionsPageImpl> decl_EditorOptionsPage (decl_EditorOptionsPageBase, "lay", "EditorOptionsPage",
constructor ("new", &new_editor_options_page, gsi::arg ("title"), gsi::arg ("index"),
"@brief Creates a new EditorOptionsPage object\n"
"@param title The title of the page\n"
"@param index The position of the page in the tab bar\n"
) +
method ("edited", &EditorOptionsPageImpl::call_edited,
"@brief Call this method when some entry widget has changed\n"
"When some entry widget (for example 'editingFinished' slot of a QLineEdit), "
"call this method to initiate a transfer of information from the page to the plugin.\n"
) +
// prevents infinite recursion
method_ext ("apply", &apply_fb, gsi::arg ("dispatcher"), "@hide") +
callback ("apply", &EditorOptionsPageImpl::apply, &EditorOptionsPageImpl::f_apply, gsi::arg ("dispatcher"),
"@brief Reimplement this method to transfer data from the page to the configuration\n"
"In this method, you should transfer all widget data into corresponding configuration updates.\n"
"Use \\Dispatcher#set_config on the dispatcher object ('dispatcher' argument) to set a configuration parameter.\n"
) +
// prevents infinite recursion
method_ext ("setup", &setup_fb, gsi::arg ("dispatcher"), "@hide") +
callback ("setup", &EditorOptionsPageImpl::setup, &EditorOptionsPageImpl::f_setup, gsi::arg ("dispatcher"),
"@brief Reimplement this method to transfer data from the configuration to the page\n"
"In this method, you should transfer all configuration data to the widgets.\n"
"Use \\Dispatcher#get_config on the dispatcher object ('dispatcher' argument) to get a configuration parameter "
"and set the editing widget's state accordingly.\n"
),
"@brief The plugin framework's editor options page\n"
"\n"
"This object provides a way to establish plugin-specific editor options pages.\n"
"\n"
"The preferred way of communication between the page and the plugin is through "
"configuration parameters. One advantage of this approach is that the current state is "
"automatically persisted.\n"
"\n"
"For this purpose, the editor options page has two methods: 'apply' which is supposed to transfer "
"the editor widget's state into configuration parameters. 'setup' does the inverse and transfer "
"configuration parameters into editor widget states. Both methods are called by the system when "
"some transfer is needed.\n"
"\n"
"When you want to respond to widget signals and transfer information, call \\edited "
"in the signal slot. This will trigger a transfer (aka 'apply').\n"
"\n"
"This class has been introduced in version 0.30.4.\n"
);
}
#endif

View File

@ -0,0 +1,74 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2025 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
*/
#if !defined(_HDR_gsiDeclLayEditorOptionsPage)
#define _HDR_gsiDeclLayEditorOptionsPage
#if defined(HAVE_QTBINDINGS)
#include "gsiDecl.h"
#include "gsiDeclBasic.h"
#include "layEditorOptionsPage.h"
#include "layLayoutViewBase.h"
namespace gsi
{
class EditorOptionsPageImpl
: public lay::EditorOptionsPage, public gsi::ObjectBase
{
public:
EditorOptionsPageImpl (const std::string &title, int index);
virtual std::string title () const
{
return m_title;
}
virtual int order () const
{
return m_index;
}
void call_edited ();
virtual void apply (lay::Dispatcher *root);
virtual void setup (lay::Dispatcher *root);
gsi::Callback f_apply;
gsi::Callback f_setup;
private:
tl::weak_ptr<lay::LayoutViewBase> mp_view;
tl::weak_ptr<lay::Dispatcher> mp_dispatcher;
std::string m_title;
int m_index;
void apply_impl (lay::Dispatcher *root);
void setup_impl (lay::Dispatcher *root);
};
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,153 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2025 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_gsiDeclLayPlugin
#define _HDR_gsiDeclLayPlugin
#include "gsiDecl.h"
#include "gsiDeclBasic.h"
#include "layEditorServiceBase.h"
#include "layLayoutViewBase.h"
namespace gsi
{
class PluginImpl
: public lay::EditorServiceBase
{
public:
PluginImpl ();
void init (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher);
void grab_mouse ();
void ungrab_mouse ();
void set_cursor (int c);
virtual void menu_activated (const std::string &symbol);
db::DPoint snap (db::DPoint p) const;
db::DVector snap_vector (db::DVector v) const;
db::DPoint snap_from_to (const db::DPoint &p, const db::DPoint &plast, bool connect, lay::angle_constraint_type ac) const;
db::DVector snap_delta (const db::DVector &v, bool connect, lay::angle_constraint_type ac) const;
db::DPoint snap2 (const db::DPoint &p, bool visualize);
db::DPoint snap2_from_to (const db::DPoint &p, const db::DPoint &plast, bool connect, lay::angle_constraint_type ac, bool visualize);
// Captures some edt space configuration events for convencience
void configure_edt (const std::string &name, const std::string &value);
// NOTE: The implementation does not allow to bypass the base class configuration call
bool configure_impl (const std::string &name, const std::string &value);
// for testing
void configure_test (const std::string &name, const std::string &value);
virtual bool configure (const std::string &name, const std::string &value);
// NOTE: The implementation does not allow to bypass the base class configuration call
virtual void config_finalize_impl ();
virtual void config_finalize ();
virtual bool key_event (unsigned int key, unsigned int buttons);
virtual bool mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio) ;
bool mouse_press_event_noref (db::DPoint p, unsigned int buttons, bool prio);
virtual bool mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio);
bool mouse_click_event_noref (db::DPoint p, unsigned int buttons, bool prio);
virtual bool mouse_double_click_event (const db::DPoint &p, unsigned int buttons, bool prio);
bool mouse_double_click_event_noref (db::DPoint p, unsigned int buttons, bool prio);
virtual bool leave_event (bool prio);
virtual bool enter_event (bool prio);
virtual bool mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio);
bool mouse_move_event_noref (db::DPoint p, unsigned int buttons, bool prio);
virtual bool mouse_release_event (const db::DPoint &p, unsigned int buttons, bool prio);
bool mouse_release_event_noref (db::DPoint p, unsigned int buttons, bool prio);
virtual bool wheel_event (int delta, bool horizontal, const db::DPoint &p, unsigned int buttons, bool prio);
bool wheel_event_noref (int delta, bool horizontal, db::DPoint p, unsigned int buttons, bool prio);
void activated_impl ();
virtual void activated ();
void deactivated_impl ();
virtual void deactivated ();
virtual void drag_cancel ();
virtual void update ();
void add_mouse_cursor_dpoint (const db::DPoint &p, bool emphasize);
void add_mouse_cursor_point (const db::Point &p, int cv_index, const db::LayerProperties &lp, bool emphasize);
void add_edge_marker_dedge (const db::DEdge &p, bool emphasize);
void add_edge_marker_edge (const db::Edge &p, int cv_index, const db::LayerProperties &lp, bool emphasize);
// for testing
bool has_tracking_position_test () const;
virtual bool has_tracking_position () const;
// for testing
db::DPoint tracking_position_test () const;
virtual db::DPoint tracking_position () const;
virtual int focus_page_open ();
virtual lay::ViewService *view_service_interface ()
{
return this;
}
lay::LayoutViewBase *view () const
{
return const_cast<lay::LayoutViewBase *> (mp_view.get ());
}
lay::Dispatcher *dispatcher () const
{
return const_cast<lay::Dispatcher *> (mp_dispatcher.get ());
}
gsi::Callback f_menu_activated;
gsi::Callback f_configure;
gsi::Callback f_config_finalize;
gsi::Callback f_key_event;
gsi::Callback f_mouse_press_event;
gsi::Callback f_mouse_click_event;
gsi::Callback f_mouse_double_click_event;
gsi::Callback f_leave_event;
gsi::Callback f_enter_event;
gsi::Callback f_mouse_move_event;
gsi::Callback f_mouse_release_event;
gsi::Callback f_wheel_event;
gsi::Callback f_activated;
gsi::Callback f_deactivated;
gsi::Callback f_drag_cancel;
gsi::Callback f_update;
gsi::Callback f_has_tracking_position;
gsi::Callback f_tracking_position;
gsi::Callback f_focus_page_open;
private:
tl::weak_ptr<lay::LayoutViewBase> mp_view;
tl::weak_ptr<lay::Dispatcher> mp_dispatcher;
// Angle constraints and grids
lay::angle_constraint_type m_connect_ac, m_move_ac;
db::DVector m_edit_grid;
bool m_snap_to_objects;
bool m_snap_objects_to_grid;
db::DVector m_global_grid;
lay::angle_constraint_type connect_ac (lay::angle_constraint_type ac) const;
lay::angle_constraint_type move_ac (lay::angle_constraint_type ac) const;
};
}
#endif

View File

@ -0,0 +1,602 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2025 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 "gsiDecl.h"
#include "gsiDeclBasic.h"
#include "gsiEnums.h"
#include "gsiDeclLayEditorOptionsPage.h"
#include "gsiDeclLayConfigPage.h"
#include "gsiDeclLayPlugin.h"
#include "layEditorOptionsPages.h"
namespace gsi
{
class PluginFactoryBase;
static std::map <std::string, PluginFactoryBase *> s_factories;
extern bool s_in_create_plugin;
class PluginFactoryBase
: public lay::PluginDeclaration
{
public:
PluginFactoryBase ()
: PluginDeclaration (),
m_implements_mouse_mode (true), mp_registration (0)
{
// .. nothing yet ..
}
~PluginFactoryBase ()
{
for (auto f = s_factories.begin (); f != s_factories.end (); ++f) {
if (f->second == this) {
s_factories.erase (f);
break;
}
}
delete mp_registration;
mp_registration = 0;
}
void register_gsi (int position, const char *name, const char *title)
{
register_gsi2 (position, name, title, 0);
}
void register_gsi2 (int position, const char *name, const char *title, const char *icon)
{
// makes the object owned by the C++ side
keep ();
// remove an existing factory with the same name
std::map <std::string, PluginFactoryBase *>::iterator f = s_factories.find (name);
if (f != s_factories.end () && f->second != this) {
// NOTE: this also removes the plugin from the s_factories list
delete f->second;
}
s_factories[name] = this;
// cancel any previous registration and register (again)
delete mp_registration;
mp_registration = new tl::RegisteredClass<lay::PluginDeclaration> (this, position, name, false /*does not own object*/);
m_mouse_mode_title = name;
if (title) {
m_mouse_mode_title += "\t";
m_mouse_mode_title += title;
}
if (icon) {
m_mouse_mode_title += "\t<";
m_mouse_mode_title += icon;
m_mouse_mode_title += ">";
}
// (dynamically) register the plugin class. This will also call initialize if the main window is
// present already.
register_plugin ();
}
virtual bool configure (const std::string &name, const std::string &value)
{
if (f_configure.can_issue ()) {
return f_configure.issue<lay::PluginDeclaration, bool, const std::string &, const std::string &> (&lay::PluginDeclaration::configure, name, value);
} else {
return lay::PluginDeclaration::configure (name, value);
}
}
virtual void config_finalize ()
{
if (f_config_finalize.can_issue ()) {
f_config_finalize.issue<lay::PluginDeclaration> (&lay::PluginDeclaration::config_finalize);
} else {
lay::PluginDeclaration::config_finalize ();
}
}
virtual bool menu_activated (const std::string &symbol) const
{
if (f_menu_activated.can_issue ()) {
return f_menu_activated.issue<lay::PluginDeclaration, bool, const std::string &> (&lay::PluginDeclaration::menu_activated, symbol);
} else {
return lay::PluginDeclaration::menu_activated (symbol);
}
}
virtual void initialize (lay::Dispatcher *root)
{
if (f_initialize.can_issue ()) {
f_initialize.issue<lay::PluginDeclaration> (&lay::PluginDeclaration::initialize, root);
} else {
lay::PluginDeclaration::initialize (root);
}
}
virtual void uninitialize (lay::Dispatcher *root)
{
if (f_uninitialize.can_issue ()) {
f_uninitialize.issue<lay::PluginDeclaration> (&lay::PluginDeclaration::uninitialize, root);
} else {
lay::PluginDeclaration::uninitialize (root);
}
}
#if defined(HAVE_QTBINDINGS)
void add_editor_options_page (EditorOptionsPageImpl *page) const
{
page->keep ();
m_editor_options_pages.push_back (page);
}
void get_editor_options_pages_impl () const
{
// .. nothing here ..
}
virtual void get_editor_options_pages (std::vector<lay::EditorOptionsPage *> &pages_out, lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) const
{
try {
m_editor_options_pages.clear ();
if (f_get_editor_options_pages.can_issue ()) {
f_get_editor_options_pages.issue<PluginFactoryBase> (&PluginFactoryBase::get_editor_options_pages_impl);
} else {
get_editor_options_pages_impl ();
}
for (auto i = m_editor_options_pages.begin (); i != m_editor_options_pages.end (); ++i) {
if (*i) {
(*i)->init (view, dispatcher);
(*i)->set_plugin_declaration (this);
pages_out.push_back (*i);
}
}
m_editor_options_pages.clear ();
} catch (tl::Exception &ex) {
tl::error << ex.msg ();
} catch (std::exception &ex) {
tl::error << ex.what ();
} catch (...) {
}
}
void add_config_page (ConfigPageImpl *page) const
{
page->keep ();
m_config_pages.push_back (page);
}
void get_config_pages_impl () const
{
// .. nothing here ..
}
virtual std::vector<std::pair <std::string, lay::ConfigPage *> > config_pages (QWidget *parent) const
{
std::vector<std::pair <std::string, lay::ConfigPage *> > pages_out;
try {
m_config_pages.clear ();
if (f_config_pages.can_issue ()) {
f_config_pages.issue<PluginFactoryBase> (&PluginFactoryBase::get_config_pages_impl);
} else {
get_config_pages_impl ();
}
pages_out.clear ();
for (auto i = m_config_pages.begin (); i != m_config_pages.end (); ++i) {
if (*i) {
(*i)->setParent (parent);
pages_out.push_back (std::make_pair ((*i)->title (), *i));
}
}
m_config_pages.clear ();
} catch (tl::Exception &ex) {
tl::error << ex.msg ();
} catch (std::exception &ex) {
tl::error << ex.what ();
} catch (...) {
}
return pages_out;
}
#endif
virtual lay::Plugin *create_plugin (db::Manager *manager, lay::Dispatcher *root, lay::LayoutViewBase *view) const
{
if (f_create_plugin.can_issue ()) {
return create_plugin_gsi (manager, root, view);
} else {
return lay::PluginDeclaration::create_plugin (manager, root, view);
}
}
virtual gsi::PluginImpl *create_plugin_gsi (db::Manager *manager, lay::Dispatcher *root, lay::LayoutViewBase *view) const
{
s_in_create_plugin = true;
gsi::PluginImpl *ret = 0;
try {
ret = f_create_plugin.issue<PluginFactoryBase, gsi::PluginImpl *, db::Manager *, lay::Dispatcher *, lay::LayoutViewBase *> (&PluginFactoryBase::create_plugin_gsi, manager, root, view);
if (ret) {
ret->init (view, root);
}
} catch (tl::Exception &ex) {
tl::error << ex.msg ();
} catch (std::exception &ex) {
tl::error << ex.what ();
} catch (...) {
}
s_in_create_plugin = false;
return ret;
}
virtual void get_menu_entries (std::vector<lay::MenuEntry> &menu_entries) const
{
menu_entries = m_menu_entries;
}
virtual void get_options (std::vector < std::pair<std::string, std::string> > &options) const
{
options = m_options;
}
void add_menu_entry1 (const std::string &menu_name, const std::string &insert_pos)
{
m_menu_entries.push_back (lay::separator (menu_name, insert_pos));
}
void add_menu_entry2 (const std::string &symbol, const std::string &menu_name, const std::string &insert_pos, const std::string &title)
{
m_menu_entries.push_back (lay::menu_item (symbol, menu_name, insert_pos, title));
}
void add_menu_entry_copy (const std::string &symbol, const std::string &menu_name, const std::string &insert_pos, const std::string &copy_from)
{
m_menu_entries.push_back (lay::menu_item_copy (symbol, menu_name, insert_pos, copy_from));
}
void add_submenu (const std::string &menu_name, const std::string &insert_pos, const std::string &title)
{
m_menu_entries.push_back (lay::submenu (menu_name, insert_pos, title));
}
void add_config_menu_item (const std::string &menu_name, const std::string &insert_pos, const std::string &title, const std::string &cname, const std::string &cvalue)
{
m_menu_entries.push_back (lay::config_menu_item (menu_name, insert_pos, title, cname, cvalue));
}
void add_menu_entry3 (const std::string &symbol, const std::string &menu_name, const std::string &insert_pos, const std::string &title, bool sub_menu)
{
if (sub_menu) {
m_menu_entries.push_back (lay::submenu (symbol, menu_name, insert_pos, title));
} else {
m_menu_entries.push_back (lay::menu_item (symbol, menu_name, insert_pos, title));
}
}
void add_option (const std::string &name, const std::string &default_value)
{
m_options.push_back (std::make_pair (name, default_value));
}
void has_tool_entry (bool f)
{
m_implements_mouse_mode = f;
}
virtual bool implements_mouse_mode (std::string &title) const
{
title = m_mouse_mode_title;
return m_implements_mouse_mode;
}
gsi::Callback f_create_plugin;
gsi::Callback f_initialize;
gsi::Callback f_uninitialize;
gsi::Callback f_configure;
gsi::Callback f_config_finalize;
gsi::Callback f_menu_activated;
gsi::Callback f_get_editor_options_pages;
gsi::Callback f_config_pages;
private:
std::vector<std::pair<std::string, std::string> > m_options;
std::vector<lay::MenuEntry> m_menu_entries;
bool m_implements_mouse_mode;
std::string m_mouse_mode_title;
tl::RegisteredClass <lay::PluginDeclaration> *mp_registration;
#if defined(HAVE_QTBINDINGS)
mutable std::vector<ConfigPageImpl *> m_config_pages;
mutable std::vector<EditorOptionsPageImpl *> m_editor_options_pages;
#endif
};
Class<gsi::PluginFactoryBase> decl_PluginFactory ("lay", "PluginFactory",
method ("register", &PluginFactoryBase::register_gsi, gsi::arg ("position"), gsi::arg ("name"), gsi::arg ("title"),
"@brief Registers the plugin factory\n"
"@param position An integer that determines the order in which the plugins are created. The internal plugins use the values from 1000 to 50000.\n"
"@param name The plugin name. This is an arbitrary string which should be unique. Hence it is recommended to use a unique prefix, i.e. \"myplugin::ThePluginClass\".\n"
"@param title The title string which is supposed to appear in the tool bar and menu related to this plugin.\n"
"\n"
"Registration of the plugin factory makes the object known to the system. Registration requires that the menu items have been set "
"already. Hence it is recommended to put the registration at the end of the initialization method of the factory class.\n"
) +
method ("register", &PluginFactoryBase::register_gsi2, gsi::arg ("position"), gsi::arg ("name"), gsi::arg ("title"), gsi::arg ("icon"),
"@brief Registers the plugin factory\n"
"@param position An integer that determines the order in which the plugins are created. The internal plugins use the values from 1000 to 50000.\n"
"@param name The plugin name. This is an arbitrary string which should be unique. Hence it is recommended to use a unique prefix, i.e. \"myplugin::ThePluginClass\".\n"
"@param title The title string which is supposed to appear in the tool bar and menu related to this plugin.\n"
"@param icon The path to the icon that appears in the tool bar and menu related to this plugin.\n"
"\n"
"This version also allows registering an icon for the tool bar.\n"
"\n"
"Registration of the plugin factory makes the object known to the system. Registration requires that the menu items have been set "
"already. Hence it is recommended to put the registration at the end of the initialization method of the factory class.\n"
) +
callback ("configure", &gsi::PluginFactoryBase::configure, &gsi::PluginFactoryBase::f_configure, gsi::arg ("name"), gsi::arg ("value"),
"@brief Gets called for configuration events for the plugin singleton\n"
"This method can be reimplemented to receive configuration events "
"for the plugin singleton. Before a configuration can be received it must be "
"registered by calling \\add_option in the plugin factories' constructor.\n"
"\n"
"The implementation of this method may return true indicating that the configuration request "
"will not be handled by further modules. It's more cooperative to return false which will "
"make the system distribute the configuration request to other receivers as well.\n"
"\n"
"@param name The configuration key\n"
"@param value The value of the configuration variable\n"
"@return True to stop further processing\n"
) +
callback ("config_finalize", &gsi::PluginFactoryBase::config_finalize, &gsi::PluginFactoryBase::f_config_finalize,
"@brief Gets called after a set of configuration events has been sent\n"
"This method can be reimplemented and is called after a set of configuration events "
"has been sent to the plugin factory singleton with \\configure. It can be used to "
"set up user interfaces properly for example.\n"
) +
callback ("menu_activated", &gsi::PluginFactoryBase::menu_activated, &gsi::PluginFactoryBase::f_menu_activated, gsi::arg ("symbol"),
"@brief Gets called when a menu item is selected\n"
"\n"
"Usually, menu-triggered functionality is implemented in the per-view instance of the plugin. "
"However, using this method it is possible to implement functionality globally for all plugin "
"instances. The symbol is the string registered with the specific menu item in the \\add_menu_item "
"call.\n"
"\n"
"If this method was handling the menu event, it should return true. This indicates that the event "
"will not be propagated to other plugins hence avoiding duplicate calls.\n"
) +
callback ("initialized", &gsi::PluginFactoryBase::initialize, &gsi::PluginFactoryBase::f_initialize, gsi::arg ("dispatcher"),
"@brief Gets called when the plugin singleton is initialized, i.e. when the application has been started.\n"
"@param dispatcher The reference to the \\MainWindow object\n"
) +
callback ("uninitialized", &gsi::PluginFactoryBase::uninitialize, &gsi::PluginFactoryBase::f_uninitialize, gsi::arg ("dispatcher"),
"@brief Gets called when the application shuts down and the plugin is unregistered\n"
"This event can be used to free resources allocated with this factory singleton.\n"
"@param dispatcher The reference to the \\MainWindow object\n"
) +
factory_callback ("create_plugin", &gsi::PluginFactoryBase::create_plugin_gsi, &gsi::PluginFactoryBase::f_create_plugin, gsi::arg ("manager"), gsi::arg ("dispatcher"), gsi::arg ("view"),
"@brief Creates the plugin\n"
"This is the basic functionality that the factory must provide. This method must create a plugin of the "
"specific type.\n"
"@param manager The database manager object responsible for handling database transactions\n"
"@param dispatcher The reference to the \\MainWindow object\n"
"@param view The \\LayoutView that is plugin is created for\n"
"@return The new \\Plugin implementation object\n"
) +
method ("add_menu_entry", &gsi::PluginFactoryBase::add_menu_entry1, gsi::arg ("menu_name"), gsi::arg ("insert_pos"),
"@brief Specifies a separator\n"
"Call this method in the factory constructor to build the menu items that this plugin shall create.\n"
"This specific call inserts a separator at the given position (insert_pos). The position uses abstract menu item paths "
"and \"menu_name\" names the component that will be created. See \\AbstractMenu for a description of the path.\n"
) +
method ("add_menu_entry", &gsi::PluginFactoryBase::add_menu_entry2, gsi::arg ("symbol"), gsi::arg ("menu_name"), gsi::arg ("insert_pos"), gsi::arg ("title"),
"@brief Specifies a menu item\n"
"Call this method in the factory constructor to build the menu items that this plugin shall create.\n"
"This specific call inserts a menu item at the specified position (insert_pos). The position uses abstract menu item paths "
"and \"menu_name\" names the component that will be created. See \\AbstractMenu for a description of the path.\n"
"When the menu item is selected \"symbol\" is the string that is sent to the \\menu_activated callback (either the global one for the factory ot the one of the per-view plugin instance).\n"
"\n"
"@param symbol The string to send to the plugin if the menu is triggered\n"
"@param menu_name The name of entry to create at the given position\n"
"@param insert_pos The position where to create the entry\n"
"@param title The title string for the item. The title can contain a keyboard shortcut in round braces after the title text, i.e. \"My Menu Item(F12)\"\n"
) +
method ("#add_menu_entry", &gsi::PluginFactoryBase::add_menu_entry3, gsi::arg ("symbol"), gsi::arg ("menu_name"), gsi::arg ("insert_pos"), gsi::arg ("title"), gsi::arg ("sub_menu"),
"@brief Specifies a menu item or sub-menu\n"
"Similar to the previous form of \"add_menu_entry\", but this version allows also to create sub-menus by setting the "
"last parameter to \"true\".\n"
"\n"
"With version 0.27 it's more convenient to use \\add_submenu."
) +
method ("add_menu_item_clone", &gsi::PluginFactoryBase::add_menu_entry_copy, gsi::arg ("symbol"), gsi::arg ("menu_name"), gsi::arg ("insert_pos"), gsi::arg ("copy_from"),
"@brief Specifies a menu item as a clone of another one\n"
"Using this method, a menu item can be made a clone of another entry (given as path by 'copy_from').\n"
"The new item will share the \\Action object with the original one, so manipulating the action will change both the original entry "
"and the new entry.\n"
"\n"
"This method has been introduced in version 0.27."
) +
method ("add_submenu", &gsi::PluginFactoryBase::add_submenu, gsi::arg ("menu_name"), gsi::arg ("insert_pos"), gsi::arg ("title"),
"@brief Specifies a menu item or sub-menu\n"
"\n"
"This method has been introduced in version 0.27."
) +
method ("add_config_menu_item", &gsi::PluginFactoryBase::add_config_menu_item, gsi::arg ("menu_name"), gsi::arg ("insert_pos"), gsi::arg ("title"), gsi::arg ("cname"), gsi::arg ("cvalue"),
"@brief Adds a configuration menu item\n"
"\n"
"Menu items created this way will send a configuration request with 'cname' as the configuration parameter name "
"and 'cvalue' as the configuration parameter value.\n"
"If 'cvalue' is a string with a single question mark (\"?\"), the item is a check box that reflects the boolean "
"value of the configuration item.\n"
"\n"
"This method has been introduced in version 0.27."
) +
method ("add_option", &gsi::PluginFactoryBase::add_option, gsi::arg ("name"), gsi::arg ("default_value"),
"@brief Specifies configuration variables.\n"
"Call this method in the factory constructor to add configuration key/value pairs to the configuration repository. "
"Without specifying configuration variables, the status of a plugin cannot be persisted. "
"\n\n"
"Once the configuration variables are known, they can be retrieved on demand using \"get_config\" from "
"\\MainWindow or listening to \\configure callbacks (either in the factory or the plugin instance). Configuration variables can "
"be set using \"set_config\" from \\MainWindow. This scheme also works without registering the configuration options, but "
"doing so has the advantage that it is guaranteed that a variable with this keys exists and has the given default value initially."
) +
#if defined(HAVE_QTBINDINGS)
method ("add_editor_options_page", &PluginFactoryBase::add_editor_options_page, gsi::arg ("page"),
"@brief Adds the given editor options page\n"
"See \\create_editor_options_pages how to use this function. The method is effective only in "
"the reimplementation context of this function.\n"
"\n"
"This method has been introduced in version 0.30.4."
) +
callback ("create_editor_options_pages", &PluginFactoryBase::get_editor_options_pages_impl, &PluginFactoryBase::f_get_editor_options_pages,
"@brief Creates the editor option pages\n"
"The editor option pages are widgets of type \\EditorOptionsPage. These Qt widgets "
"are displayed in a seperate dock (the 'editor options') and become visible when the plugin is active - i.e. "
"its mode is selected. Use this method to provide customized pages that will be displayed in the "
"editor options dock.\n"
"\n"
"In order to create config pages, instantiate a \\EditorOptionsPage object and "
"call \\add_editor_options_page to register it.\n"
"\n"
"This method has been introduced in version 0.30.4."
) +
method ("add_config_page", &PluginFactoryBase::add_config_page, gsi::arg ("page"),
"@brief Adds the given configuration page\n"
"See \\create_config_pages how to use this function. The method is effective only in "
"the reimplementation context of this function.\n"
"\n"
"This method has been introduced in version 0.30.4."
) +
callback ("create_config_pages", &PluginFactoryBase::get_config_pages_impl, &PluginFactoryBase::f_config_pages,
"@brief Creates the configuration widgets\n"
"The configuration pages are widgets that are displayed in the "
"configuration dialog ('File/Setup'). Every plugin can create multiple such "
"widgets and specify, where these widgets are displayed. The widgets are of type \\ConfigPage.\n"
"\n"
"The title string also specifies the location of the widget in the "
"configuration page hierarchy. See \\ConfigPage for more details.\n"
"\n"
"In order to create config pages, instantiate a \\ConfigPage object and "
"call \\add_config_page to register it.\n"
"\n"
"This method has been introduced in version 0.30.4."
) +
#endif
method ("has_tool_entry=", &gsi::PluginFactoryBase::has_tool_entry, gsi::arg ("f"),
"@brief Enables or disables the tool bar entry\n"
"Initially this property is set to true. This means that the plugin will have a visible entry in the toolbar. "
"This property can be set to false to disable this feature. In that case, the title and icon given on registration will be ignored. "
),
"@brief The plugin framework's plugin factory object\n"
"\n"
"Plugins are components that extend KLayout's functionality in various aspects. Scripting support exists "
"currently for providing mouse mode handlers and general on-demand functionality connected with a menu "
"entry.\n"
"\n"
"Plugins are objects that implement the \\Plugin interface. Each layout view is associated with one instance "
"of such an object. The PluginFactory is a singleton which is responsible for creating \\Plugin objects and "
"providing certain configuration information such as where to put the menu items connected to this plugin and "
"what configuration keys are used.\n"
"\n"
"An implementation of PluginFactory must at least provide an implementation of \\create_plugin. This method "
"must instantiate a new object of the specific plugin.\n"
"\n"
"After the factory has been created, it must be registered in the system using one of the \\register methods. "
"It is therefore recommended to put the call to \\register at the end of the \"initialize\" method. For the registration "
"to work properly, the menu items must be defined before \\register is called.\n"
"\n"
"The following features can also be implemented:\n"
"\n"
"@<ul>\n"
" @<li>Reserve keys in the configuration file using \\add_option in the constructor@</li>\n"
" @<li>Create menu items by using \\add_menu_entry in the constructor@</li>\n"
" @<li>Set the title for the mode entry that appears in the tool bar using the \\register argument@</li>\n"
" @<li>Provide global functionality (independent from the layout view) using \\configure or \\menu_activated@</li>\n"
"@</ul>\n"
"\n"
"This is a simple example for a plugin in Ruby. It switches the mouse cursor to a 'cross' cursor when it is active:\n"
"\n"
"@code\n"
"class PluginTestFactory < RBA::PluginFactory\n"
"\n"
" # Constructor\n"
" def initialize\n"
" # registers the new plugin class at position 100000 (at the end), with name\n"
" # \"my_plugin_test\" and title \"My plugin test\"\n"
" register(100000, \"my_plugin_test\", \"My plugin test\")\n"
" end\n"
" \n"
" # Create a new plugin instance of the custom type\n"
" def create_plugin(manager, dispatcher, view)\n"
" return PluginTest.new\n"
" end\n"
"\n"
"end\n"
"\n"
"# The plugin class\n"
"class PluginTest < RBA::Plugin\n"
" def mouse_moved_event(p, buttons, prio)\n"
" if prio\n"
" # Set the cursor to cross if our plugin is active.\n"
" set_cursor(RBA::Cursor::Cross)\n"
" end\n"
" # Returning false indicates that we don't want to consume the event.\n"
" # This way for example the cursor position tracker still works.\n"
" false\n"
" end\n"
" def mouse_click_event(p, buttons, prio)\n"
" if prio\n"
" puts \"mouse button clicked.\"\n"
" # This indicates we want to consume the event and others don't receive the mouse click\n"
" # with prio = false.\n"
" return true\n"
" end\n"
" # don't consume the event if we are not active.\n"
" false\n"
" end\n"
"end\n"
"\n"
"# Instantiate the new plugin factory.\n"
"PluginTestFactory.new\n"
"@/code\n"
"\n"
"This class has been introduced in version 0.22.\n"
);
}

View File

@ -7,6 +7,9 @@ include($$PWD/../../lib.pri)
DEFINES += MAKE_LAY_LIBRARY
HEADERS = \
gsiDeclLayConfigPage.h \
gsiDeclLayEditorOptionsPage.h \
gsiDeclLayPlugin.h \
layApplication.h \
layClipDialog.h \
layControlWidgetStack.h \
@ -117,8 +120,12 @@ FORMS = \
SOURCES = \
gsiDeclLayApplication.cc \
gsiDeclLayConfigPage.cc \
gsiDeclLayEditorOptionsPage.cc \
gsiDeclLayHelpDialog.cc \
gsiDeclLayMainWindow.cc \
gsiDeclLayPlugin.cc \
gsiDeclLayPluginFactory.cc \
layApplication.cc \
layClipDialog.cc \
layControlWidgetStack.cc \

View File

@ -5,6 +5,8 @@
<file alias="new_macro.lym">macro_templates/new_macro.lym</file>
<file alias="new_text_file.txt">macro_templates/new_text_file.txt</file>
<file alias="new_ruby_file.rb">macro_templates/new_ruby_file.rb</file>
<file alias="drag_box_sample.lym">macro_templates/drag_box_sample.lym</file>
<file alias="drag_box_sample_python.lym">macro_templates/drag_box_sample_python.lym</file>
<file alias="pcell.lym">macro_templates/pcell.lym</file>
<file alias="pcell_sample.lym">macro_templates/pcell_sample.lym</file>
<file alias="qt_designer.lym">macro_templates/qt_designer.lym</file>

View File

@ -1664,32 +1664,38 @@ MainWindow::select_mode (int m)
}
}
// if the current mode supports editing, show the editor options panel
update_editor_options_dock ();
const lay::PluginDeclaration *pd_sel = 0;
for (tl::Registrar<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::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;
}
}
void
MainWindow::update_editor_options_dock ()
{
// if the current mode supports editing, show the editor options panel
if (eo_visible != m_eo_visible) {
m_eo_visible = eo_visible;
show_dock_widget (mp_eo_dock_widget, m_eo_visible);
const lay::PluginDeclaration *pd_sel = 0;
for (tl::Registrar<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::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);
}
}
@ -2439,6 +2445,7 @@ MainWindow::select_view (int index)
current_view_changed ();
update_editor_options_dock ();
clear_current_pos ();
edits_enabled_changed ();
clear_message ();
@ -4381,6 +4388,11 @@ MainWindow::plugin_registered (lay::PluginDeclaration *cls)
for (std::vector <lay::LayoutViewWidget *>::iterator vp = mp_views.begin (); vp != mp_views.end (); ++vp) {
(*vp)->view ()->create_plugins ();
}
// regenerate the setup form
delete mp_setup_form;
mp_setup_form = new SettingsForm (0, dispatcher (), "setup_form"),
mp_setup_form->setup ();
}
void

View File

@ -95,6 +95,7 @@ class ProgressWidget;
class LAY_PUBLIC MainWindow
: public QMainWindow,
public tl::Object,
public gsi::ObjectBase,
public lay::DispatcherDelegate
{
Q_OBJECT
@ -857,6 +858,7 @@ private:
void interactive_close_view (int from, int to, bool invert_range, bool all_cellviews);
void call_on_current_view (void (lay::LayoutView::*func) (), const std::string &op_desc);
void current_view_changed ();
void update_editor_options_dock ();
void update_window_title ();
void update_tab_title (int i);
void add_view (LayoutViewWidget *view);

View File

@ -0,0 +1,431 @@
<?xml version="1.0" encoding="utf-8"?>
<klayout-macro>
<description>A plugin sample\nThis sample provides a box drawing feature and demonstrates UI components and snapping</description>
<format>general</format>
<autorun>true</autorun>
<autorun-early>false</autorun-early>
<show-in-menu>false</show-in-menu>
<shortcut></shortcut>
<interpreter>ruby</interpreter>
<text># Sample plugin
# This plugin implements a box that can be drawn by
# clicking at the first and then at the second point.
# There is one box which is replacing the previous one.
# Line color and line width of the box can be configured
# by editor options (line width) or configuration pages
# (color). These settings are managed through configuration
# options and their current state is persisted.
#
# The dimension of the box can be entered numerically
# while dragging the box. This feature is implemented
# through a modal "focus page", which opens when you
# press the Tab key during editing and when the keyboard
# focus is on the canvas.
#
# Register this macro as "autorun" to enable the plugin
# on startup.
module DragBox
CFG_COLOR = "drag-box-color"
CFG_WIDTH = "drag-box-width"
# An option page providing a single entry box for configuring the line width
# This page communicates via configuration options. One advantage of this
# approach is that the values are persisted
class DragBoxEditorOptionsPage &lt; RBA::EditorOptionsPage
# Creates a new page with title "Options" and at position 1 (second from left)
def initialize
super("Options", 1)
layout2 = RBA::QVBoxLayout::new(self)
layout = RBA::QHBoxLayout::new(self)
layout2.addLayout(layout)
label = RBA::QLabel::new("Line width", self)
layout.addWidget(label)
@spin_box = RBA::QSpinBox::new(self)
@spin_box.setMinimum(1)
@spin_box.setMaximum(16)
layout.addWidget(@spin_box)
layout.addStretch(1)
layout2.addStretch(1)
# connect the spin box value change with the "edited" slot
# which will result in a call of "apply".
@spin_box.valueChanged = lambda { |x| self.edited }
end
# "setup" is called when the page needs to be populated with information -
# i.e. on first show.
def setup(dispatcher)
begin
@spin_box.setValue(dispatcher.get_config(CFG_WIDTH).to_i)
rescue
@spin_box.setValue(1)
end
end
# "apply" is called when the page is requested to submit the entered
# values to the plugin. Usually this should be done via configuration
# events.
def apply(dispatcher)
dispatcher.set_config(CFG_WIDTH, @spin_box.value.to_s)
end
end
# A (modal) option page, also called a "focus page". This page is
# registered like an editor options page. It is brought to front
# when the user hits the "Tab" key during editing.
# In this case, this page uses "setup" and "apply" callbacks to
# set and fetch information. It also employs a handler named
# "update_box" to communicate changes between the client (the
# plugin) and the page.
#
# Attributes that the client needs to take care of are
# "self.box" (the current box), "self.pfix" (the start point)
# and "self.update_box".
class DragBoxFocusPage &lt; RBA::EditorOptionsPage
# Creates a new page with title "Options" and at
# position 1 (second from left)
attr_accessor :box
attr_accessor :pfix
attr_accessor :update_box
def initialize
super("Geometry", 2)
self.focus_page = true
self.modal_page = true
@box = RBA::DBox::new
@pfix = RBA::DPoint::new
layout = RBA::QGridLayout::new(self)
layout.setColumnStretch(1, 1)
label = RBA::QLabel::new("Width", self)
layout.addWidget(label, 0, 0, 1, 1)
@le_width = RBA::QLineEdit::new(self)
layout.addWidget(@le_width, 0, 1, 1, 1)
label = RBA::QLabel::new("Height", self)
layout.addWidget(label, 1, 0, 1, 1)
@le_height = RBA::QLineEdit::new(self)
layout.addWidget(@le_height, 1, 1, 1, 1)
layout.setRowStretch(2, 1)
end
# Is called when the page needs to be set up.
# We assume that the client has properly set up self.box
def setup(dispatcher)
@le_width.text = "%.12g" % @box.width
@le_height.text = "%.12g" % @box.height
end
# Apply is called when the dialog is accepted or the "Apply" button is pressed
# Usually this method is intended to submit configuration parameter changes,
# but we can use it for any other purpose as well.
def apply(dispatcher)
# fetches the coordinates from the entry boxes
# throws an exception in case of an error
x = @le_width.text.to_f
y = @le_height.text.to_f
# prepares a new box with the given dimensions
# using the initial point ("pfix") and considering
# the drag direction
t = b = @pfix.y
l = r = @pfix.x
if @box.bottom &lt; t - 1e-10
b = t - y
else
t = b + y
end
if @box.left &lt; l - 1e-10
l = r - x
else
r = l + x
end
# issue the event (call the handler) to inform the plugin of this change
if @update_box
@update_box.call(RBA::DBox::new(l, b, r, t))
end
end
end
# The widget placed into the configuration page
# A configuration page with a single entry box to change
# the box color in RGB hex style.
# Configuration pages appear in the Setup dialog and can
# communicate only through configuration parameter updates.
class DragBoxConfigPage &lt; RBA::ConfigPage
# Initializes the page. Places it on a new section ("Drag Box") and "Configure" page
# and creates a single entry field.
def initialize
super("Drag Box|Configure")
layout = RBA::QHBoxLayout::new(self)
label = RBA::QLabel::new("Color (hex, rrggbb)", self)
layout.addWidget(label)
@line_edit = RBA::QLineEdit::new(self)
layout.addWidget(@line_edit)
layout.addStretch(1)
end
# This method is called to request an update of the entry fields
def setup(dispatcher)
@line_edit.setText(dispatcher.get_config(CFG_COLOR))
end
# This method is called to request a transfer of the edited values
# to the configuration space.
def apply(dispatcher)
dispatcher.set_config(CFG_COLOR, @line_edit.text)
end
end
# The custom plugin implementation.
class DragBoxPlugin &lt; RBA::Plugin
def initialize(view)
super()
@marker = nil
@last_marker = nil
@box = nil
@start_point = nil
@view = view
@color = nil
@width = 1
end
# This method receives configuration callbacks
def configure(name, value)
if name == CFG_COLOR
# configure marker color
begin
@color = value != "" ? value.to_i(16) : nil
rescue
@color = nil
end
self._configure_marker
elsif name == CFG_WIDTH
# configure marker line width
begin
@width = value.to_i
rescue
@width = nil
end
self._configure_marker
end
end
# clears all markers
def _clear_marker
[ @marker, @last_marker ].each { |marker| marker &amp;&amp; marker._destroy }
@marker = nil
@last_marker = nil
end
# stops dragging the marker and copy to a static one
def _finish
if @last_marker
@last_marker._destroy
end
@last_marker = @marker
@marker = nil
# reset to idle
self.ungrab_mouse
RBA::MainWindow.instance.message("Box finished: " + @box.to_s, 10000)
end
# updates the marker with the current box
def _update_marker
if ! @marker
@marker = RBA::Marker::new(@view)
self._configure_marker
end
@marker.set(@box)
end
# changes the marker's appearance
def _configure_marker
if @marker
@marker.line_style = 2 # short-dashed
@marker.vertex_size = 0 # no vertexes
@marker.line_width = @width
@marker.color = @color ? (@color | 0xff000000) : 0 # auto
end
end
# Updates the box with the given value and updates the marker.
# This method is bound to the focus page handler when needed.
def _update_box(box)
@box = box
self._update_marker
end
# overloaded callback: the focus page is requested
def focus_page_open
# stop unless dragging
if !@marker
return
end
# configure the focus page and show it:
# the page will call the handler of "update_box" to commit
# changes to the box
fp = self.focus_page
fp.box = @box
fp.pfix = @start_point
fp.update_box = lambda { |box| self._update_box(box) }
ret = fp.show
fp.update_box = nil
if ret == 1
# accepted: stop dragging now, we are done.
self._finish
end
return ret
end
# overloaded callback:
# plugin is activated - i.e. the mode is selected
def activated
RBA::MainWindow.instance.message("Click on point to start dragging a box", 10000)
end
# overloaded callback:
# plugin is deactivated - i.e. the mode is unselected
def deactivated
self._clear_marker
RBA::MainWindow.instance.message("", 0)
end
# overloaded callback:
# a mouse button was clicked
def mouse_click_event(p, buttons, prio)
if prio
# first-level event: start a new box or
# stop dragging it and freeze the box
if ! @marker
p = self.snap2(p)
@box = RBA::DBox::new(p, p)
@start_point = p
self._clear_marker
self._update_marker
self.grab_mouse
RBA::MainWindow.instance.message("Drag the box and click again", 10000)
else
p = self.snap2(p, @start_point, true, self.ac_from_buttons(buttons))
self._update_box(RBA::DBox::new(@start_point, p))
self._finish
end
return true
end
return false
end
# overloaded callback:
# the mouse was moved
def mouse_moved_event(p, buttons, prio)
if prio
# first-level event: if not dragging, provide a
# mouse cursor for tracking. If dragging, update
# the box and provide a mouse cursor.
if !@marker
self.clear_mouse_cursors
p = self.snap2(p, :visualize =&gt; true)
self.add_mouse_cursor(p)
else
self.clear_mouse_cursors
p = self.snap2(p, @start_point, true, self.ac_from_buttons(buttons), :visualize =&gt; true)
self.add_mouse_cursor(p)
@box = RBA::DBox::new(@start_point, p)
self._update_marker
end
end
# NOTE: we must not digest this event (i.e. return true)
# to allow the mouse tracker to receive the events as well
return false
end
end
# Implements a "plugin factory".
# The purpose of this object is to create a plugin object
# and corresponding UI objects.
class DragBoxPluginFactory &lt; RBA::PluginFactory
def initialize
super
self.has_tool_entry = true
# NOTE: it's a good practice to register configuration options
self.add_option(CFG_WIDTH, "1")
self.add_option(CFG_COLOR, "")
self.register(-1000, "drag_box", "Drag Box")
end
# Called to create the configuration pages
def create_config_pages
self.add_config_page(DragBoxConfigPage::new)
end
# Called to create the editor options pages
def create_editor_options_pages
self.add_editor_options_page(DragBoxEditorOptionsPage::new)
self.add_editor_options_page(DragBoxFocusPage::new)
end
# Creates the plugin
def create_plugin(manager, root, view)
return DragBoxPlugin::new(view)
end
end
# Creates the singleton instance - as we register it,
# it is not garbage collected
DragBoxPluginFactory::new
end
</text>
</klayout-macro>

View File

@ -0,0 +1,443 @@
<?xml version="1.0" encoding="utf-8"?>
<klayout-macro>
<description>A plugin sample\nThis sample provides a box drawing feature and demonstrates UI components and snapping</description>
<format>general</format>
<autorun>true</autorun>
<autorun-early>false</autorun-early>
<show-in-menu>false</show-in-menu>
<shortcut></shortcut>
<interpreter>python</interpreter>
<text># Sample plugin
# This plugin implements a box that can be drawn by
# clicking at the first and then at the second point.
# There is one box which is replacing the previous one.
# Line color and line width of the box can be configured
# by editor options (line width) or configuration pages
# (color). These settings are managed through configuration
# options and their current state is persisted.
#
# The dimension of the box can be entered numerically
# while dragging the box. This feature is implemented
# through a modal "focus page", which opens when you
# press the Tab key during editing and when the keyboard
# focus is on the canvas.
#
# Register this macro as "autorun" to enable the plugin
# on startup.
cfg_color = "drag-box-color"
cfg_width = "drag-box-width"
# The widget placed into the editor options dock
class DragBoxEditorOptionsPage(pya.EditorOptionsPage):
"""
An option page providing a single entry box for configuring the line width
This page communicates via configuration options. One advantage of this
approach is that the values are persisted
"""
def __init__(self):
"""
Creates a new page with title "Options" and at position 1 (second from left)
"""
super(DragBoxEditorOptionsPage, self).__init__("Options", 1)
layout2 = pya.QVBoxLayout(self)
layout = pya.QHBoxLayout(self)
layout2.addLayout(layout)
label = pya.QLabel("Line width", self)
layout.addWidget(label)
self.spin_box = pya.QSpinBox(self)
self.spin_box.setMinimum(1)
self.spin_box.setMaximum(16)
layout.addWidget(self.spin_box)
layout.addStretch(1)
layout2.addStretch(1)
# connect the spin box value change with the "edited" slot
# which will result in a call of "apply".
self.spin_box.valueChanged = lambda x: self.edited()
def setup(self, dispatcher):
"""
"setup" is called when the page needs to be populated with information -
i.e. on first show.
"""
try:
self.spin_box.setValue(int(dispatcher.get_config(cfg_width)))
except:
self.spin_box.setValue(1)
def apply(self, dispatcher):
"""
"apply" is called when the page is requested to submit the entered
values to the plugin. Usually this should be done via configuration
events.
"""
dispatcher.set_config(cfg_width, str(self.spin_box.value))
# The modal dialog page that appears when "Tab" is pressed
class DragBoxFocusPage(pya.EditorOptionsPage):
"""
A (modal) option page, also called a "focus page". This page is
registered like an editor options page. It is brought to front
when the user hits the "Tab" key during editing.
In this case, this page uses "setup" and "apply" callbacks to
set and fetch information. It also employs a handler named
"update_box" to communicate changes between the client (the
plugin) and the page.
Attributes that the client needs to take care of are
"self.box" (the current box), "self.pfix" (the start point)
and "self.update_box".
"""
def __init__(self):
"""
Creates a new page with title "Options" and at
position 1 (second from left)
"""
super(DragBoxFocusPage, self).__init__("Geometry", 2)
self.focus_page = True
self.modal_page = True
self.box = pya.DBox()
self.pfix = pya.DPoint()
self.update_box = None
layout = pya.QGridLayout(self)
layout.setColumnStretch(1, 1)
label = pya.QLabel("Width", self)
layout.addWidget(label, 0, 0, 1, 1)
self.le_width = pya.QLineEdit(self)
layout.addWidget(self.le_width, 0, 1, 1, 1)
label = pya.QLabel("Height", self)
layout.addWidget(label, 1, 0, 1, 1)
self.le_height = pya.QLineEdit(self)
layout.addWidget(self.le_height, 1, 1, 1, 1)
layout.setRowStretch(2, 1)
def setup(self, dispatcher):
"""
Is called when the page needs to be set up.
We assume that the client has properly set up self.box
"""
self.le_width.text = "%.12g" % self.box.width()
self.le_height.text = "%.12g" % self.box.height()
def apply(self, dispatcher):
"""
Apply is called when the dialog is accepted or the "Apply" button is pressed
Usually this method is intended to submit configuration parameter changes,
but we can use it for any other purpose as well.
"""
# fetches the coordinates from the entry boxes
# throws an exception in case of an error
x = float(self.le_width.text)
y = float(self.le_height.text)
# prepares a new box with the given dimensions
# using the initial point ("pfix") and considering
# the drag direction
t = b = self.pfix.y
l = r = self.pfix.x
if self.box.bottom &lt; t - 1e-10:
b = t - y
else:
t = b + y
if self.box.left &lt; l - 1e-10:
l = r - x
else:
r = l + x
# issue the event (call the handler) to inform the plugin of this change
if self.update_box is not None:
self.update_box(pya.DBox(l, b, r, t))
# The widget placed into the configuration page
class DragBoxConfigPage(pya.ConfigPage):
"""
A configuration page with a single entry box to change
the box color in RGB hex style.
Configuration pages appear in the Setup dialog and can
communicate only through configuration parameter updates.
"""
def __init__(self):
"""
Initializes the page. Places it on a new section ("Drag Box") and "Configure" page
and creates a single entry field.
"""
super(DragBoxConfigPage, self).__init__("Drag Box|Configure")
layout = pya.QHBoxLayout(self)
label = pya.QLabel("Color (hex, rrggbb)", self)
layout.addWidget(label)
self.line_edit = pya.QLineEdit(self)
layout.addWidget(self.line_edit)
layout.addStretch(1)
def setup(self, dispatcher):
"""
This method is called to request an update of the entry fields
"""
self.line_edit.setText(dispatcher.get_config(cfg_color))
def apply(self, dispatcher):
"""
This method is called to request a transfer of the edited values
to the configuration space.
"""
dispatcher.set_config(cfg_color, self.line_edit.text)
class DragBoxPlugin(pya.Plugin):
"""
The custom plugin implementation.
"""
def __init__(self, view):
super(DragBoxPlugin, self).__init__()
self.marker = None
self.last_marker = None
self.box = None
self.start_point = None
self.view = view
self.color = None
self.width = 1
def configure(self, name, value):
"""
This method receives configuration callbacks
"""
if name == cfg_color:
# configure marker color
try:
if value != "":
self.color = int(value, 16)
else:
self.color = None
except:
self.color = None
self._configure_marker()
elif name == cfg_width:
# configure marker line width
try:
self.width = int(value)
except:
self.width = None
self._configure_marker()
def _clear_marker(self):
"""
clears all markers
"""
for marker in [ self.marker, self.last_marker ]:
if marker is not None:
marker._destroy()
self.marker = None
self.last_marker = None
def _finish(self):
"""
stops dragging the marker and copy to a static one
"""
if self.last_marker is not None:
self.last_marker._destroy()
self.last_marker = self.marker
self.marker = None
# reset to idle
self.ungrab_mouse()
pya.MainWindow.instance().message("Box finished: " + str(self.box), 10000)
def _update_marker(self):
"""
updates the marker with the current box
"""
if self.marker is None:
self.marker = pya.Marker(self.view)
self._configure_marker()
self.marker.set(self.box)
def _configure_marker(self):
"""
changes the marker's appearance
"""
if self.marker is not None:
self.marker.line_style = 2 # short-dashed
self.marker.vertex_size = 0 # no vertexes
self.marker.line_width = self.width
if self.color is not None:
self.marker.color = self.color | 0xff000000
else:
self.marker.color = 0 # auto
def _update_box(self, box):
"""
Updates the box with the given value and updates the marker.
This method is bound to the focus page handler when needed.
"""
self.box = box
self._update_marker()
def focus_page_open(self):
"""
overloaded callback: the focus page is requested
"""
# stop unless dragging
if self.marker is None:
return
# configure the focus page and show it:
# the page will call the handler of "update_box" to commit
# changes to the box
fp = self.focus_page()
fp.box = self.box
fp.pfix = self.start_point
fp.update_box = self._update_box
ret = fp.show()
fp.update_box = None
if ret == 1:
# accepted: stop dragging now, we are done.
self._finish()
return ret
def activated(self):
"""
overloaded callback:
plugin is activated - i.e. the mode is selected
"""
pya.MainWindow.instance().message("Click on point to start dragging a box", 10000)
def deactivated(self):
"""
overloaded callback:
plugin is deactivated - i.e. the mode is unselected
"""
self._clear_marker()
pya.MainWindow.instance().message("", 0)
def mouse_click_event(self, p, buttons, prio):
"""
overloaded callback:
a mouse button was clicked
"""
if prio:
# first-level event: start a new box or
# stop dragging it and freeze the box
if self.marker is None:
p = self.snap2(p)
self.box = pya.DBox(p, p)
self.start_point = p
self._clear_marker()
self._update_marker()
self.grab_mouse()
pya.MainWindow.instance().message("Drag the box and click again", 10000)
else:
p = self.snap2(p, self.start_point, True, self.ac_from_buttons(buttons))
self._update_box(pya.DBox(self.start_point, p))
self._finish()
return True
return False
def mouse_moved_event(self, p, buttons, prio):
"""
overloaded callback:
the mouse was moved
"""
if prio:
# first-level event: if not dragging, provide a
# mouse cursor for tracking. If dragging, update
# the box and provide a mouse cursor.
if self.marker is None:
self.clear_mouse_cursors()
p = self.snap2(p, visualize=True)
self.add_mouse_cursor(p)
else:
self.clear_mouse_cursors()
p = self.snap2(p, self.start_point, True, self.ac_from_buttons(buttons), visualize=True)
self.add_mouse_cursor(p)
self.box = pya.DBox(self.start_point, p)
self._update_marker()
# NOTE: we must not digest this event (i.e. return True)
# to allow the mouse tracker to receive the events as well
return False
class DragBoxPluginFactory(pya.PluginFactory):
"""
Implements a "plugin factory".
The purpose of this object is to create a plugin object
and corresponding UI objects.
"""
def __init__(self):
super(DragBoxPluginFactory, self).__init__()
self.has_tool_entry = True
# NOTE: it's a good practice to register configuration options
self.add_option(cfg_width, "1")
self.add_option(cfg_color, "")
self.register(-1000, "drag_box", "Drag Box")
def create_config_pages(self):
"""
Called to create the configuration pages
"""
self.add_config_page(DragBoxConfigPage())
def create_editor_options_pages(self):
"""
Called to create the editor options pages
"""
self.add_editor_options_page(DragBoxEditorOptionsPage())
self.add_editor_options_page(DragBoxFocusPage())
def create_plugin(self, manager, root, view):
"""
Creates the plugin
"""
return DragBoxPlugin(view)
# Creates the singleton instance - as we register it,
# it is not garbage collected
DragBoxPluginFactory()
</text>
</klayout-macro>

View File

@ -25,6 +25,7 @@ editor_hooks_sample.lym
qt_designer.lym
qt_dialog.lym
qt_server.lym
drag_box_sample.lym
[pymacros]
# General group
@ -45,4 +46,5 @@ editor_hooks_sample_python.lym
qt_designer_python.lym
qt_dialog_python.lym
qt_server_python.lym
drag_box_sample_python.lym

View File

@ -0,0 +1,139 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2025 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 "gsiDecl.h"
#include "gsiDeclBasic.h"
#include "layDispatcher.h"
namespace gsi
{
static std::vector<std::string>
get_config_names (lay::Dispatcher *dispatcher)
{
std::vector<std::string> names;
dispatcher->get_config_names (names);
return names;
}
static lay::Dispatcher *dispatcher_instance ()
{
return lay::Dispatcher::instance ();
}
static tl::Variant get_config (lay::Dispatcher *dispatcher, const std::string &name)
{
std::string value;
if (dispatcher->config_get (name, value)) {
return tl::Variant (value);
} else {
return tl::Variant ();
}
}
/**
* @brief Exposes the Dispatcher interface
*
* This interface is intentionally not derived from Plugin. It is used currently to
* identify the dispatcher node for configuration. The Plugin nature of this interface
* is somewhat artificial and may be removed later.
*
* TODO: this is a duplicate of the respective methods in LayoutView and Application.
* This is intentional since we don't want to spend the only derivation path on this.
* Once there is a mixin concept, provide a path through that concept.
*/
Class<lay::Dispatcher> decl_Dispatcher ("lay", "Dispatcher",
method ("clear_config", &lay::Dispatcher::clear_config,
"@brief Clears the configuration parameters\n"
) +
method ("instance", &dispatcher_instance,
"@brief Gets the singleton instance of the Dispatcher object\n"
"\n"
"@return The instance\n"
) +
method ("write_config", &lay::Dispatcher::write_config, gsi::arg ("file_name"),
"@brief Writes configuration to a file\n"
"@return A value indicating whether the operation was successful\n"
"\n"
"If the configuration file cannot be written, false \n"
"is returned but no exception is thrown.\n"
) +
method ("read_config", &lay::Dispatcher::read_config, gsi::arg ("file_name"),
"@brief Reads the configuration from a file\n"
"@return A value indicating whether the operation was successful\n"
"\n"
"This method silently does nothing, if the config file does not\n"
"exist. If it does and an error occurred, the error message is printed\n"
"on stderr. In both cases, false is returned.\n"
) +
method_ext ("get_config", &get_config, gsi::arg ("name"),
"@brief Gets the value of a local configuration parameter\n"
"\n"
"@param name The name of the configuration parameter whose value shall be obtained (a string)\n"
"\n"
"@return The value of the parameter or nil if there is no such parameter\n"
) +
method ("set_config", (void (lay::Dispatcher::*) (const std::string &, const std::string &)) &lay::Dispatcher::config_set, gsi::arg ("name"), gsi::arg ("value"),
"@brief Set a local configuration parameter with the given name to the given value\n"
"\n"
"@param name The name of the configuration parameter to set\n"
"@param value The value to which to set the configuration parameter\n"
"\n"
"This method sets a configuration parameter with the given name to the given value. "
"Values can only be strings. Numerical values have to be converted into strings first. "
"Local configuration parameters override global configurations for this specific view. "
"This allows for example to override global settings of background colors. "
"Any local settings are not written to the configuration file. "
) +
method_ext ("get_config_names", &get_config_names,
"@brief Gets the configuration parameter names\n"
"\n"
"@return A list of configuration parameter names\n"
"\n"
"This method returns the names of all known configuration parameters. These names can be used to "
"get and set configuration parameter values.\n"
) +
method ("commit_config", &lay::Dispatcher::config_end,
"@brief Commits the configuration settings\n"
"\n"
"Some configuration options are queued for performance reasons and become active only after 'commit_config' has been called. "
"After a sequence of \\set_config calls, this method should be called to activate the "
"settings made by these calls.\n"
),
"@brief Root of the configuration space in the plugin context and menu dispatcher\n"
"\n"
"This class provides access to the root configuration space in the context "
"of plugin programming. You can use this class to obtain configuration parameters "
"from the configuration tree during plugin initialization. However, the "
"preferred way of plugin configuration is through \\Plugin#configure.\n"
"\n"
"Currently, the application object provides an identical entry point for configuration modification. "
"For example, \"Application::instance.set_config\" is identical to \"Dispatcher::instance.set_config\". "
"Hence there is little motivation for the Dispatcher class currently and "
"this interface may be modified or removed in the future."
"\n"
"This class has been introduced in version 0.25 as 'PluginRoot'.\n"
"It is renamed and enhanced as 'Dispatcher' in 0.27."
);
}

View File

@ -144,6 +144,11 @@ static std::string get_line_style (lay::LayoutViewBase *view, unsigned int index
return view->line_styles ().style (index).to_string ();
}
static std::string layer_list_name (lay::LayoutViewBase *view, unsigned int index)
{
return view->get_properties (index).name ();
}
static void transaction (lay::LayoutViewBase *view, const std::string &desc)
{
view->manager ()->transaction (desc);
@ -358,14 +363,6 @@ static QWidget *widget (lay::LayoutViewBase *view)
#endif
static std::vector<std::string>
get_config_names (lay::LayoutViewBase *view)
{
std::vector<std::string> names;
view->get_config_names (names);
return names;
}
static void
send_key_press_event (lay::LayoutViewBase *view, unsigned int key, unsigned int buttons)
{
@ -497,7 +494,9 @@ static bool view_is_dirty (lay::LayoutViewBase *view)
return view->is_dirty ();
}
LAYBASIC_PUBLIC Class<lay::LayoutViewBase> decl_LayoutViewBase ("lay", "LayoutViewBase",
extern Class<lay::Dispatcher> decl_Dispatcher;
LAYBASIC_PUBLIC Class<lay::LayoutViewBase> decl_LayoutViewBase (decl_Dispatcher, "lay", "LayoutViewBase",
gsi::constant ("LV_NoLayers", (unsigned int) lay::LayoutViewBase::LV_NoLayers,
"@brief With this option, no layers view will be provided (see \\layer_control_frame)\n"
"Use this value with the constructor's 'options' argument.\n"
@ -1572,6 +1571,10 @@ LAYBASIC_PUBLIC Class<lay::LayoutViewBase> decl_LayoutViewBase ("lay", "LayoutVi
"@brief Sets the title of the given layer properties tab\n"
"This method has been introduced in version 0.21.\n"
) +
gsi::method_ext ("layer_list_name", &layer_list_name, gsi::arg ("index"),
"@brief Gets the title of the given layer properties tab\n"
"This method has been introduced in version 0.30.4.\n"
) +
gsi::method_ext ("remove_stipple", &remove_stipple, gsi::arg ("index"),
"@brief Removes the stipple pattern with the given index\n"
"The pattern with an index less than the first custom pattern cannot be removed. "
@ -1940,68 +1943,6 @@ LAYBASIC_PUBLIC Class<lay::LayoutViewBase> decl_LayoutViewBase ("lay", "LayoutVi
"\n"
"This method has been added in version 0.26."
) +
// HINT: the cast is important to direct GSI to the LayoutView method rather than the
// Plugin method (in which case we get a segmentation violation ..)
// TODO: this method belongs to the Plugin interface and should be located there.
// Change this once there is a mixin concept available and the Plugin interface can
// be mixed into LayoutView.
gsi::method ("clear_config", (void (lay::LayoutViewBase::*)()) &lay::LayoutViewBase::clear_config,
"@brief Clears the local configuration parameters\n"
"\n"
"See \\set_config for a description of the local configuration parameters."
) +
// TODO: this method belongs to the Plugin interface and should be located there.
// Change this once there is a mixin concept available and the Plugin interface can
// be mixed into LayoutView.
gsi::method_ext ("get_config_names", &get_config_names,
"@brief Gets the configuration parameter names\n"
"\n"
"@return A list of configuration parameter names\n"
"\n"
"This method returns the names of all known configuration parameters. These names can be used to "
"get and set configuration parameter values.\n"
"\n"
"This method was introduced in version 0.25.\n"
) +
// TODO: this method belongs to the Plugin interface and should be located there.
// Change this once there is a mixin concept available and the Plugin interface can
// be mixed into LayoutView.
gsi::method ("get_config", (std::string (lay::LayoutViewBase::*)(const std::string &name) const) &lay::LayoutViewBase::config_get, gsi::arg ("name"),
"@brief Gets the value of a local configuration parameter\n"
"\n"
"@param name The name of the configuration parameter whose value shall be obtained (a string)\n"
"\n"
"@return The value of the parameter\n"
"\n"
"See \\set_config for a description of the local configuration parameters."
) +
// TODO: this method belongs to the Plugin interface and should be located there.
// Change this once there is a mixin concept available and the Plugin interface can
// be mixed into LayoutView.
gsi::method ("set_config", (void (lay::LayoutViewBase::*)(const std::string &name, const std::string &value)) &lay::LayoutViewBase::config_set, gsi::arg ("name"), gsi::arg ("value"),
"@brief Sets a local configuration parameter with the given name to the given value\n"
"\n"
"@param name The name of the configuration parameter to set\n"
"@param value The value to which to set the configuration parameter\n"
"\n"
"This method sets a local configuration parameter with the given name to the given value. "
"Values can only be strings. Numerical values have to be converted into strings first. "
"Local configuration parameters override global configurations for this specific view. "
"This allows for example to override global settings of background colors. "
"Any local settings are not written to the configuration file. "
) +
// TODO: this method belongs to the Plugin interface and should be located there.
// Change this once there is a mixin concept available and the Plugin interface can
// be mixed into LayoutView.
gsi::method ("commit_config", (void (lay::LayoutViewBase::*)()) &lay::LayoutViewBase::config_end,
"@brief Commits the configuration settings\n"
"\n"
"Some configuration options are queued for performance reasons and become active only after 'commit_config' has been called. "
"After a sequence of \\set_config calls, this method should be called to activate the "
"settings made by these calls.\n"
"\n"
"This method has been introduced in version 0.25.\n"
) +
gsi::method_ext ("transaction", &gsi::transaction, gsi::arg ("description"),
"@brief Begins a transaction\n"
"\n"

File diff suppressed because it is too large Load Diff

View File

@ -54,6 +54,15 @@ Editable::Editable (lay::Editables *editables)
}
}
void
Editable::init (lay::Editables *editables)
{
mp_editables = editables;
if (editables) {
editables->m_editables.push_back (this);
}
}
Editable::~Editable ()
{
// Reasoning for reset (): on MSVC, virtual functions must not be called inside

View File

@ -71,6 +71,11 @@ public:
*/
Editable (Editables *editables = 0);
/**
* @brief Initializes after constructor with a null pointer was called
*/
void init (Editables *editables);
/**
* @brief The constructor
*/

View File

@ -26,6 +26,10 @@
#include "layEditorOptionsPage.h"
#include "layEditorOptionsPages.h"
#include "layLayoutViewBase.h"
#include "tlExceptions.h"
#include <QApplication>
#include <QKeyEvent>
namespace lay
{
@ -34,16 +38,96 @@ namespace lay
// EditorOptionsPage implementation
EditorOptionsPage::EditorOptionsPage (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher)
: QWidget (0), mp_owner (0), m_active (true), mp_plugin_declaration (0), mp_dispatcher (dispatcher), mp_view (view)
: QWidget (0), mp_owner (0), m_active (true), m_focus_page (false), m_modal_page (false), mp_plugin_declaration (0), mp_dispatcher (dispatcher), mp_view (view)
{
attach_events ();
}
EditorOptionsPage::EditorOptionsPage ()
: QWidget (0), mp_owner (0), m_active (true), m_focus_page (false), m_modal_page (false), mp_plugin_declaration (0), mp_dispatcher (0), mp_view (0)
{
// .. nothing yet ..
}
EditorOptionsPage::~EditorOptionsPage ()
{
set_owner (0);
}
void
EditorOptionsPage::init (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher)
{
mp_view = view;
mp_dispatcher = dispatcher;
attach_events ();
}
void
EditorOptionsPage::edited ()
{
apply (dispatcher ());
}
static bool is_parent_widget (QWidget *w, QWidget *parent)
{
while (w && w != parent) {
w = dynamic_cast<QWidget *> (w->parent ());
}
return w == parent;
}
bool
EditorOptionsPage::focusNextPrevChild (bool next)
{
bool res = QWidget::focusNextPrevChild (next);
// Stop making the focus leave the page - this way we can jump back to the
// view on "enter"
if (res && ! is_modal_page () && ! is_parent_widget (QApplication::focusWidget (), this) && focusWidget ()) {
focusWidget ()->setFocus ();
}
return res;
}
void
EditorOptionsPage::keyPressEvent (QKeyEvent *event)
{
BEGIN_PROTECTED
if (! is_modal_page () && event->modifiers () == Qt::NoModifier && event->key () == Qt::Key_Return) {
// The Return key on a non-modal page commits the values and gives back the focus
// to the view
apply (dispatcher ());
view ()->set_focus ();
event->accept ();
} else {
QWidget::keyPressEvent (event);
}
END_PROTECTED
}
void
EditorOptionsPage::set_focus ()
{
setFocus (Qt::TabFocusReason);
QWidget::focusNextPrevChild (true);
}
int
EditorOptionsPage::show ()
{
if (mp_owner && m_active) {
if (! is_modal_page ()) {
mp_owner->make_page_current (this);
return -1;
} else {
return mp_owner->exec_modal (this) ? 1 : 0;
}
} else {
return -1;
}
}
void
EditorOptionsPage::attach_events ()
{

View File

@ -25,7 +25,7 @@
#ifndef HDR_layEditorOptionsPage
#define HDR_layEditorOptionsPage
#include "layuiCommon.h"
#include "laybasicCommon.h"
#include "tlObject.h"
@ -49,13 +49,14 @@ class EditorOptionsPages;
/**
* @brief The base class for a object properties page
*/
class LAYUI_PUBLIC EditorOptionsPage
class LAYBASIC_PUBLIC EditorOptionsPage
: public QWidget, public tl::Object
{
Q_OBJECT
public:
EditorOptionsPage (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher);
EditorOptionsPage ();
virtual ~EditorOptionsPage ();
virtual std::string title () const = 0;
@ -65,20 +66,28 @@ public:
virtual void commit_recent (lay::Dispatcher * /*root*/) { }
virtual void config_recent_for_layer (lay::Dispatcher * /*root*/, const db::LayerProperties & /*lp*/, int /*cv_index*/) { }
bool is_focus_page () const { return m_focus_page; }
void set_focus_page (bool f) { m_focus_page = f; }
void set_focus ();
bool is_modal_page () const { return m_modal_page; }
void set_modal_page (bool f) { m_modal_page = f; }
bool active () const { return m_active; }
void activate (bool active);
void set_owner (EditorOptionsPages *owner);
/**
* @brief Shows the editor page
* @return -1, if the page is shown non-modal, otherwise 1 or 0 if the dialog was accepted (1) or rejected (0)
*/
int show ();
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 ());
}
void init (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher);
protected:
lay::Dispatcher *dispatcher () const
{
return mp_dispatcher;
@ -89,12 +98,21 @@ protected:
return mp_view;
}
protected slots:
void edited ();
protected:
virtual void active_cellview_changed () { }
virtual void technology_changed (const std::string & /*tech*/) { }
virtual bool focusNextPrevChild (bool next);
virtual void keyPressEvent (QKeyEvent *event);
private:
EditorOptionsPages *mp_owner;
bool m_active;
bool m_focus_page;
bool m_modal_page;
const lay::PluginDeclaration *mp_plugin_declaration;
lay::Dispatcher *mp_dispatcher;
lay::LayoutViewBase *mp_view;

View File

@ -0,0 +1,449 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2025 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
*/
#if defined(HAVE_QT)
#include "tlInternational.h"
#include "layEditorOptionsPages.h"
#include "tlExceptions.h"
#include "layPlugin.h"
#include "layLayoutViewBase.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QTabWidget>
#include <QToolButton>
#include <QCompleter>
#include <QLineEdit>
#include <QDialogButtonBox>
#include <QPushButton>
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<lay::EditorOptionsPage *> &pages, lay::Dispatcher *dispatcher)
: QFrame (parent), mp_dispatcher (dispatcher)
{
mp_modal_pages = new EditorOptionsModalPages (this);
QVBoxLayout *ly1 = new QVBoxLayout (this);
ly1->setContentsMargins (0, 0, 0, 0);
mp_pages = new QTabWidget (this);
mp_pages->setSizePolicy (QSizePolicy (QSizePolicy::Ignored, QSizePolicy::Ignored));
ly1->addWidget (mp_pages);
m_pages = pages;
for (std::vector <lay::EditorOptionsPage *>::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 ();
}
delete mp_modal_pages;
mp_modal_pages = 0;
}
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 <lay::EditorOptionsPage *>::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) {
if ((*p)->active () && ! (*p)->is_modal_page ()) {
return true;
}
}
return false;
}
bool
EditorOptionsPages::has_modal_content () const
{
for (std::vector <lay::EditorOptionsPage *>::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) {
if ((*p)->active () && (*p)->is_modal_page ()) {
return true;
}
}
return false;
}
bool
EditorOptionsPages::exec_modal (EditorOptionsPage *page)
{
for (int i = 0; i < mp_modal_pages->count (); ++i) {
if (mp_modal_pages->widget (i) == page) {
// found the page - make it current and show the dialog
mp_modal_pages->set_current_index (i);
page->setup (mp_dispatcher);
page->set_focus ();
return mp_modal_pages->exec () != 0;
}
}
return false;
}
void
EditorOptionsPages::activate (const lay::Plugin *plugin)
{
for (auto op = m_pages.begin (); op != m_pages.end (); ++op) {
bool is_active = false;
if ((*op)->plugin_declaration () == 0) {
is_active = (plugin && plugin->plugin_declaration ()->enable_catchall_editor_options_pages ());
} else if (plugin && plugin->plugin_declaration () == (*op)->plugin_declaration ()) {
is_active = true;
}
(*op)->activate (is_active);
}
}
void
EditorOptionsPages::unregister_page (lay::EditorOptionsPage *page)
{
std::vector <lay::EditorOptionsPage *> pages;
for (std::vector <lay::EditorOptionsPage *>::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::make_page_current (lay::EditorOptionsPage *page)
{
for (int i = 0; i < mp_pages->count (); ++i) {
if (mp_pages->widget (i) == page) {
mp_pages->setCurrentIndex (i);
page->setup (mp_dispatcher);
page->set_focus ();
break;
}
}
}
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 <lay::EditorOptionsPage *> 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);
}
while (mp_modal_pages->count () > 0) {
mp_modal_pages->remove_page (0);
}
int index = -1;
int modal_index = -1;
for (std::vector <lay::EditorOptionsPage *>::iterator p = sorted_pages.begin (); p != sorted_pages.end (); ++p) {
if ((*p)->active ()) {
if (! (*p)->is_modal_page ()) {
if ((*p) == page) {
index = mp_pages->count ();
}
mp_pages->addTab (*p, tl::to_qstring ((*p)->title ()));
} else {
if ((*p) == page) {
modal_index = mp_modal_pages->count ();
}
mp_modal_pages->add_page (*p);
}
} 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);
if (modal_index < 0) {
modal_index = mp_modal_pages->current_index ();
}
if (modal_index >= int (mp_modal_pages->count ())) {
modal_index = mp_modal_pages->count () - 1;
}
mp_modal_pages->set_current_index (modal_index);
setVisible (mp_pages->count () > 0);
}
void
EditorOptionsPages::setup ()
{
BEGIN_PROTECTED
for (std::vector <lay::EditorOptionsPage *>::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 (false);
do_apply (true);
END_PROTECTED_W (this)
}
void
EditorOptionsPages::do_apply (bool modal)
{
for (std::vector <lay::EditorOptionsPage *>::iterator p = m_pages.begin (); p != m_pages.end (); ++p) {
if ((*p)->active () && modal == (*p)->is_modal_page ()) {
// 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 (false);
END_PROTECTED_W (this)
}
// ------------------------------------------------------------------
// EditorOptionsModalPages implementation
EditorOptionsModalPages::EditorOptionsModalPages (EditorOptionsPages *parent)
: QDialog (parent), mp_parent (parent), mp_single_page (0)
{
QVBoxLayout *ly = new QVBoxLayout (this);
ly->setContentsMargins (0, 0, 0, 0);
QVBoxLayout *ly4 = new QVBoxLayout (0);
ly4->setContentsMargins (6, 6, 6, 0);
ly->addLayout (ly4);
mp_pages = new QTabWidget (this);
ly4->addWidget (mp_pages, 1);
#if QT_VERSION >= 0x50400
mp_pages->setTabBarAutoHide (true);
#endif
mp_pages->hide ();
mp_single_page_frame = new QFrame (this);
QVBoxLayout *ly2 = new QVBoxLayout (mp_single_page_frame);
ly2->setContentsMargins (0, 0, 0, 0);
ly->addWidget (mp_single_page_frame, 1);
mp_single_page_frame->hide ();
QVBoxLayout *ly3 = new QVBoxLayout (0);
ly3->setContentsMargins (6, 6, 6, 6);
ly->addLayout (ly3);
mp_button_box = new QDialogButtonBox (this);
ly3->addWidget (mp_button_box);
mp_button_box->setOrientation (Qt::Horizontal);
mp_button_box->setStandardButtons (QDialogButtonBox::Cancel | QDialogButtonBox::Apply | QDialogButtonBox::Ok);
connect (mp_button_box, SIGNAL (clicked(QAbstractButton *)), this, SLOT (clicked(QAbstractButton *)));
connect (mp_button_box, SIGNAL (accepted()), this, SLOT (accept()));
connect (mp_button_box, SIGNAL (rejected()), this, SLOT (reject()));
update_title ();
}
EditorOptionsModalPages::~EditorOptionsModalPages ()
{
// .. nothing yet ..
}
int
EditorOptionsModalPages::count ()
{
return mp_single_page ? 1 : mp_pages->count ();
}
int
EditorOptionsModalPages::current_index ()
{
return mp_single_page ? 0 : mp_pages->currentIndex ();
}
void
EditorOptionsModalPages::set_current_index (int index)
{
if (! mp_single_page) {
mp_pages->setCurrentIndex (index);
}
}
void
EditorOptionsModalPages::add_page (EditorOptionsPage *page)
{
if (! mp_single_page) {
if (mp_pages->count () == 0) {
mp_single_page = page;
mp_single_page->setParent (mp_single_page_frame);
mp_single_page_frame->layout ()->addWidget (mp_single_page);
mp_single_page_frame->show ();
mp_pages->hide ();
} else {
mp_pages->addTab (page, tl::to_qstring (page->title ()));
}
} else {
mp_pages->clear ();
mp_single_page_frame->layout ()->removeWidget (mp_single_page);
mp_single_page_frame->hide ();
mp_pages->addTab (mp_single_page, tl::to_qstring (mp_single_page->title ()));
mp_single_page = 0;
mp_pages->addTab (page, tl::to_qstring (page->title ()));
mp_pages->show ();
}
update_title ();
}
void
EditorOptionsModalPages::remove_page (int index)
{
if (mp_single_page) {
if (index == 0) {
mp_single_page->setParent (0);
mp_single_page = 0;
mp_single_page_frame->hide ();
mp_single_page_frame->layout ()->removeWidget (mp_single_page);
}
} else {
mp_pages->removeTab (index);
if (mp_pages->count () == 1) {
mp_pages->hide ();
mp_single_page = dynamic_cast<EditorOptionsPage *> (mp_pages->widget (0));
mp_pages->removeTab (0);
mp_single_page->setParent (mp_single_page_frame);
mp_single_page_frame->layout ()->addWidget (mp_single_page);
mp_single_page_frame->show ();
}
}
update_title ();
}
void
EditorOptionsModalPages::update_title ()
{
if (mp_single_page) {
setWindowTitle (tl::to_qstring (mp_single_page->title ()));
} else {
setWindowTitle (tr ("Editor Options"));
}
}
EditorOptionsPage *
EditorOptionsModalPages::widget (int index)
{
if (mp_single_page) {
return index == 0 ? mp_single_page : 0;
} else {
return dynamic_cast<EditorOptionsPage *> (mp_pages->widget (index));
}
}
void
EditorOptionsModalPages::accept ()
{
BEGIN_PROTECTED
mp_parent->do_apply (true);
QDialog::accept ();
END_PROTECTED
}
void
EditorOptionsModalPages::reject ()
{
QDialog::reject ();
}
void
EditorOptionsModalPages::clicked (QAbstractButton *button)
{
BEGIN_PROTECTED
if (button == mp_button_box->button (QDialogButtonBox::Apply)) {
mp_parent->do_apply (true);
}
END_PROTECTED
}
}
#endif

View File

@ -25,17 +25,19 @@
#ifndef HDR_layEditorOptionsPages
#define HDR_layEditorOptionsPages
#include "layuiCommon.h"
#include "laybasicCommon.h"
#include "layEditorOptionsPage.h"
#include <tlVariant.h>
#include <QFrame>
#include <QDialog>
#include <vector>
#include <string>
class QTabWidget;
class QLabel;
class QDialogButtonBox;
class QAbstractButton;
namespace lay
{
@ -43,11 +45,12 @@ namespace lay
class PluginDeclaration;
class Dispatcher;
class Plugin;
class EditorOptionsModalPages;
/**
* @brief The object properties dialog
* @brief The object properties tab widget
*/
class LAYUI_PUBLIC EditorOptionsPages
class LAYBASIC_PUBLIC EditorOptionsPages
: public QFrame
{
Q_OBJECT
@ -58,7 +61,10 @@ public:
void unregister_page (lay::EditorOptionsPage *page);
void activate_page (lay::EditorOptionsPage *page);
void activate (const lay::Plugin *plugin);
void focusInEvent (QFocusEvent *event);
void make_page_current (lay::EditorOptionsPage *page);
bool exec_modal (lay::EditorOptionsPage *page);
const std::vector <lay::EditorOptionsPage *> &pages () const
{
@ -66,6 +72,8 @@ public:
}
bool has_content () const;
bool has_modal_content () const;
void do_apply (bool modal);
public slots:
void apply ();
@ -75,9 +83,43 @@ private:
std::vector <lay::EditorOptionsPage *> m_pages;
lay::Dispatcher *mp_dispatcher;
QTabWidget *mp_pages;
EditorOptionsModalPages *mp_modal_pages;
void update (lay::EditorOptionsPage *page);
void do_apply ();
};
/**
* @brief The object properties modal page dialog
*/
class LAYBASIC_PUBLIC EditorOptionsModalPages
: public QDialog
{
Q_OBJECT
public:
EditorOptionsModalPages (EditorOptionsPages *parent);
~EditorOptionsModalPages ();
int count ();
int current_index ();
void set_current_index (int index);
void add_page (EditorOptionsPage *page);
void remove_page (int index);
EditorOptionsPage *widget (int index);
private slots:
void accept ();
void reject ();
void clicked (QAbstractButton *button);
private:
EditorOptionsPages *mp_parent;
QTabWidget *mp_pages;
QFrame *mp_single_page_frame;
EditorOptionsPage *mp_single_page;
QDialogButtonBox *mp_button_box;
void update_title ();
};
}

View File

@ -21,6 +21,8 @@
*/
#include "layEditorServiceBase.h"
#include "layEditorOptionsPage.h"
#include "layEditorOptionsPages.h"
#include "layViewport.h"
#include "layLayoutViewBase.h"
#include "laybasicConfig.h"
@ -207,16 +209,26 @@ private:
// --------------------------------------------------------------------------------------
EditorServiceBase::EditorServiceBase (LayoutViewBase *view)
: lay::ViewService (view->canvas ()),
: lay::ViewService (view ? view->canvas () : 0),
lay::Editable (view),
lay::Plugin (view),
mp_view (view),
m_cursor_enabled (true),
m_has_tracking_position (false)
m_has_tracking_position (false),
m_active (false)
{
// .. nothing yet ..
}
void
EditorServiceBase::init (LayoutViewBase *view)
{
mp_view = view;
lay::Plugin::init (view);
lay::ViewService::init (view ? view->canvas () : 0);
lay::Editable::init (view);
}
EditorServiceBase::~EditorServiceBase ()
{
clear_mouse_cursors ();
@ -265,9 +277,11 @@ EditorServiceBase::clear_mouse_cursors ()
}
void
EditorServiceBase::mouse_cursor_from_snap_details (const lay::PointSnapToObjectResult &snap_details)
EditorServiceBase::mouse_cursor_from_snap_details (const lay::PointSnapToObjectResult &snap_details, bool noclear)
{
clear_mouse_cursors ();
if (! noclear) {
clear_mouse_cursors ();
}
add_mouse_cursor (snap_details.snapped_point,
snap_details.object_snap == lay::PointSnapToObjectResult::ObjectVertex ||
@ -319,15 +333,95 @@ void
EditorServiceBase::deactivated ()
{
clear_mouse_cursors ();
if (ui ()) {
ui ()->ungrab_mouse (this);
}
m_active = false;
}
void
EditorServiceBase::activated ()
{
m_active = true;
}
#if defined(HAVE_QT)
std::vector<lay::EditorOptionsPage *>
EditorServiceBase::editor_options_pages ()
{
lay::EditorOptionsPages *eo_pages = mp_view->editor_options_pages ();
if (!eo_pages) {
return std::vector<lay::EditorOptionsPage *> ();
} else {
std::vector<lay::EditorOptionsPage *> pages;
for (auto p = eo_pages->pages ().begin (); p != eo_pages->pages ().end (); ++p) {
if ((*p)->plugin_declaration () == plugin_declaration ()) {
pages.push_back (*p);
}
}
return pages;
}
}
lay::EditorOptionsPage *
EditorServiceBase::focus_page ()
{
auto pages = editor_options_pages ();
for (auto p = pages.begin (); p != pages.end (); ++p) {
if ((*p)->is_focus_page ()) {
return *p;
}
}
return 0;
}
bool
EditorServiceBase::key_event (unsigned int key, unsigned int buttons)
{
if (is_active () && key == Qt::Key_Tab && buttons == 0) {
focus_page_open ();
return true;
} else {
return false;
}
}
int
EditorServiceBase::focus_page_open ()
{
auto fp = focus_page ();
return fp ? fp->show () : 0;
}
void
EditorServiceBase::show_error (tl::Exception &ex)
{
tl::error << ex.msg ();
#if defined(HAVE_QT)
QMessageBox::critical (ui ()->widget (), tr ("Error"), tl::to_qstring (ex.msg ()));
#endif
}
#else
bool
EditorServiceBase::key_event (unsigned int key, unsigned int buttons)
{
return false;
}
void
EditorServiceBase::show_error (tl::Exception &ex)
{
tl::error << ex.msg ();
}
int
EditorServiceBase::focus_page_open ()
{
return 0;
}
#endif
}

View File

@ -46,7 +46,12 @@ public:
/**
* @brief Constructor
*/
EditorServiceBase (lay::LayoutViewBase *view);
EditorServiceBase (lay::LayoutViewBase *view = 0);
/**
* @brief Initialize after constructor was called with null view pointer
*/
void init (lay::LayoutViewBase *view);
/**
* @brief Destructor
@ -69,6 +74,14 @@ public:
return this;
}
/**
* @brief Gets a value indicating whether the plugin is active
*/
bool is_active () const
{
return m_active;
}
/**
* @brief Adds a mouse cursor to the given point
*/
@ -97,7 +110,7 @@ public:
/**
* @brief Provides a nice mouse tracking cursor from the given snap details
*/
void mouse_cursor_from_snap_details (const lay::PointSnapToObjectResult &snap_details);
void mouse_cursor_from_snap_details (const lay::PointSnapToObjectResult &snap_details, bool noclear = false);
/**
* @brief Gets the tracking cursor color
@ -136,10 +149,141 @@ public:
*/
void show_error (tl::Exception &ex);
protected:
/**
* @brief Menu command handler
*/
virtual void menu_activated (const std::string & /*symbol*/)
{
// .. this implementation does nothing ..
}
/**
* @brief Sets a configuration option
*/
virtual bool configure (const std::string &name, const std::string &value);
/**
* @brief Configuration finalization
*/
virtual void config_finalize ()
{
lay::Plugin::config_finalize ();
}
/**
* @brief Called when the plugin is deactivated
*/
virtual void deactivated ();
/**
* @brief Called when the plugin is activated
*/
virtual void activated ();
/**
* @brief Key event handler
*/
virtual bool key_event (unsigned int /*key*/, unsigned int /*buttons*/);
/**
* @brief Mouse press event handler
*/
virtual bool mouse_press_event (const db::DPoint & /*p*/, unsigned int /*buttons*/, bool /*prio*/)
{
return false;
}
/**
* @brief Mouse single-click event handler
*/
virtual bool mouse_click_event (const db::DPoint & /*p*/, unsigned int /*buttons*/, bool /*prio*/)
{
return false;
}
/**
* @brief Mouse double-click event handler
*/
virtual bool mouse_double_click_event (const db::DPoint & /*p*/, unsigned int /*buttons*/, bool /*prio*/)
{
return false;
}
/**
* @brief Mouse leave event handler
*/
virtual bool leave_event (bool /*prio*/)
{
return false;
}
/**
* @brief Mouse enter event handler
*/
virtual bool enter_event (bool /*prio*/)
{
return false;
}
/**
* @brief Mouse move event handler
*/
virtual bool mouse_move_event (const db::DPoint & /*p*/, unsigned int /*buttons*/, bool /*prio*/)
{
return false;
}
/**
* @brief Mouse release event handler
*/
virtual bool mouse_release_event (const db::DPoint & /*p*/, unsigned int /*buttons*/, bool /*prio*/)
{
return false;
}
/**
* @brief Wheel event handler
*/
virtual bool wheel_event (int /*delta*/, bool /*horizontal*/, const db::DPoint & /*p*/, unsigned int /*buttons*/, bool /*prio*/)
{
return false;
}
/**
* @brief Updates the internal data after a coordinate system change for example
*/
virtual void update ()
{
// The default implementation does nothing
}
/**
* @brief This method is called when some mouse dragging operation should be cancelled
*/
virtual void drag_cancel ()
{
// The default implementation does nothing
}
/**
* @brief Gets called when the focus page opens
*
* The default implementation will call fp->show() and return its return value.
*/
virtual int focus_page_open ();
#if defined(HAVE_QT)
/**
* @brief Gets the editor options pages associated with this plugin
*/
std::vector<lay::EditorOptionsPage *> editor_options_pages ();
/**
* @brief Gets the focus page or 0 if there is none
*/
lay::EditorOptionsPage *focus_page ();
#endif
private:
// The marker representing the mouse cursor
lay::LayoutViewBase *mp_view;
@ -148,6 +292,7 @@ private:
bool m_cursor_enabled;
bool m_has_tracking_position;
db::DPoint m_tracking_position;
bool m_active;
};
}

View File

@ -25,8 +25,7 @@
#include "dbLayout.h"
#include "dbLibrary.h"
#include "edtUtils.h"
#include "edtService.h"
#include "layEditorUtils.h"
#include "layCellView.h"
#include "layLayoutViewBase.h"
@ -38,10 +37,35 @@
# include <QMessageBox>
#endif
namespace edt {
namespace lay
{
// -------------------------------------------------------------
int snap_range_pixels ()
{
return 8; // TODO: make variable
}
// Convert buttons to an angle constraint
lay::angle_constraint_type
ac_from_buttons (unsigned int buttons)
{
if ((buttons & lay::ShiftButton) != 0) {
if ((buttons & lay::ControlButton) != 0) {
return lay::AC_Any;
} else {
return lay::AC_Ortho;
}
} else {
if ((buttons & lay::ControlButton) != 0) {
return lay::AC_Diagonal;
} else {
return lay::AC_Global;
}
}
}
std::string pcell_parameters_to_string (const std::map<std::string, tl::Variant> &parameters)
{
std::string param;

View File

@ -21,15 +21,18 @@
*/
#ifndef HDR_edtUtils
#define HDR_edtUtils
#ifndef HDR_layEditorUtils
#define HDR_layEditorUtils
#include <limits>
#include <list>
#include <utility>
#include <vector>
#include "laybasicCommon.h"
#include "layObjectInstPath.h"
#include "laySnap.h"
#include "dbInstElement.h"
#include "dbClipboardData.h"
@ -38,23 +41,34 @@
namespace lay
{
class LayoutViewBase;
class Dispatcher;
}
namespace edt {
class LayoutViewBase;
class Dispatcher;
class Service;
// -------------------------------------------------------------
/**
* @brief Gets the snap range in pixels
*/
LAYBASIC_PUBLIC int snap_range_pixels ();
/**
* @brief Convert a button flag set to an angle constraint
*
* This implements the standard modifiers for angle constraints - i.e.
* ortho for "Shift".
*/
LAYBASIC_PUBLIC lay::angle_constraint_type ac_from_buttons (unsigned int buttons);
/**
* @brief Serializes PCell parameters to a string
*/
std::string pcell_parameters_to_string (const std::map<std::string, tl::Variant> &parameters);
LAYBASIC_PUBLIC std::string pcell_parameters_to_string (const std::map<std::string, tl::Variant> &parameters);
/**
* @brief Deserializes PCell parameters from a string
*/
std::map<std::string, tl::Variant> pcell_parameters_from_string (const std::string &s);
LAYBASIC_PUBLIC std::map<std::string, tl::Variant> pcell_parameters_from_string (const std::string &s);
/**
* @brief Fetch PCell parameters from a cell and merge the guiding shapes into them
@ -64,6 +78,7 @@ std::map<std::string, tl::Variant> pcell_parameters_from_string (const std::stri
* @param parameters_for_pcell Will receive the parameters
* @return true, if the cell is a PCell and parameters have been fetched
*/
LAYBASIC_PUBLIC
bool
get_parameters_from_pcell_and_guiding_shapes (db::Layout *layout, db::cell_index_type cell_index, db::pcell_parameters_type &parameters_for_pcell);
@ -71,23 +86,14 @@ get_parameters_from_pcell_and_guiding_shapes (db::Layout *layout, db::cell_index
/**
* @brief Request to make the given layer the current one (asks whether to create the layer if needed)
*/
LAYBASIC_PUBLIC
bool
set_or_request_current_layer (lay::LayoutViewBase *view, const db::LayerProperties &lp, unsigned int cv_index, bool make_current = true);
/**
* @brief A helper class that identifies clipboard data for edt::
*/
class ClipboardData
: public db::ClipboardData
{
public:
ClipboardData () { }
};
/**
* @brief A cache for the transformation variants for a certain layer and cell view index for a lay::LayoutView
*/
class TransformationVariants
class LAYBASIC_PUBLIC TransformationVariants
{
public:
TransformationVariants (const lay::LayoutViewBase *view, bool per_cv_and_layer = true, bool per_cv = true);

View File

@ -398,21 +398,6 @@ LayoutViewBase::init (db::Manager *mgr)
mp_canvas = new lay::LayoutCanvas (this);
// occupy services and editables:
// these services get deleted by the canvas destructor automatically:
if ((m_options & LV_NoTracker) == 0) {
mp_tracker = new lay::MouseTracker (this);
}
if ((m_options & LV_NoZoom) == 0) {
mp_zoom_service = new lay::ZoomService (this);
}
if ((m_options & LV_NoSelection) == 0) {
mp_selection_service = new lay::SelectionService (this);
}
if ((m_options & LV_NoMove) == 0) {
mp_move_service = new lay::MoveService (this);
}
create_plugins ();
}
@ -602,20 +587,41 @@ void LayoutViewBase::create_plugins (const lay::PluginDeclaration *except_this)
clear_plugins ();
// create the plugins
for (tl::Registrar<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::end (); ++cls) {
for (tl::Registrar<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::end (); ) {
if (&*cls != except_this) {
// NOTE: during "create_plugin" a plugin may be unregistered, so don't increment the iterator after
auto current = cls.operator-> ();
std::string current_name = cls.current_name ();
++cls;
if (current != except_this) {
// TODO: clean solution. The following is a HACK:
if (cls.current_name () == "ant::Plugin" || cls.current_name () == "img::Plugin") {
if (current_name == "ant::Plugin" || current_name == "img::Plugin") {
// ant and img are created always
create_plugin (&*cls);
create_plugin (current);
} else if (current_name == "laybasic::MouseTrackerPlugin") {
if ((m_options & LV_NoTracker) == 0) {
mp_tracker = dynamic_cast<lay::MouseTracker *> (create_plugin (current));
}
} else if (current_name == "laybasic::MoveServicePlugin") {
if ((m_options & LV_NoMove) == 0) {
mp_move_service = dynamic_cast<lay::MoveService *> (create_plugin (current));
}
} else if (current_name == "laybasic::SelectionServicePlugin") {
if ((m_options & LV_NoSelection) == 0) {
mp_selection_service = dynamic_cast<lay::SelectionService *> (create_plugin (current));
}
} else if (current_name == "laybasic::ZoomServicePlugin") {
if ((m_options & LV_NoZoom) == 0) {
mp_zoom_service = dynamic_cast<lay::ZoomService *> (create_plugin (current));
}
} else if ((options () & LV_NoPlugins) == 0) {
// others: only create unless LV_NoPlugins is set
create_plugin (&*cls);
} else if ((options () & LV_NoGrid) == 0 && cls.current_name () == "GridNetPlugin") {
create_plugin (current);
} else if ((options () & LV_NoGrid) == 0 && current_name == "lay::GridNetPlugin") {
// except grid net plugin which is created on request
create_plugin (&*cls);
create_plugin (current);
}
}
@ -685,6 +691,12 @@ LayoutViewBase::message (const std::string & /*s*/, int /*timeout*/)
// .. nothing yet ..
}
void
LayoutViewBase::set_focus ()
{
// .. nothing yet ..
}
bool
LayoutViewBase::is_dirty () const
{
@ -763,14 +775,6 @@ LayoutViewBase::configure (const std::string &name, const std::string &value)
{
lay::Dispatcher::configure (name, value);
if (mp_move_service && mp_move_service->configure (name, value)) {
return true;
}
if (mp_tracker && mp_tracker->configure (name, value)) {
return true;
}
if (name == cfg_default_lyp_file) {
m_def_lyp_file = value;
@ -1419,14 +1423,6 @@ LayoutViewBase::config_finalize ()
void
LayoutViewBase::enable_edits (bool enable)
{
// enable or disable these services:
if (mp_selection_service) {
mp_selection_service->enable (enable);
}
if (mp_move_service) {
mp_move_service->enable (enable);
}
// enable or disable the services that implement "lay::ViewService"
for (std::vector<lay::Plugin *>::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
lay::ViewService *svc = (*p)->view_service_interface ();
@ -1965,6 +1961,22 @@ LayoutViewBase::set_properties (unsigned int index, const LayerPropertiesList &p
}
}
void
LayoutViewBase::clear_layers (unsigned int index)
{
LayerPropertiesList ll;
ll.set_name (get_properties (index).name ());
set_properties (index, ll);
}
void
LayoutViewBase::clear_layers ()
{
LayerPropertiesList ll;
ll.set_name (get_properties ().name ());
set_properties (ll);
}
void
LayoutViewBase::expand_properties ()
{
@ -4848,13 +4860,6 @@ LayoutViewBase::background_color (tl::Color c)
do_set_background_color (c, contrast);
if (mp_selection_service) {
mp_selection_service->set_colors (c, contrast);
}
if (mp_zoom_service) {
mp_zoom_service->set_colors (c, contrast);
}
// Set the color for all ViewService interfaces
for (std::vector<lay::Plugin *>::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
lay::ViewService *svc = (*p)->view_service_interface ();
@ -5517,7 +5522,7 @@ LayoutViewBase::paste_interactive (bool transient_mode)
// operations.
trans->close ();
if (mp_move_service && mp_move_service->begin_move (trans.release (), transient_mode)) {
if (mp_move_service && mp_move_service->start_move (trans.release (), transient_mode)) {
switch_mode (-1); // move mode
}
}
@ -5796,20 +5801,12 @@ LayoutViewBase::mode (int m)
m_mode = m;
mp_active_plugin = 0;
if (m > 0) {
for (std::vector<lay::Plugin *>::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
if ((*p)->plugin_declaration ()->id () == m) {
mp_active_plugin = *p;
mp_canvas->activate ((*p)->view_service_interface ());
break;
}
for (std::vector<lay::Plugin *>::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
if ((*p)->plugin_declaration ()->id () == m) {
mp_active_plugin = *p;
mp_canvas->activate ((*p)->view_service_interface ());
break;
}
} else if (m == 0 && mp_selection_service) {
mp_canvas->activate (mp_selection_service);
} else if (m == -1 && mp_move_service) {
mp_canvas->activate (mp_move_service);
}
}

View File

@ -157,8 +157,8 @@ struct LAYBASIC_PUBLIC LayerDisplayProperties
* It manages the layer display list, bookmark list etc.
*/
class LAYBASIC_PUBLIC LayoutViewBase :
public lay::Editables,
public lay::Dispatcher
public lay::Dispatcher, // needs to be first as it is the GSI base class
public lay::Editables
{
public:
typedef lay::CellView::unspecific_cell_path_type cell_path_type;
@ -295,6 +295,11 @@ public:
*/
virtual void message (const std::string &s = "", int timeout = 10);
/**
* @brief Sets the keyboard focus to the view
*/
virtual void set_focus ();
/**
* @brief The "dirty" flag indicates that one of the layout has been modified
*
@ -510,18 +515,12 @@ public:
/**
* @brief Clear the given layer view list
*/
void clear_layers (unsigned int index)
{
set_properties (index, LayerPropertiesList ());
}
void clear_layers (unsigned int index);
/**
* @brief Clear the current layer view list
*/
void clear_layers ()
{
set_properties (LayerPropertiesList ());
}
void clear_layers ();
/**
* @brief Access the current layer properties list

View File

@ -31,7 +31,7 @@ namespace lay
{
MouseTracker::MouseTracker (lay::LayoutViewBase *view)
: lay::ViewService (view->canvas ()), mp_view (view),
: lay::ViewService (view->canvas ()), lay::Plugin (view), mp_view (view),
m_cursor_color (tl::Color ()), m_cursor_line_style (0), m_cursor_enabled (false)
{
ui ()->grab_mouse (this, false);
@ -129,5 +129,31 @@ MouseTracker::mouse_move_event (const db::DPoint &p, unsigned int /*buttons*/, b
return false;
}
} // namespace lay
// ----------------------------------------------------------------------------
// NOTE: configuration currently is not declared here.
// Same for the configuration pages.
class MouseTrackerDeclaration
: public lay::PluginDeclaration
{
public:
MouseTrackerDeclaration ()
{
// .. nothing yet ..
}
virtual lay::Plugin *create_plugin (db::Manager * /*manager*/, lay::Dispatcher * /*dispatcher*/, lay::LayoutViewBase *view) const
{
return new MouseTracker (view);
}
virtual bool enable_catchall_editor_options_pages () const
{
return false;
}
};
static tl::RegisteredClass<lay::PluginDeclaration> tracker_decl (new MouseTrackerDeclaration (), -1000, "laybasic::MouseTrackerPlugin");
} // namespace lay

View File

@ -26,6 +26,7 @@
#include "layViewObject.h"
#include "layMarker.h"
#include "layPlugin.h"
#include "tlObject.h"
class QMouseEvent;
@ -36,7 +37,7 @@ class LayoutCanvas;
class LayoutViewBase;
class MouseTracker
: public lay::ViewService
: public lay::ViewService, public lay::Plugin
{
public:
MouseTracker (lay::LayoutViewBase *view);
@ -45,6 +46,11 @@ public:
bool leave_event (bool prio);
bool configure (const std::string &name, const std::string &value);
lay::ViewService *view_service_interface ()
{
return this;
}
private:
lay::LayoutViewBase *mp_view;
tl::shared_collection<lay::DMarker> mp_markers;

View File

@ -20,11 +20,10 @@
*/
#include "layMove.h"
#include "layEditable.h"
#include "layLayoutViewBase.h"
#include "layEditorUtils.h"
#include "laySelector.h"
#include "laybasicConfig.h"
@ -35,7 +34,7 @@ namespace lay
// MoveService implementation
MoveService::MoveService (lay::LayoutViewBase *view)
: lay::ViewService (view->canvas ()),
: lay::EditorServiceBase (view),
m_dragging (false),
m_dragging_transient (false),
mp_editables (view),
@ -50,44 +49,36 @@ MoveService::~MoveService ()
drag_cancel ();
}
void
void
MoveService::deactivated ()
{
EditorServiceBase::deactivated ();
m_shift = db::DPoint ();
mp_view->clear_transient_selection ();
mp_editables->clear_transient_selection ();
drag_cancel ();
}
lay::angle_constraint_type
ac_from_buttons (unsigned int buttons)
{
if ((buttons & lay::ShiftButton) != 0) {
if ((buttons & lay::ControlButton) != 0) {
return lay::AC_Any;
} else {
return lay::AC_Ortho;
}
} else {
if ((buttons & lay::ControlButton) != 0) {
return lay::AC_Diagonal;
} else {
return lay::AC_Global;
}
}
}
bool
MoveService::configure (const std::string &name, const std::string &value)
{
if (lay::EditorServiceBase::configure (name, value)) {
return true;
}
if (name == cfg_grid) {
tl::from_string (value, m_global_grid);
}
return false; // not taken
}
bool
MoveService::key_event (unsigned int key, unsigned int /*buttons*/)
MoveService::key_event (unsigned int key, unsigned int buttons)
{
if (lay::EditorServiceBase::key_event (key, buttons)) {
return true;
}
double dx = 0.0, dy = 0.0;
if (int (key) == lay::KeyDown) {
dy = -1.0;
@ -129,6 +120,16 @@ MoveService::key_event (unsigned int key, unsigned int /*buttons*/)
}
}
int
MoveService::focus_page_open ()
{
// This method is called on "Tab" by "key_event". "fp" is null as we don't have a focus page registered.
if (is_active () && dispatcher ()) {
dispatcher ()->menu_activated ("cm_sel_move");
}
return 0;
}
bool
MoveService::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio)
{
@ -240,7 +241,7 @@ MoveService::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool
}
bool
MoveService::begin_move (db::Transaction *transaction, bool transient_selection)
MoveService::start_move (db::Transaction *transaction, bool transient_selection)
{
if (m_dragging) {
return false;
@ -308,7 +309,7 @@ MoveService::handle_click (const db::DPoint &p, unsigned int buttons, bool drag_
ui ()->hover_reset ();
mp_view->clear_transient_selection ();
mp_editables->clear_transient_selection ();
m_dragging = true;
m_dragging_transient = drag_transient;
@ -366,5 +367,24 @@ MoveService::finish ()
}
}
}
// ----------------------------------------------------------------------------
class MoveServiceDeclaration
: public lay::PluginDeclaration
{
public:
MoveServiceDeclaration ()
: lay::PluginDeclaration (-1)
{
// .. nothing yet ..
}
virtual lay::Plugin *create_plugin (db::Manager * /*manager*/, lay::Dispatcher * /*dispatcher*/, lay::LayoutViewBase *view) const
{
return new MoveService (view);
}
};
static tl::RegisteredClass<lay::PluginDeclaration> move_service_decl (new MoveServiceDeclaration (), -970, "laybasic::MoveServicePlugin");
}

View File

@ -24,25 +24,25 @@
#define HDR_layMove
#include "laybasicCommon.h"
#include "layEditorServiceBase.h"
#include "dbManager.h"
#include "layViewObject.h"
#include <memory>
namespace lay {
class Editables;
class LayoutViewBase;
class LAYBASIC_PUBLIC MoveService :
public lay::ViewService
public lay::EditorServiceBase
{
public:
MoveService (lay::LayoutViewBase *view);
~MoveService ();
bool start_move (db::Transaction *transaction = 0, bool transient_selection = false);
bool configure (const std::string &name, const std::string &value);
bool begin_move (db::Transaction *transaction = 0, bool transient_selection = false);
void finish ();
void cancel ();
@ -56,6 +56,7 @@ private:
virtual bool key_event (unsigned int key, unsigned int buttons);
virtual void drag_cancel ();
virtual void deactivated ();
int focus_page_open ();
bool handle_click (const db::DPoint &p, unsigned int buttons, bool drag_transient, db::Transaction *transaction);

View File

@ -55,6 +55,12 @@ PluginDeclaration::PluginDeclaration ()
// .. nothing yet ..
}
PluginDeclaration::PluginDeclaration (int id)
: m_id (id), m_editable_enabled (true)
{
// .. nothing yet ..
}
PluginDeclaration::~PluginDeclaration ()
{
if (Dispatcher::instance ()) {
@ -309,8 +315,23 @@ PluginDeclaration::register_plugin ()
// Plugin implementation
Plugin::Plugin (Plugin *parent, bool standalone)
: mp_parent (parent), mp_plugin_declaration (0), dm_finalize_config (this, &lay::Plugin::config_end), m_standalone (standalone)
: mp_parent (0), mp_plugin_declaration (0), dm_finalize_config (this, &lay::Plugin::config_end), m_standalone (false)
{
init (parent, standalone);
}
Plugin::Plugin ()
: mp_parent (0), mp_plugin_declaration (0), dm_finalize_config (this, &lay::Plugin::config_end), m_standalone (false)
{
// .. nothing yet (waiting for init) ..
}
void
Plugin::init (Plugin *parent, bool standalone)
{
mp_parent = parent;
m_standalone = standalone;
if (! parent) {
if (! standalone) {
// load the root with the default configuration
@ -325,6 +346,7 @@ Plugin::Plugin (Plugin *parent, bool standalone)
}
}
Plugin::~Plugin ()
{
if (mp_parent) {

View File

@ -160,6 +160,11 @@ public:
*/
PluginDeclaration ();
/**
* @brief Constructor with a fixed ID
*/
PluginDeclaration (int id);
/**
* @brief Destructor
*/
@ -331,6 +336,18 @@ public:
{
// .. no pages in the default implementation ..
}
/**
* @brief Gets a value indicating whether "catchall" editor options pages shall be included
*
* "catchall" editor options pages are ones that are unspecific and render a null "plugin_declaration".
* A plugin can choose to include these pages if it listens to global configuration events.
* Otherwise it should return false here to suppress these pages.
*/
virtual bool enable_catchall_editor_options_pages () const
{
return true;
}
#endif
/**
@ -504,6 +521,18 @@ public:
*/
Plugin (Plugin *parent, bool standalone = false);
/**
* @brief The default constructor
*
* This constructor needs to be followed by init()
*/
Plugin ();
/**
* @brief Initialization, following the default constructor
*/
void init (Plugin *parent, bool standalone = false);
/**
* @brief The destructor
*/

View File

@ -43,6 +43,7 @@ SelectionService::SelectionService (lay::LayoutViewBase *view) :
QObject (),
#endif
lay::ViewService (view->canvas ()),
lay::Plugin (view),
mp_view (view),
mp_box (0),
m_color (0),
@ -317,4 +318,29 @@ SelectionService::begin (const db::DPoint &pos)
ui ()->grab_mouse (this, true);
}
// ----------------------------------------------------------------------------
class SelectionServiceDeclaration
: public lay::PluginDeclaration
{
public:
SelectionServiceDeclaration ()
: lay::PluginDeclaration (0)
{
// .. nothing yet ..
}
virtual lay::Plugin *create_plugin (db::Manager * /*manager*/, lay::Dispatcher * /*dispatcher*/, lay::LayoutViewBase *view) const
{
return new SelectionService (view);
}
virtual bool enable_catchall_editor_options_pages () const
{
return false;
}
};
static tl::RegisteredClass<lay::PluginDeclaration> selection_service_decl (new SelectionServiceDeclaration (), -980, "laybasic::SelectionServicePlugin");
}

View File

@ -29,6 +29,7 @@
#include "layViewObject.h"
#include "layEditable.h"
#include "layPlugin.h"
#if defined (HAVE_QT)
# include <QTimer>
@ -45,7 +46,8 @@ class LAYBASIC_PUBLIC SelectionService :
#if defined (HAVE_QT)
public QObject,
#endif
public lay::ViewService
public lay::ViewService,
public lay::Plugin
{
#if defined (HAVE_QT)
Q_OBJECT
@ -55,6 +57,11 @@ public:
SelectionService (lay::LayoutViewBase *view);
~SelectionService ();
lay::ViewService *view_service_interface ()
{
return this;
}
void set_colors (tl::Color background, tl::Color color);
void begin (const db::DPoint &pos);

View File

@ -191,8 +191,15 @@ ViewObject::freeze ()
// ViewService implementation
ViewService::ViewService (ViewObjectUI *widget)
: mp_widget (widget), m_abs_grab (false), m_enabled (true)
: mp_widget (0), m_abs_grab (false), m_enabled (true)
{
init (widget);
}
void
ViewService::init (ViewObjectUI *widget)
{
mp_widget = widget;
if (widget) {
widget->register_service (this);
}

View File

@ -104,6 +104,11 @@ public:
*/
ViewService (ViewObjectUI *widget = 0);
/**
* @brief Initialization, can follow default constructor
*/
void init (ViewObjectUI *widget);
/**
* @brief Destructor
*/

View File

@ -32,7 +32,7 @@ namespace lay
// ZoomService implementation
ZoomService::ZoomService (lay::LayoutViewBase *view)
: lay::ViewService (view->canvas ()),
: lay::ViewService (view->canvas ()), lay::Plugin (view),
mp_view (view),
mp_box (0),
m_color (0)
@ -282,5 +282,28 @@ ZoomService::begin (const db::DPoint &pos)
ui ()->grab_mouse (this, true);
}
}
// ----------------------------------------------------------------------------
class ZoomServiceDeclaration
: public lay::PluginDeclaration
{
public:
ZoomServiceDeclaration ()
{
// .. nothing yet ..
}
virtual lay::Plugin *create_plugin (db::Manager * /*manager*/, lay::Dispatcher * /*dispatcher*/, lay::LayoutViewBase *view) const
{
return new ZoomService (view);
}
virtual bool enable_catchall_editor_options_pages () const
{
return false;
}
};
static tl::RegisteredClass<lay::PluginDeclaration> zoom_service_decl (new ZoomServiceDeclaration (), -990, "laybasic::ZoomServicePlugin");
}

View File

@ -26,6 +26,7 @@
#define HDR_layZoomBox
#include "layViewObject.h"
#include "layPlugin.h"
namespace lay
{
@ -35,7 +36,7 @@ class LayoutCanvas;
class RubberBox;
class LAYBASIC_PUBLIC ZoomService
: public lay::ViewService
: public lay::ViewService, public lay::Plugin
{
public:
ZoomService (lay::LayoutViewBase *view);
@ -45,6 +46,11 @@ public:
void begin (const db::DPoint &pos);
void begin_pan (const db::DPoint &pos);
lay::ViewService *view_service_interface ()
{
return this;
}
private:
virtual bool mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio);
virtual bool mouse_release_event (const db::DPoint &p, unsigned int buttons, bool prio);

View File

@ -28,13 +28,16 @@ DEFINES += MAKE_LAYBASIC_LIBRARY
SOURCES += \
gsiDeclLayLayers.cc \
gsiDeclLayDispatcher.cc \
gsiDeclLayLayoutViewBase.cc \
gsiDeclLayMarker.cc \
gsiDeclLayMenu.cc \
gsiDeclLayPlugin.cc \
gsiDeclLayTlAdded.cc \
gsiDeclLayRdbAdded.cc \
layAbstractMenu.cc \
layEditorOptionsPage.cc \
layEditorOptionsPages.cc \
layEditorUtils.cc \
layLayoutViewConfig.cc \
layMargin.cc \
laybasicForceLink.cc \
@ -87,6 +90,9 @@ SOURCES += \
layUtils.cc \
HEADERS += \
layEditorOptionsPage.h \
layEditorOptionsPages.h \
layEditorUtils.h \
layMargin.h \
laybasicConfig.h \
laybasicForceLink.h \

View File

@ -390,6 +390,14 @@
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<tabstops>
<tabstop>oversampling</tabstop>
<tabstop>subres_mode</tabstop>
<tabstop>highres_mode</tabstop>
<tabstop>default_font_size</tabstop>
<tabstop>global_trans</tabstop>
<tabstop>def_depth</tabstop>
</tabstops>
<resources>
<include location="../../icons/icons.qrc"/>
</resources>

View File

@ -240,6 +240,15 @@ p, li { white-space: pre-wrap; }
<header>layWidgets.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>lib_cb</tabstop>
<tabstop>le_cell_name</tabstop>
<tabstop>find_next</tabstop>
<tabstop>lv_cells</tabstop>
<tabstop>cb_show_all_cells</tabstop>
<tabstop>ok_button</tabstop>
<tabstop>cancel_button</tabstop>
</tabstops>
<resources>
<include location="../../icons/icons.qrc"/>
</resources>

View File

@ -144,6 +144,11 @@
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>cbx_context</tabstop>
<tabstop>cbx_window</tabstop>
<tabstop>le_max_markers</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

Some files were not shown because too many files have changed in this diff Show More