Enhancement of the "interactive" (infix) modes

1.) Copy & Cut will now take the selection from
    the transient selection if no real selection is present
2.) Hence, Copy & Cut are always enabled
3.) The same if true for duplicate
4.) Move interactive will also act immediately on the transient
    selection.
This commit is contained in:
Matthias Koefferlein 2019-12-05 00:36:46 +01:00
parent 2fa545d80b
commit 717470a389
13 changed files with 189 additions and 58 deletions

View File

@ -901,11 +901,6 @@ Service::drag_cancel ()
delete mp_active_ruler;
mp_active_ruler = 0;
}
if (mp_transient_ruler) {
delete mp_transient_ruler;
mp_transient_ruler = 0;
}
}
int
@ -1030,6 +1025,8 @@ Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::ang
// cancel any pending move or drag operations, reset mp_active_ruler
widget ()->drag_cancel (); // KLUDGE: every service does this to the same service manager
clear_transient_selection ();
// choose move mode
if (mode == lay::Editable::Selected) {
@ -1486,6 +1483,7 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio
// stop dragging
drag_cancel ();
clear_transient_selection ();
// end the transaction
manager ()->commit ();
@ -1535,6 +1533,7 @@ void
Service::deactivated ()
{
drag_cancel ();
clear_transient_selection ();
}
std::pair<bool, db::DPoint>
@ -1576,6 +1575,8 @@ struct RulerIdComp
void
Service::reduce_rulers (int num)
{
clear_transient_selection ();
lay::AnnotationShapes::iterator rfrom = mp_view->annotation_shapes ().begin ();
lay::AnnotationShapes::iterator rto = mp_view->annotation_shapes ().end ();
@ -1921,6 +1922,21 @@ Service::clear_transient_selection ()
}
}
void
Service::transient_to_selection ()
{
if (mp_transient_ruler) {
for (lay::AnnotationShapes::iterator r = mp_view->annotation_shapes ().begin (); r != mp_view->annotation_shapes ().end (); ++r) {
const ant::Object *robj = dynamic_cast <const ant::Object *> (r->ptr ());
if (robj == mp_transient_ruler->ruler ()) {
m_selected.insert (std::make_pair (r, 0));
selection_to_view ();
return;
}
}
}
}
void
Service::clear_previous_selection ()
{

View File

@ -284,6 +284,11 @@ public:
*/
virtual void clear_previous_selection ();
/**
* @brief Turns the transient selection to the selection
*/
virtual void transient_to_selection ();
/**
* @brief Establish a transient selection
*/
@ -548,7 +553,7 @@ private:
bool select (obj_iterator obj, lay::Editable::SelectionMode mode);
/**
* @brief Clear the selection
* @brief Clears the selection
*/
void clear_selection ();

View File

@ -357,6 +357,9 @@ Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::ang
{
if (view ()->is_editable () && mode == lay::Editable::Selected) {
// flush any pending updates of the markers
dm_selection_to_view.execute ();
m_move_start = p;
m_move_trans = db::DTrans ();
m_move_sel = true; // TODO: there is no "false". Remove this.
@ -1187,6 +1190,15 @@ Service::selection_applies (const lay::ObjectInstPath & /*sel*/) const
return false;
}
void
Service::transient_to_selection ()
{
if (! m_transient_selection.empty ()) {
m_selection.insert (m_transient_selection.begin (), m_transient_selection.end ());
selection_to_view ();
}
}
void
Service::clear_previous_selection ()
{

View File

@ -184,11 +184,6 @@ public:
*/
virtual bool select (const db::DBox &box, lay::Editable::SelectionMode mode);
/**
* @brief Clears the previous selection
*/
virtual void clear_previous_selection ();
/**
* @brief Returns true, if the given selected object is handled by this service
*/
@ -269,11 +264,21 @@ public:
*/
bool select (const lay::ObjectInstPath &obj, lay::Editable::SelectionMode mode);
/**
* @brief Clears the previous selection
*/
void clear_previous_selection ();
/**
* @brief Establish a transient selection
*/
bool transient_select (const db::DPoint &pos);
/**
* @brief Turns the transient selection to the selection
*/
virtual void transient_to_selection ();
/**
* @brief Clear the transient selection
*/

View File

@ -1063,6 +1063,15 @@ Service::clear_previous_selection ()
m_previous_selection.clear ();
}
void
Service::transient_to_selection ()
{
if (mp_transient_view) {
m_selected.insert (std::make_pair (mp_transient_view->image_ref (), 0));
selection_to_view ();
}
}
bool
Service::select (obj_iterator obj, lay::Editable::SelectionMode mode)
{

View File

@ -304,6 +304,11 @@ public:
*/
virtual bool transient_select (const db::DPoint &pos);
/**
* @brief Turns the transient selection to the selection
*/
virtual void transient_to_selection ();
/**
* @brief Clear the transient selection
*/

View File

@ -2275,7 +2275,7 @@ MainWindow::do_cm_duplicate (bool interactive)
{
BEGIN_PROTECTED
if (current_view () && current_view ()->has_selection ()) {
if (current_view ()) {
// Do duplicate simply by concatenating copy & paste currently.
// Save the clipboard state before in order to preserve the current content
@ -2327,7 +2327,7 @@ MainWindow::cm_copy ()
{
BEGIN_PROTECTED
if (current_view () && current_view ()->has_selection ()) {
if (current_view ()) {
current_view ()->copy ();
current_view ()->clear_selection ();
}
@ -2370,7 +2370,7 @@ MainWindow::cm_cut ()
{
BEGIN_PROTECTED
if (current_view () && current_view ()->has_selection ()) {
if (current_view ()) {
current_view ()->cut ();
current_view ()->cancel (); // see del() for reason why cancel is after cut
current_view ()->clear_selection ();
@ -2766,21 +2766,6 @@ MainWindow::update_action_states ()
}
if (mp_menu->is_valid ("edit_menu.copy")) {
Action copy_action = mp_menu->action ("edit_menu.copy");
copy_action.set_enabled (current_view () && current_view ()->has_selection () && edits_enabled ());
}
if (mp_menu->is_valid ("edit_menu.duplicate")) {
Action copy_action = mp_menu->action ("edit_menu.duplicate");
copy_action.set_enabled (current_view () && current_view ()->has_selection () && edits_enabled ());
}
if (mp_menu->is_valid ("edit_menu.cut")) {
Action cut_action = mp_menu->action ("edit_menu.cut");
cut_action.set_enabled (current_view () && current_view ()->has_selection () && edits_enabled ());
}
if (mp_menu->is_valid ("edit_menu.paste")) {
Action paste_action = mp_menu->action ("edit_menu.paste");
paste_action.set_enabled (! db::Clipboard::instance ().empty () && edits_enabled ());

View File

@ -821,6 +821,29 @@ Class<lay::LayoutView> decl_LayoutView (QT_EXTERNAL_BASE (QWidget) "lay", "Layou
"selection. Calling this method is useful to ensure there are no potential interactions with the script's "
"functionality.\n"
) +
gsi::method ("clear_selection", &lay::LayoutView::clear_selection,
"@brief Clears the selection of all objects (shapes, annotations, images ...)\n"
"\n"
"This method has been introduced in version 0.26.2\n"
) +
gsi::method ("clear_transient_selection", &lay::LayoutView::clear_transient_selection,
"@brief Clears the transient selection (mouse-over hightlights) of all objects (shapes, annotations, images ...)\n"
"\n"
"This method has been introduced in version 0.26.2\n"
) +
gsi::method ("transient_to_selection", &lay::LayoutView::transient_to_selection,
"@brief Turns the transient selection into the actual selection\n"
"\n"
"The current selection is cleared before. All highlighted objects under the mouse will become selected. "
"This applies to all types of objects (rulers, shapes, images ...).\n"
"\n"
"This method has been introduced in version 0.26.2\n"
) +
gsi::method ("selection_bbox", &lay::LayoutView::selection_bbox,
"@brief Returns the bounding box of the current selection\n"
"\n"
"This method has been introduced in version 0.26.2\n"
) +
gsi::method ("stop", &lay::LayoutView::stop,
"@brief Stops redraw thread and close any browsers\n"
"This method usually does not need to be called explicitly. The redraw thread is stopped automatically."
@ -1077,7 +1100,6 @@ Class<lay::LayoutView> decl_LayoutView (QT_EXTERNAL_BASE (QWidget) "lay", "Layou
) +
gsi::method_ext ("delete_layers", &delete_layers1, gsi::arg ("iterators"),
"@brief Deletes the layer properties nodes specified by the iterator\n"
"@args iterators\n"
"\n"
"This method deletes the nodes specifies by the iterators. This method is the most convenient way to "
"delete multiple entries.\n"
@ -1837,9 +1859,8 @@ static void cv_show_all_cells (lay::CellViewRef *cv)
}
Class<lay::CellViewRef> decl_CellView ("lay", "CellView",
method ("==", static_cast<bool (lay::CellViewRef::*) (const lay::CellViewRef &) const> (&lay::CellViewRef::operator==),
method ("==", static_cast<bool (lay::CellViewRef::*) (const lay::CellViewRef &) const> (&lay::CellViewRef::operator==), gsi::arg ("other"),
"@brief Equality: indicates whether the cellviews refer to the same one\n"
"@args other\n"
"In version 0.25, the definition of the equality operator has been changed to reflect identity of the "
"cellview. Before that version, identity of the cell shown was implied."
) +
@ -1868,32 +1889,28 @@ Class<lay::CellViewRef> decl_CellView ("lay", "CellView",
"@brief Returns true, if the cellview is valid\n"
"A cellview may become invalid if the corresponding tab is closed for example."
) +
method ("path=|set_path", &lay::CellViewRef::set_unspecific_path,
method ("path=|set_path", &lay::CellViewRef::set_unspecific_path, gsi::arg ("path"),
"@brief Sets the unspecific part of the path explicitly\n"
"@args path\n"
"\n"
"Setting the unspecific part of the path will clear the context path component and\n"
"update the context and target cell.\n"
) +
method ("context_path=|set_context_path", &lay::CellViewRef::set_specific_path,
method ("context_path=|set_context_path", &lay::CellViewRef::set_specific_path, gsi::arg ("path"),
"@brief Sets the context path explicitly\n"
"@args path\n"
"\n"
"This method assumes that the unspecific part of the path \n"
"is established already and that the context path starts\n"
"from the context cell.\n"
) +
method ("cell_index=|set_cell", (void (lay::CellViewRef::*) (lay::CellViewRef::cell_index_type)) &lay::CellViewRef::set_cell,
method ("cell_index=|set_cell", (void (lay::CellViewRef::*) (lay::CellViewRef::cell_index_type)) &lay::CellViewRef::set_cell, gsi::arg ("cell_index"),
"@brief Sets the path to the given cell\n"
"@args cell_index\n"
"\n"
"This method will construct any path to this cell, not a \n"
"particular one. It will clear the context path\n"
"and update the context and target cell. Note that the cell is specified by it's index.\n"
) +
method ("cell_name=|set_cell_name", (void (lay::CellViewRef::*) (const std::string &)) &lay::CellViewRef::set_cell,
method ("cell_name=|set_cell_name", (void (lay::CellViewRef::*) (const std::string &)) &lay::CellViewRef::set_cell, gsi::arg ("cell_name"),
"@brief Sets the cell by name\n"
"@args cell_name\n"
"\n"
"If the name is not a valid one, the cellview will become\n"
"invalid.\n"
@ -1901,9 +1918,8 @@ Class<lay::CellViewRef> decl_CellView ("lay", "CellView",
"particular one. It will clear the context path\n"
"and update the context and target cell.\n"
) +
method_ext ("cell=", set_cell,
method_ext ("cell=", set_cell, gsi::arg ("cell"),
"@brief Sets the cell by reference to a Cell object\n"
"@args cell\n"
"Setting the cell reference to nil invalidates the cellview. "
"This method will construct any path to this cell, not a \n"
"particular one. It will clear the context path\n"
@ -1961,9 +1977,8 @@ Class<lay::CellViewRef> decl_CellView ("lay", "CellView",
"@brief Returns the technology name for the layout behind the given cell view\n"
"This method has been added in version 0.23.\n"
) +
method_ext ("technology=", &apply_technology,
method_ext ("technology=", &apply_technology, gsi::arg ("tech_name"),
"@brief Sets the technology for the layout behind the given cell view\n"
"@args tech_name\n"
"According to the specification of the technology, new layer properties may be loaded "
"or the net tracer may be reconfigured. If the layout is shown in multiple views, the "
"technology is updated for all views.\n"
@ -1972,9 +1987,8 @@ Class<lay::CellViewRef> decl_CellView ("lay", "CellView",
method_ext ("layout", &get_layout,
"@brief Gets the reference to the layout object addressed by this view\n"
) +
method_ext ("descend", &cv_descend,
method_ext ("descend", &cv_descend, gsi::arg ("path"),
"@brief Descends further into the hierarchy.\n"
"@args path\n"
"Adds the given path (given as an array of InstElement objects) to the specific path of the "
"cellview with the given index. In effect, the cell addressed by the terminal of the new path "
"components can be shown in the context of the upper cells, if the minimum hierarchy level is "

View File

@ -301,6 +301,22 @@ Editables::clear_transient_selection ()
signal_transient_selection_changed ();
}
void
Editables::transient_to_selection ()
{
cancel_edits ();
for (iterator e = begin (); e != end (); ++e) {
e->select (db::DBox (), lay::Editable::Reset); // clear selection
e->clear_previous_selection ();
e->transient_to_selection ();
e->clear_transient_selection ();
}
// send a signal to the observers
signal_transient_selection_changed ();
signal_selection_changed ();
}
void
Editables::clear_selection ()
{

View File

@ -182,6 +182,14 @@ public:
return false;
}
/**
* @brief Turns the transient selection to the selection
*/
virtual void transient_to_selection ()
{
// .. nothing yet ..
}
/**
* @brief Clear the transient selection
*
@ -418,7 +426,7 @@ public:
* or must be given in micron units.
*/
db::DBox selection_bbox ();
/**
* @brief transform the selection
*
@ -457,6 +465,11 @@ public:
*/
void clear_transient_selection ();
/**
* @brief Turns the transient selection to the selection
*/
void transient_to_selection ();
/**
* @brief Clear the previous selection
*

View File

@ -5154,7 +5154,7 @@ LayoutView::paste_interactive ()
// operations.
trans->close ();
if (mp_move_service->begin_move (trans.release ())) {
if (mp_move_service->begin_move (trans.release (), false)) {
switch_mode (-1); // move mode
}
}
@ -5167,7 +5167,14 @@ LayoutView::copy ()
} else if (mp_control_panel && mp_control_panel->has_focus ()) {
mp_control_panel->copy ();
} else {
if (lay::Editables::selection_size () == 0) {
// try to use the transient selection for the real one
lay::Editables::transient_to_selection ();
}
lay::Editables::copy ();
}
}
@ -5182,8 +5189,15 @@ LayoutView::cut ()
db::Transaction trans (manager (), tl::to_string (QObject::tr ("Cut Layers")));
mp_control_panel->cut ();
} else {
if (lay::Editables::selection_size () == 0) {
// try to use the transient selection for the real one
lay::Editables::transient_to_selection ();
}
db::Transaction trans (manager (), tl::to_string (QObject::tr ("Cut")));
lay::Editables::cut ();
}
}

View File

@ -38,6 +38,7 @@ MoveService::MoveService (lay::LayoutView *view)
: QObject (),
lay::ViewService (view->view_object_widget ()),
m_dragging (false),
m_dragging_transient (false),
mp_editables (view),
mp_view (view),
m_global_grid (0.001)
@ -148,6 +149,9 @@ MoveService::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool p
}
// track mouse position for the infix move initiation
m_mouse_pos = p;
return ret; // not taken to allow the mouse tracker to receive events as well
}
@ -163,7 +167,7 @@ MoveService::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool
return true;
}
if (prio && (buttons & lay::LeftButton) != 0) {
if (handle_dragging (p, buttons, 0)) {
if (handle_dragging (p, buttons, false, 0)) {
return true;
}
}
@ -216,7 +220,7 @@ bool
MoveService::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio)
{
if (prio && (buttons & lay::LeftButton) != 0) {
if (handle_dragging (p, buttons, 0)) {
if (handle_dragging (p, buttons, false, 0)) {
return true;
}
}
@ -230,26 +234,50 @@ MoveService::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool
}
bool
MoveService::begin_move (db::Transaction *transaction)
MoveService::begin_move (db::Transaction *transaction, bool selected_after_move)
{
if (m_dragging) {
return false;
}
std::auto_ptr<db::Transaction> trans_holder (transaction);
drag_cancel ();
bool drag_transient = ! selected_after_move;
if (mp_editables->selection_size () == 0) {
// try to use the transient selection for the real one
mp_editables->transient_to_selection ();
drag_transient = true;
}
if (mp_editables->selection_size () == 0) {
// still nothing selected
return false;
}
db::DBox bbox = mp_editables->selection_bbox ();
if (bbox.empty ()) {
// nothing selected
// nothing (useful) selected
return false;
}
set_cursor (lay::Cursor::size_all);
// emulate a "begin move" at the center of the selection bbox - this will become the reference point
return handle_dragging (bbox.center (), 0, trans_holder.release ());
// emulate a "begin move" at the current mouse position if inside the box or the closest point
// of the box.
db::DPoint pstart = m_mouse_pos;
if (! bbox.contains (pstart)) {
pstart.set_x (std::max (pstart.x (), bbox.p1 ().x ()));
pstart.set_x (std::min (pstart.x (), bbox.p2 ().x ()));
pstart.set_y (std::max (pstart.y (), bbox.p1 ().y ()));
pstart.set_y (std::min (pstart.y (), bbox.p2 ().y ()));
}
return handle_dragging (pstart, 0, drag_transient, trans_holder.release ());
}
bool
MoveService::handle_dragging (const db::DPoint &p, unsigned int buttons, db::Transaction *transaction)
MoveService::handle_dragging (const db::DPoint &p, unsigned int buttons, bool drag_transient, db::Transaction *transaction)
{
std::auto_ptr<db::Transaction> trans_holder (transaction);
@ -267,6 +295,7 @@ MoveService::handle_dragging (const db::DPoint &p, unsigned int buttons, db::Tra
mp_view->clear_transient_selection ();
m_dragging = true;
m_dragging_transient = drag_transient;
widget ()->grab_mouse (this, false);
m_shift = db::DPoint ();
@ -278,8 +307,14 @@ MoveService::handle_dragging (const db::DPoint &p, unsigned int buttons, db::Tra
} else {
m_dragging = false;
widget ()->ungrab_mouse (this);
mp_editables->end_move (p, ac_from_buttons (buttons), mp_transaction.release ());
if (m_dragging_transient) {
mp_editables->clear_selection ();
}
return true;
}

View File

@ -49,7 +49,7 @@ public:
~MoveService ();
virtual bool configure (const std::string &name, const std::string &value);
bool begin_move (db::Transaction *transaction = 0);
bool begin_move (db::Transaction *transaction = 0, bool selected_after_move = true);
private:
virtual bool mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio);
@ -62,13 +62,15 @@ private:
virtual void drag_cancel ();
virtual void deactivated ();
bool handle_dragging (const db::DPoint &p, unsigned int buttons, db::Transaction *transaction);
bool handle_dragging (const db::DPoint &p, unsigned int buttons, bool drag_transient, db::Transaction *transaction);
bool m_dragging;
bool m_dragging_transient;
lay::Editables *mp_editables;
lay::LayoutView *mp_view;
double m_global_grid;
db::DPoint m_shift;
db::DPoint m_mouse_pos;
std::auto_ptr<db::Transaction> mp_transaction;
};