mirror of
https://github.com/KLayout/klayout.git
synced 2026-09-07 02:51:38 +02:00
Initialized repository with current sources.
This commit is contained in:
@@ -0,0 +1,588 @@
|
||||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 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 "layEditable.h"
|
||||
#include "dbClipboard.h"
|
||||
#include "tlAssert.h"
|
||||
|
||||
#include "layPropertiesDialog.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// A helper compare functor
|
||||
|
||||
template <class X, class Y>
|
||||
struct first_of_pair_cmp_f
|
||||
{
|
||||
bool operator() (const std::pair<X,Y> &p1, const std::pair<X,Y> &p2) const
|
||||
{
|
||||
return p1.first < p2.first;
|
||||
}
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Editable implementation
|
||||
|
||||
Editable::Editable (lay::Editables *editables)
|
||||
: mp_editables (editables)
|
||||
{
|
||||
if (editables) {
|
||||
editables->m_editables.push_back (this);
|
||||
}
|
||||
}
|
||||
|
||||
Editable::~Editable ()
|
||||
{
|
||||
// erase the object from the table of enabled services
|
||||
if (mp_editables) {
|
||||
mp_editables->enable (this, false);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Editables implementation
|
||||
|
||||
Editables::Editables (db::Manager *manager)
|
||||
: db::Object (manager), mp_properties_dialog (0), m_move_selection (false), m_any_move_operation (false)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
Editables::~Editables ()
|
||||
{
|
||||
cancel_edits ();
|
||||
}
|
||||
|
||||
void
|
||||
Editables::del ()
|
||||
{
|
||||
if (selection_size () > 0) {
|
||||
|
||||
cancel_edits ();
|
||||
|
||||
// begin the transaction
|
||||
tl_assert (! manager ()->transacting ());
|
||||
manager ()->transaction (tl::to_string (QObject::tr ("Delete")));
|
||||
// this dummy operation will update the screen:
|
||||
manager ()->queue (this, new db::Op ());
|
||||
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
e->del ();
|
||||
}
|
||||
|
||||
// end the transaction
|
||||
manager ()->commit ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Editables::cut ()
|
||||
{
|
||||
if (selection_size () > 0) {
|
||||
|
||||
cancel_edits ();
|
||||
|
||||
// this dummy operation will update the screen:
|
||||
manager ()->queue (this, new db::Op ());
|
||||
|
||||
db::Clipboard::instance ().clear ();
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
e->cut ();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Editables::copy ()
|
||||
{
|
||||
if (selection_size () > 0) {
|
||||
db::Clipboard::instance ().clear ();
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
e->copy ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
db::DBox
|
||||
Editables::selection_bbox ()
|
||||
{
|
||||
db::DBox sel_bbox;
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
sel_bbox += e->selection_bbox ();
|
||||
}
|
||||
return sel_bbox;
|
||||
}
|
||||
|
||||
void
|
||||
Editables::transform (const db::DCplxTrans &tr)
|
||||
{
|
||||
if (selection_size () > 0) {
|
||||
|
||||
try {
|
||||
|
||||
tl_assert (! manager ()->transacting ());
|
||||
manager ()->transaction (tl::to_string (QObject::tr ("Transform")));
|
||||
// this dummy operation will update the screen:
|
||||
manager ()->queue (this, new db::Op ());
|
||||
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
e->transform (tr);
|
||||
}
|
||||
|
||||
// end the transaction
|
||||
manager ()->commit ();
|
||||
|
||||
} catch (...) {
|
||||
manager ()->clear ();
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Editables::paste ()
|
||||
{
|
||||
if (! db::Clipboard::instance ().empty ()) {
|
||||
|
||||
cancel_edits ();
|
||||
|
||||
// this dummy operation will update the screen:
|
||||
if (manager ()->transacting ()) {
|
||||
manager ()->queue (this, new db::Op ());
|
||||
}
|
||||
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
e->paste ();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Editables::enable (lay::Editable *obj, bool en)
|
||||
{
|
||||
if (en) {
|
||||
m_enabled.insert (obj);
|
||||
} else {
|
||||
cancel_edits ();
|
||||
obj->select (db::DBox (), lay::Editable::Reset); // clear selection
|
||||
m_enabled.erase (obj);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Editables::transient_select (const db::DPoint &pt)
|
||||
{
|
||||
bool same_point = (m_last_selected_point.is_point () && m_last_selected_point.center ().sq_double_distance (pt) < 1e-10);
|
||||
if (! same_point) {
|
||||
clear_previous_selection ();
|
||||
}
|
||||
|
||||
m_last_selected_point = db::DBox (pt, pt);
|
||||
|
||||
// in a first pass evaluate the point selection proximity value to select
|
||||
// those plugins that are active. This code is a copy of the code for the single-point selection below.
|
||||
|
||||
std::vector< std::pair <double, iterator> > plugins;
|
||||
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
if (m_enabled.find (&*e) != m_enabled.end ()) {
|
||||
plugins.push_back (std::make_pair (e->click_proximity (pt, lay::Editable::Replace), e));
|
||||
}
|
||||
}
|
||||
|
||||
// sort the plugins found by the proximity
|
||||
std::sort (plugins.begin (), plugins.end (), first_of_pair_cmp_f<double, iterator> ());
|
||||
|
||||
// and call select on those objects until the first one (with the least proximity)
|
||||
// has something selected
|
||||
std::vector< std::pair<double, iterator> >::const_iterator pi = plugins.begin ();
|
||||
for ( ; pi != plugins.end (); ++pi) {
|
||||
if (pi->second->transient_select (pt)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if no plugin has selected anything, try a reset (clear previous selection) and select again: this is supposed
|
||||
// to implement the cycling protocol which allows the plugins to cycle through different
|
||||
// selections for repeated clicks on the same point. Let this happen for replace mode
|
||||
// only because otherwise clearing the selection does not make sense.
|
||||
if (same_point && pi == plugins.end ()) {
|
||||
|
||||
clear_previous_selection ();
|
||||
|
||||
plugins.clear ();
|
||||
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
if (m_enabled.find (&*e) != m_enabled.end ()) {
|
||||
plugins.push_back (std::make_pair (e->click_proximity (pt, lay::Editable::Replace), e));
|
||||
}
|
||||
}
|
||||
|
||||
// sort the plugins found by the proximity
|
||||
std::sort (plugins.begin (), plugins.end (), first_of_pair_cmp_f<double, iterator> ());
|
||||
|
||||
for (pi = plugins.begin (); pi != plugins.end (); ++pi) {
|
||||
if (pi->second->transient_select (pt)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// send a signal to the observers
|
||||
signal_transient_selection_changed ();
|
||||
}
|
||||
|
||||
void
|
||||
Editables::clear_previous_selection ()
|
||||
{
|
||||
m_last_selected_point = db::DBox ();
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
e->clear_previous_selection ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Editables::clear_transient_selection ()
|
||||
{
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
e->clear_transient_selection ();
|
||||
}
|
||||
|
||||
// send a signal to the observers
|
||||
signal_transient_selection_changed ();
|
||||
}
|
||||
|
||||
void
|
||||
Editables::clear_selection ()
|
||||
{
|
||||
cancel_edits ();
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
e->select (db::DBox (), lay::Editable::Reset); // clear selection
|
||||
e->clear_transient_selection ();
|
||||
e->clear_previous_selection ();
|
||||
}
|
||||
|
||||
// send a signal to the observers
|
||||
signal_selection_changed ();
|
||||
}
|
||||
|
||||
void
|
||||
Editables::select ()
|
||||
{
|
||||
cancel_edits ();
|
||||
clear_transient_selection ();
|
||||
clear_previous_selection ();
|
||||
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
if (m_enabled.find (&*e) != m_enabled.end ()) {
|
||||
e->select (db::DBox (), lay::Editable::Replace); // select "all"
|
||||
}
|
||||
}
|
||||
|
||||
// send a signal to the observers
|
||||
signal_selection_changed ();
|
||||
}
|
||||
|
||||
void
|
||||
Editables::select (const db::DBox &box, lay::Editable::SelectionMode mode)
|
||||
{
|
||||
if (box.is_point ()) {
|
||||
select (box.center (), mode);
|
||||
} else {
|
||||
|
||||
cancel_edits ();
|
||||
clear_transient_selection ();
|
||||
clear_previous_selection ();
|
||||
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
if (m_enabled.find (&*e) != m_enabled.end ()) {
|
||||
e->select (box, mode);
|
||||
}
|
||||
}
|
||||
|
||||
// send a signal to the observers
|
||||
signal_selection_changed ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Editables::select (const db::DPoint &pt, lay::Editable::SelectionMode mode)
|
||||
{
|
||||
bool same_point = (m_last_selected_point.is_point () && m_last_selected_point.center ().sq_double_distance (pt) < 1e-10);
|
||||
if (! same_point) {
|
||||
clear_previous_selection ();
|
||||
}
|
||||
|
||||
m_last_selected_point = db::DBox (pt, pt);
|
||||
|
||||
cancel_edits ();
|
||||
clear_transient_selection ();
|
||||
|
||||
// in a first pass evaluate the point selection proximity value to select
|
||||
// those plugins that are active.
|
||||
|
||||
std::vector< std::pair <double, iterator> > plugins;
|
||||
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
if (m_enabled.find (&*e) != m_enabled.end ()) {
|
||||
plugins.push_back (std::make_pair (e->click_proximity (pt, mode), e));
|
||||
}
|
||||
}
|
||||
|
||||
// sort the plugins found by the proximity
|
||||
std::sort (plugins.begin (), plugins.end (), first_of_pair_cmp_f<double, iterator> ());
|
||||
|
||||
// and call select on those objects until the first one (with the least proximity)
|
||||
// has something selected
|
||||
std::vector< std::pair<double, iterator> >::const_iterator pi = plugins.begin ();
|
||||
for ( ; pi != plugins.end (); ++pi) {
|
||||
if (pi->second->select (db::DBox (pt, pt), mode)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if no plugin has selected anything, try a reset (clear_previous selection) and select again: this is supposed
|
||||
// to implement the cycling protocol which allows the plugins to cycle through different
|
||||
// selections for repeated clicks on the same point. Let this happen for replace mode
|
||||
// only because otherwise clearing the selection does not make sense.
|
||||
if (same_point && pi == plugins.end () && mode == lay::Editable::Replace) {
|
||||
|
||||
clear_previous_selection ();
|
||||
|
||||
plugins.clear ();
|
||||
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
if (m_enabled.find (&*e) != m_enabled.end ()) {
|
||||
plugins.push_back (std::make_pair (e->click_proximity (pt, mode), e));
|
||||
}
|
||||
}
|
||||
|
||||
std::sort (plugins.begin (), plugins.end (), first_of_pair_cmp_f<double, iterator> ());
|
||||
|
||||
for (pi = plugins.begin (); pi != plugins.end (); ++pi) {
|
||||
if (pi->second->select (db::DBox (pt, pt), mode)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// in replace mode clear the selections on the following plugins
|
||||
if (mode == lay::Editable::Replace && pi != plugins.end ()) {
|
||||
for (++pi ; pi != plugins.end (); ++pi) {
|
||||
pi->second->select (db::DBox (), lay::Editable::Reset);
|
||||
}
|
||||
}
|
||||
|
||||
// send a signal to the observers
|
||||
signal_selection_changed ();
|
||||
}
|
||||
|
||||
bool
|
||||
Editables::begin_move (const db::DPoint &p, lay::angle_constraint_type ac)
|
||||
{
|
||||
cancel_edits ();
|
||||
clear_previous_selection ();
|
||||
|
||||
m_move_selection = false;
|
||||
m_any_move_operation = false;
|
||||
|
||||
// In a first pass evaluate the point selection proximity value to select
|
||||
// those plugins that are close to the given point. This code is a copy of the code for the single-point selection below.
|
||||
std::vector< std::pair <double, iterator> > plugins;
|
||||
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
if (m_enabled.find (&*e) != m_enabled.end ()) {
|
||||
plugins.push_back (std::make_pair (e->click_proximity (p, lay::Editable::Replace), e));
|
||||
}
|
||||
}
|
||||
|
||||
// sort the plugins found by the proximity
|
||||
std::sort (plugins.begin (), plugins.end (), first_of_pair_cmp_f<double, iterator> ());
|
||||
|
||||
if (selection_size () > 0 && selection_bbox ().contains (p)) {
|
||||
|
||||
// if anything is selected and we are within the selection bbox,
|
||||
// issue a move operation on all editables: first try a Partial mode begin_move
|
||||
for (std::vector< std::pair<double, iterator> >::const_iterator pi = plugins.begin (); pi != plugins.end (); ++pi) {
|
||||
|
||||
if (pi->second->begin_move (Editable::Partial, p, ac)) {
|
||||
|
||||
// clear the selection on all other plugins, because we focus on one plugin
|
||||
for (std::vector< std::pair<double, iterator> >::const_iterator pii = plugins.begin (); pii != plugins.end (); ++pii) {
|
||||
if (pii->second != pi->second) {
|
||||
pii->second->select (db::DBox (), lay::Editable::Reset);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// if something is selected, issue a move operation on all editables
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
e->begin_move (Editable::Selected, p, ac);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
// don't move the selection - clear the existing one first
|
||||
clear_selection ();
|
||||
|
||||
// if nothing is selected, issue a move operation on all editables
|
||||
std::vector< std::pair<double, iterator> >::const_iterator pi = plugins.begin ();
|
||||
// HACK: only the first plugin (measured through click_proximity) has a chance to intercept
|
||||
// the standard procedure which is select + move selected. The reason behind that is that
|
||||
// for example edtService does not implement Any but rather relies on select + Selected mode.
|
||||
// Hence allowing some "later" plugin to override it at this point would break the least
|
||||
// proximity priority rule.
|
||||
if (pi != plugins.end () && pi->second->begin_move (Editable::Any, p, ac)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// nothing particular selected - select ourselves and start over
|
||||
select (p, Editable::Replace);
|
||||
|
||||
// now we assume we have a selection - try to begin_move on this.
|
||||
if (selection_size () > 0) {
|
||||
m_move_selection = true;
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
e->begin_move (Editable::Selected, p, ac);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Editables::move (const db::DPoint &p, lay::angle_constraint_type ac)
|
||||
{
|
||||
m_any_move_operation = true;
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
e->move (p, ac);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Editables::move_transform (const db::DPoint &p, db::DFTrans t, lay::angle_constraint_type ac)
|
||||
{
|
||||
m_any_move_operation = true;
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
e->move_transform (p, t, ac);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Editables::end_move (const db::DPoint &p, lay::angle_constraint_type ac)
|
||||
{
|
||||
if (m_any_move_operation) {
|
||||
|
||||
// begin the transaction
|
||||
tl_assert (! manager ()->transacting ());
|
||||
manager ()->transaction (tl::to_string (QObject::tr ("Move")));
|
||||
// this dummy operation will update the screen:
|
||||
manager ()->queue (this, new db::Op ());
|
||||
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
e->end_move (p, ac);
|
||||
}
|
||||
|
||||
// end the transaction
|
||||
manager ()->commit ();
|
||||
|
||||
// clear the selection that was set previously
|
||||
if (m_move_selection) {
|
||||
clear_selection ();
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// if nothing was moved, treat the end_move as a select which makes the move sticky or
|
||||
// replaces a complex selection by a simple one
|
||||
edit_cancel ();
|
||||
select (p, Editable::Replace);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
Editables::selection_size ()
|
||||
{
|
||||
size_t c = 0;
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
c += e->selection_size ();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
Editables::edit_cancel ()
|
||||
{
|
||||
clear_previous_selection ();
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
e->edit_cancel ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Editables::cancel_edits ()
|
||||
{
|
||||
// close the property dialog
|
||||
if (mp_properties_dialog) {
|
||||
delete mp_properties_dialog;
|
||||
}
|
||||
mp_properties_dialog = 0;
|
||||
|
||||
// cancel any edit operations
|
||||
for (iterator e = begin (); e != end (); ++e) {
|
||||
e->edit_cancel ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Editables::show_properties (QWidget *parent)
|
||||
{
|
||||
cancel_edits ();
|
||||
mp_properties_dialog = new lay::PropertiesDialog (parent, manager (), this);
|
||||
mp_properties_dialog->show ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user