diff --git a/src/ant/ant/antService.cc b/src/ant/ant/antService.cc index 24a2397bf..c84694978 100644 --- a/src/ant/ant/antService.cc +++ b/src/ant/ant/antService.cc @@ -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 @@ -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 (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 () { diff --git a/src/ant/ant/antService.h b/src/ant/ant/antService.h index f8e6faaaf..91eb807ea 100644 --- a/src/ant/ant/antService.h +++ b/src/ant/ant/antService.h @@ -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 (); diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index 254f3976c..7d5914bde 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -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 () { diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h index 0ac8246b8..2f0fee85d 100644 --- a/src/edt/edt/edtService.h +++ b/src/edt/edt/edtService.h @@ -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 */ diff --git a/src/img/img/imgService.cc b/src/img/img/imgService.cc index 3b4987c14..15402a987 100644 --- a/src/img/img/imgService.cc +++ b/src/img/img/imgService.cc @@ -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) { diff --git a/src/img/img/imgService.h b/src/img/img/imgService.h index 6d8c070ad..e32af8552 100644 --- a/src/img/img/imgService.h +++ b/src/img/img/imgService.h @@ -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 */ diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 33fcf8237..0a3924964 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -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 ()); diff --git a/src/laybasic/laybasic/gsiDeclLayLayoutView.cc b/src/laybasic/laybasic/gsiDeclLayLayoutView.cc index 2e0b3d44c..9eb7715b1 100644 --- a/src/laybasic/laybasic/gsiDeclLayLayoutView.cc +++ b/src/laybasic/laybasic/gsiDeclLayLayoutView.cc @@ -821,6 +821,29 @@ Class 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 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 decl_CellView ("lay", "CellView", - method ("==", static_cast (&lay::CellViewRef::operator==), + method ("==", static_cast (&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 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 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 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 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 " diff --git a/src/laybasic/laybasic/layEditable.cc b/src/laybasic/laybasic/layEditable.cc index e85c67087..1f9db6a6f 100644 --- a/src/laybasic/laybasic/layEditable.cc +++ b/src/laybasic/laybasic/layEditable.cc @@ -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 () { diff --git a/src/laybasic/laybasic/layEditable.h b/src/laybasic/laybasic/layEditable.h index 6fd58531c..2007bacff 100644 --- a/src/laybasic/laybasic/layEditable.h +++ b/src/laybasic/laybasic/layEditable.h @@ -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 * diff --git a/src/laybasic/laybasic/layLayoutView.cc b/src/laybasic/laybasic/layLayoutView.cc index d669f4da0..f437fd510 100644 --- a/src/laybasic/laybasic/layLayoutView.cc +++ b/src/laybasic/laybasic/layLayoutView.cc @@ -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 (); + } } diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index 8e366293c..7245ded29 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -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 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 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; } diff --git a/src/laybasic/laybasic/layMove.h b/src/laybasic/laybasic/layMove.h index b3bf39b2e..45608a125 100644 --- a/src/laybasic/laybasic/layMove.h +++ b/src/laybasic/laybasic/layMove.h @@ -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 mp_transaction; };