From 70a4ce82b3af0e1c293b02206e1da2efbaf6978c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 3 Sep 2019 22:53:32 +0200 Subject: [PATCH 1/2] First implementation of infix mode Three mode menu items appear in "Targets for Key Binding" in the setup dialog and can be bound to a key. "Move Interactive" will immediately start moving the selection. "Paste Interactive" and "Duplicate Interactive" will paste and then immediately start moving. Remaining issue: when Paste or Duplicate moves are cancelled the pasted objects will still be there and at the original location. So they are may be hard to see. Also with Undo, two undo items are there: Paste and Move. --- src/lay/lay/layMainConfigPages.cc | 3 +- src/lay/lay/layMainWindow.cc | 92 ++++++++++++++++++++------ src/lay/lay/layMainWindow.h | 27 +++++--- src/laybasic/laybasic/layLayoutView.cc | 40 +++++++++++ src/laybasic/laybasic/layLayoutView.h | 19 ++++++ src/laybasic/laybasic/layMove.cc | 16 +++++ src/laybasic/laybasic/layMove.h | 1 + 7 files changed, 165 insertions(+), 33 deletions(-) diff --git a/src/lay/lay/layMainConfigPages.cc b/src/lay/lay/layMainConfigPages.cc index 5b95b0229..ceb7aabc0 100644 --- a/src/lay/lay/layMainConfigPages.cc +++ b/src/lay/lay/layMainConfigPages.cc @@ -434,9 +434,10 @@ CustomizeMenuConfigPage::apply (const std::vector top_level_menus; top_level_menus.insert (std::make_pair (std::string (), tl::to_string (QObject::tr ("Main Menu")))); + top_level_menus.insert (std::make_pair (std::string ("secrets"), tl::to_string (QObject::tr ("Key Binding Targets")))); top_level_menus.insert (std::make_pair (std::string ("lcp_context_menu"), tl::to_string (QObject::tr ("Layer Panel Context Menu")))); top_level_menus.insert (std::make_pair (std::string ("hcp_context_menu"), tl::to_string (QObject::tr ("Cell List Context Menu")))); - + // fill the bindings list mp_ui->bindings_list->clear (); diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 3ae39ec3d..71a1da23d 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -742,6 +742,13 @@ MainWindow::init_menu () { // default menu layout + MenuLayoutEntry secret_menu [] = { + MenuLayoutEntry ("paste_interactive:edit", tl::to_string (QObject::tr ("Paste Interactive")), SLOT (cm_paste_interactive ())), + MenuLayoutEntry ("duplicate_interactive:edit", tl::to_string (QObject::tr ("Duplicate Interactive")), SLOT (cm_duplicate_interactive ())), + MenuLayoutEntry ("sel_move_interactive", tl::to_string (QObject::tr ("Move Interactive")), SLOT (cm_sel_move_interactive ())), + MenuLayoutEntry::last () + }; + MenuLayoutEntry empty_menu [] = { MenuLayoutEntry::last () }; @@ -995,6 +1002,7 @@ MainWindow::init_menu () MenuLayoutEntry ("macros_menu", tl::to_string (QObject::tr ("&Macros")), macros_menu), MenuLayoutEntry::separator ("help_group"), MenuLayoutEntry ("help_menu", tl::to_string (QObject::tr ("&Help")), help_menu), + MenuLayoutEntry ("@secrets", tl::to_string (QObject::tr ("Secret Features")), secret_menu), MenuLayoutEntry ("@toolbar", "", toolbar_entries), MenuLayoutEntry::last () }; @@ -2233,7 +2241,7 @@ MainWindow::cm_cell_copy () } void -MainWindow::cm_duplicate () +MainWindow::do_cm_duplicate (bool interactive) { BEGIN_PROTECTED @@ -2248,7 +2256,11 @@ MainWindow::cm_duplicate () current_view ()->copy (); current_view ()->clear_selection (); current_view ()->cancel (); - current_view ()->paste (); + if (interactive) { + current_view ()->paste_interactive (); + } else { + current_view ()->paste (); + } db::Clipboard::instance ().swap (saved_clipboard); } catch (...) { db::Clipboard::instance ().swap (saved_clipboard); @@ -2260,6 +2272,26 @@ MainWindow::cm_duplicate () END_PROTECTED } +void +MainWindow::cm_duplicate () +{ + BEGIN_PROTECTED + + do_cm_duplicate (false); + + END_PROTECTED +} + +void +MainWindow::cm_duplicate_interactive () +{ + BEGIN_PROTECTED + + do_cm_duplicate (true); + + END_PROTECTED +} + void MainWindow::cm_copy () { @@ -2274,19 +2306,35 @@ MainWindow::cm_copy () } void -MainWindow::cm_paste () +MainWindow::do_cm_paste (bool interactive) { BEGIN_PROTECTED if (current_view () && ! db::Clipboard::instance ().empty ()) { current_view ()->cancel (); current_view ()->clear_selection (); - current_view ()->paste (); + if (interactive) { + current_view ()->paste_interactive (); + } else { + current_view ()->paste (); + } } END_PROTECTED } +void +MainWindow::cm_paste () +{ + do_cm_paste (false); +} + +void +MainWindow::cm_paste_interactive () +{ + do_cm_paste (true); +} + void MainWindow::cm_cut () { @@ -3568,6 +3616,12 @@ MainWindow::cm_sel_move_to () call_on_current_view (&lay::LayoutView::cm_sel_move_to, tl::to_string (QObject::tr ("move selection to position"))); } +void +MainWindow::cm_sel_move_interactive () +{ + call_on_current_view (&lay::LayoutView::cm_sel_move_interactive, tl::to_string (QObject::tr ("move selection interactively"))); +} + void MainWindow::cm_sel_scale () { @@ -3685,19 +3739,7 @@ MainWindow::clone_current_view () // create a new view view = new lay::LayoutView (current_view (), &m_manager, lay::ApplicationBase::instance ()->is_editable (), plugin_root (), mp_view_stack); - connect (view, SIGNAL (title_changed ()), this, SLOT (view_title_changed ())); - connect (view, SIGNAL (dirty_changed ()), this, SLOT (view_title_changed ())); - connect (view, SIGNAL (edits_enabled_changed ()), this, SLOT (edits_enabled_changed ())); - connect (view, SIGNAL (menu_needs_update ()), this, SLOT (menu_needs_update ())); - connect (view, SIGNAL (show_message (const std::string &, int)), this, SLOT (message (const std::string &, int))); - connect (view, SIGNAL (current_pos_changed (double, double, bool)), this, SLOT (current_pos (double, double, bool))); - connect (view, SIGNAL (clear_current_pos ()), this, SLOT (clear_current_pos ())); - mp_views.push_back (view); - - // we must resize the widget here to set the geometry properly. - // This is required to make zoom_fit work. - view->setGeometry (0, 0, mp_view_stack->width (), mp_view_stack->height ()); - view->show (); + add_view (view); // set initial attributes view->set_hier_levels (curr->get_hier_levels ()); @@ -4291,12 +4333,9 @@ MainWindow::create_layout (const std::string &technology, int mode) return create_or_load_layout (0, 0, technology, mode); } -int -MainWindow::do_create_view () +void +MainWindow::add_view (lay::LayoutView *view) { - // create a new view - lay::LayoutView *view = new lay::LayoutView (&m_manager, lay::ApplicationBase::instance ()->is_editable (), plugin_root (), mp_view_stack); - connect (view, SIGNAL (title_changed ()), this, SLOT (view_title_changed ())); connect (view, SIGNAL (dirty_changed ()), this, SLOT (view_title_changed ())); connect (view, SIGNAL (edits_enabled_changed ()), this, SLOT (edits_enabled_changed ())); @@ -4304,6 +4343,7 @@ MainWindow::do_create_view () connect (view, SIGNAL (show_message (const std::string &, int)), this, SLOT (message (const std::string &, int))); connect (view, SIGNAL (current_pos_changed (double, double, bool)), this, SLOT (current_pos (double, double, bool))); connect (view, SIGNAL (clear_current_pos ()), this, SLOT (clear_current_pos ())); + connect (view, SIGNAL (mode_change (int)), this, SLOT (select_mode (int))); mp_views.push_back (view); @@ -4311,6 +4351,14 @@ MainWindow::do_create_view () // This is required to make zoom_fit work. view->setGeometry (0, 0, mp_view_stack->width (), mp_view_stack->height ()); view->show (); +} + +int +MainWindow::do_create_view () +{ + // create a new view + lay::LayoutView *view = new lay::LayoutView (&m_manager, lay::ApplicationBase::instance ()->is_editable (), plugin_root (), mp_view_stack); + add_view (view); // set initial attributes view->set_synchronous (synchronous ()); diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index de4ef64ec..dc79128c4 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -504,13 +504,6 @@ public: */ void select_view (int index); - /** - * @brief Select the given mode - * - * @param The index of the mode to select - */ - void select_mode (int m); - /** * @brief Get the instance of the assistant */ @@ -645,17 +638,17 @@ signals: public slots: /** - * @brief Display the current position + * @brief Displays the current position */ void current_pos (double x, double y, bool dbu_units); /** - * @brief Clear the current position + * @brief Clears the current position */ void clear_current_pos (); /** - * @brief Display a status message next to the coordinates + * @brief Displays a status message next to the coordinates */ void message (const std::string &s, int ms); @@ -664,6 +657,13 @@ public slots: */ void clear_message (); + /** + * @brief Selects the given mode + * + * @param The index of the mode to select + */ + void select_mode (int m); + /** * @brief Called when one of the built-in modes (i.e. select, move) is selected */ @@ -691,7 +691,9 @@ public slots: void cm_show_properties (); void cm_copy (); void cm_paste (); + void cm_paste_interactive (); void cm_duplicate (); + void cm_duplicate_interactive (); void cm_cut (); void cm_zoom_fit_sel (); void cm_zoom_fit (); @@ -772,6 +774,7 @@ public slots: void cm_sel_scale (); void cm_sel_move (); void cm_sel_move_to (); + void cm_sel_move_interactive (); void cm_show_assistant (); // forwarded to the current view: layer list context menu @@ -946,6 +949,9 @@ private: void closeEvent (QCloseEvent *event); void resizeEvent (QResizeEvent *event); + void do_cm_paste (bool interactive); + void do_cm_duplicate (bool interactive); + void format_message (); int dirty_files (std::string &dirty_files); @@ -956,6 +962,7 @@ private: void current_view_changed (); void update_window_title (); void update_tab_title (int i); + void add_view (LayoutView *view); bool can_close (); lay::CellViewRef create_or_load_layout (const std::string *filename, const db::LoadLayoutOptions *options, const std::string &tech, const int mode); diff --git a/src/laybasic/laybasic/layLayoutView.cc b/src/laybasic/laybasic/layLayoutView.cc index 531a604ca..3f0c2ce78 100644 --- a/src/laybasic/laybasic/layLayoutView.cc +++ b/src/laybasic/laybasic/layLayoutView.cc @@ -5085,6 +5085,29 @@ LayoutView::paste () } } +void +LayoutView::paste_interactive () +{ + clear_selection (); + + { + db::Transaction trans (manager (), tl::to_string (QObject::tr ("Paste"))); + + // let the receivers sort out who is pasting what .. + if (mp_hierarchy_panel) { + mp_hierarchy_panel->paste (); + } + if (mp_control_panel) { + mp_control_panel->paste (); + } + lay::Editables::paste (); + } + + if (mp_move_service->begin_move ()) { + switch_mode (-1); // move mode + } +} + void LayoutView::copy () { @@ -6763,6 +6786,23 @@ LayoutView::cm_sel_scale () } } +void +LayoutView::cm_sel_move_interactive () +{ + if (mp_move_service->begin_move ()) { + switch_mode (-1); // move mode + } +} + +void +LayoutView::switch_mode (int m) +{ + if (m_mode != m) { + mode (m); + emit mode_change (m); + } +} + void LayoutView::cm_sel_move_to () { diff --git a/src/laybasic/laybasic/layLayoutView.h b/src/laybasic/laybasic/layLayoutView.h index d562d74ca..6d9da0bde 100644 --- a/src/laybasic/laybasic/layLayoutView.h +++ b/src/laybasic/laybasic/layLayoutView.h @@ -254,6 +254,11 @@ public: */ void paste (); + /** + * @brief Pastes from clipboard and initiates a move + */ + void paste_interactive (); + /** * @brief Copies to clipboard * @@ -1617,6 +1622,14 @@ public: */ void mode (int m); + /** + * @brief Switches the application's mode + * + * Switches the mode on application level. Use this method to initiate + * a mode switch from the view. + */ + void switch_mode (int m); + /** * @brief Test, if the view is currently in move mode. */ @@ -2582,6 +2595,7 @@ public slots: void cm_sel_scale (); void cm_sel_move (); void cm_sel_move_to (); + void cm_sel_move_interactive (); // forwarded to the layer control panel void cm_new_tab (); @@ -2698,6 +2712,11 @@ signals: */ void menu_needs_update (); + /** + * @brief The view initiated a mode change + */ + void mode_change (int m); + protected: /** * @brief Establish the view operations diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index deb9d5e56..7a6a5e708 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -229,6 +229,22 @@ MoveService::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool return false; } +bool +MoveService::begin_move () +{ + drag_cancel (); + + db::DBox bbox = mp_editables->selection_bbox (); + if (bbox.empty ()) { + // nothing 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); +} bool MoveService::handle_dragging (const db::DPoint &p, unsigned int buttons) diff --git a/src/laybasic/laybasic/layMove.h b/src/laybasic/laybasic/layMove.h index 84ff6c7dd..0c12efaf2 100644 --- a/src/laybasic/laybasic/layMove.h +++ b/src/laybasic/laybasic/layMove.h @@ -46,6 +46,7 @@ public: ~MoveService (); virtual bool configure (const std::string &name, const std::string &value); + bool begin_move (); private: virtual bool mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio); From fa7288502078f195890854003196d78f18c14c72 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 4 Sep 2019 23:47:05 +0200 Subject: [PATCH 2/2] issue #317: provide undo combination for the paste+move sequence in 'interactive paste'. Same for 'interactive dup' --- src/db/db/dbManager.cc | 16 +++++ src/db/db/dbManager.h | 47 ++++++++++++++- src/db/unit_tests/dbObject.cc | 83 ++++++++++++++++++++++++++ src/laybasic/laybasic/layEditable.cc | 57 ++++++++++-------- src/laybasic/laybasic/layEditable.h | 16 ++++- src/laybasic/laybasic/layLayoutView.cc | 10 +++- src/laybasic/laybasic/layMove.cc | 26 ++++++-- src/laybasic/laybasic/layMove.h | 8 ++- 8 files changed, 220 insertions(+), 43 deletions(-) diff --git a/src/db/db/dbManager.cc b/src/db/db/dbManager.cc index 51dec8ab9..b7f842394 100644 --- a/src/db/db/dbManager.cc +++ b/src/db/db/dbManager.cc @@ -143,6 +143,22 @@ Manager::last_transaction_id () const return m_transactions.empty () ? 0 : reinterpret_cast (& m_transactions.back ()); } +void +Manager::cancel () +{ + if (db::transactions_enabled ()) { + + // commit and undo - revert changes done so far + commit (); + undo (); + + // delete all following transactions + erase_transactions (m_current, m_transactions.end ()); + m_current = m_transactions.end (); + + } +} + void Manager::commit () { diff --git a/src/db/db/dbManager.h b/src/db/db/dbManager.h index 014fb49c4..5dfa2fb02 100644 --- a/src/db/db/dbManager.h +++ b/src/db/db/dbManager.h @@ -140,6 +140,14 @@ public: */ void commit (); + /** + * @brief Cancels a transaction + * + * If called instead of commit, this method will undo all operations of the pending + * transaction. + */ + void cancel (); + /** * @brief Undo the current transaction * @@ -256,13 +264,20 @@ private: * * This object controls a transaction through it's lifetime. On construction, the * transaction is started, on destruction, the transaction is committed. + * + * "cancel" can be used to cancel the operation. This will undo all operations collected + * so far and delete the transaction. + * + * "close" temporarily disable the collection of operations. + * "open" will enable operation collection again and continue + * collection at the point when it was stopped with "close". */ class DB_PUBLIC Transaction { public: Transaction (db::Manager *manager, const std::string &desc) - : mp_manager (manager), m_transaction_id (0) + : mp_manager (manager), m_transaction_id (0), m_description (desc) { if (mp_manager) { m_transaction_id = mp_manager->transaction (desc); @@ -270,7 +285,7 @@ public: } Transaction (db::Manager *manager, const std::string &desc, db::Manager::transaction_id_t join_with) - : mp_manager (manager), m_transaction_id (0) + : mp_manager (manager), m_transaction_id (0), m_description (desc) { if (mp_manager) { m_transaction_id = mp_manager->transaction (desc, join_with); @@ -280,11 +295,36 @@ public: ~Transaction () { if (mp_manager) { - mp_manager->commit (); + if (mp_manager->transacting ()) { + mp_manager->commit (); + } mp_manager = 0; } } + void cancel () + { + if (mp_manager) { + open (); + mp_manager->cancel (); + mp_manager = 0; + } + } + + void close () + { + if (mp_manager->transacting ()) { + mp_manager->commit (); + } + } + + void open () + { + if (! mp_manager->transacting ()) { + mp_manager->transaction (m_description, m_transaction_id); + } + } + db::Manager::transaction_id_t id () const { return m_transaction_id; @@ -293,6 +333,7 @@ public: private: db::Manager *mp_manager; db::Manager::transaction_id_t m_transaction_id; + std::string m_description; // no copying. Transaction (const Transaction &); diff --git a/src/db/unit_tests/dbObject.cc b/src/db/unit_tests/dbObject.cc index 036bee571..aa7c434ed 100644 --- a/src/db/unit_tests/dbObject.cc +++ b/src/db/unit_tests/dbObject.cc @@ -151,6 +151,8 @@ struct B : public db::Object { if (transacting ()) { manager ()->queue (this, new BO (d)); + } else { + x += d; } } @@ -230,3 +232,84 @@ TEST(2) EXPECT_EQ (BO::inst_count (), 0); } +TEST(3) +{ + db::Manager *man = new db::Manager (); + { + EXPECT_EQ (man->available_undo ().first, false); + EXPECT_EQ (man->available_redo ().first, false); + + B b (man); + man->transaction ("add 1"); + b.add (1); + man->commit (); + + EXPECT_EQ (b.x, 1); + EXPECT_EQ (man->available_undo ().first, true); + EXPECT_EQ (man->available_undo ().second, "add 1"); + + man->transaction ("add 1,2"); + b.add (1); + b.add (2); + man->cancel (); + EXPECT_EQ (b.x, 1); + EXPECT_EQ (man->available_undo ().first, true); + EXPECT_EQ (man->available_redo ().first, false); + + man->undo (); + EXPECT_EQ (b.x, 0); + EXPECT_EQ (man->available_undo ().first, false); + EXPECT_EQ (man->available_redo ().first, true); + } + + delete man; + EXPECT_EQ (BO::inst_count (), 0); +} + +TEST(4) +{ + db::Manager *man = new db::Manager (); + { + EXPECT_EQ (man->available_undo ().first, false); + EXPECT_EQ (man->available_redo ().first, false); + + B b (man); + { + db::Transaction t (man, "add 1"); + b.add (1); + } + + EXPECT_EQ (b.x, 1); + EXPECT_EQ (man->available_undo ().first, true); + EXPECT_EQ (man->available_undo ().second, "add 1"); + + { + db::Transaction t (man, "add 1,2"); + b.add (1); + EXPECT_EQ (b.x, 2); + EXPECT_EQ (man->transacting (), true); + t.close (); + EXPECT_EQ (man->transacting (), false); + b.add (1); // after close -> not undone! + EXPECT_EQ (b.x, 3); + t.open (); + EXPECT_EQ (man->transacting (), true); + b.add (2); + EXPECT_EQ (b.x, 5); + t.cancel (); + } + + EXPECT_EQ (b.x, 2); + EXPECT_EQ (man->available_undo ().first, true); + EXPECT_EQ (man->available_redo ().first, false); + + man->undo (); + EXPECT_EQ (b.x, 1); + EXPECT_EQ (man->available_undo ().first, false); + EXPECT_EQ (man->available_redo ().first, true); + } + + delete man; + EXPECT_EQ (BO::inst_count (), 0); +} + diff --git a/src/laybasic/laybasic/layEditable.cc b/src/laybasic/laybasic/layEditable.cc index d2dff0e4f..e85c67087 100644 --- a/src/laybasic/laybasic/layEditable.cc +++ b/src/laybasic/laybasic/layEditable.cc @@ -28,6 +28,7 @@ #include "layPropertiesDialog.h" #include +#include namespace lay { @@ -82,25 +83,30 @@ Editables::~Editables () } void -Editables::del () +Editables::del (db::Transaction *transaction) { + std::auto_ptr trans_holder (transaction ? transaction : new db::Transaction (manager (), tl::to_string (QObject::tr ("Delete")))); + if (selection_size () > 0) { - cancel_edits (); + try { - // 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 ()); + trans_holder->open (); - for (iterator e = begin (); e != end (); ++e) { - e->del (); + cancel_edits (); + + // this dummy operation will update the screen: + manager ()->queue (this, new db::Op ()); + + for (iterator e = begin (); e != end (); ++e) { + e->del (); + } + + } catch (...) { + trans_holder->cancel (); + throw; } - // end the transaction - manager ()->commit (); - } } @@ -155,14 +161,16 @@ Editables::selection_catch_bbox () } void -Editables::transform (const db::DCplxTrans &tr) +Editables::transform (const db::DCplxTrans &tr, db::Transaction *transaction) { + std::auto_ptr trans_holder (transaction ? transaction : new db::Transaction (manager (), tl::to_string (QObject::tr ("Transform")))); + if (selection_size () > 0) { try { - tl_assert (! manager ()->transacting ()); - manager ()->transaction (tl::to_string (QObject::tr ("Transform"))); + trans_holder->open (); + // this dummy operation will update the screen: manager ()->queue (this, new db::Op ()); @@ -170,11 +178,8 @@ Editables::transform (const db::DCplxTrans &tr) e->transform (tr); } - // end the transaction - manager ()->commit (); - } catch (...) { - manager ()->clear (); + trans_holder->cancel (); throw; } @@ -525,13 +530,14 @@ Editables::move_transform (const db::DPoint &p, db::DFTrans t, lay::angle_constr } void -Editables::end_move (const db::DPoint &p, lay::angle_constraint_type ac) +Editables::end_move (const db::DPoint &p, lay::angle_constraint_type ac, db::Transaction *transaction) { + std::auto_ptr trans_holder (transaction ? transaction : new db::Transaction (manager (), tl::to_string (QObject::tr ("Move")))); + if (m_any_move_operation) { - // begin the transaction - tl_assert (! manager ()->transacting ()); - manager ()->transaction (tl::to_string (QObject::tr ("Move"))); + trans_holder->open (); + // this dummy operation will update the screen: manager ()->queue (this, new db::Op ()); @@ -539,9 +545,6 @@ Editables::end_move (const db::DPoint &p, lay::angle_constraint_type ac) e->end_move (p, ac); } - // end the transaction - manager ()->commit (); - // clear the selection that was set previously if (m_move_selection) { clear_selection (); @@ -549,6 +552,8 @@ Editables::end_move (const db::DPoint &p, lay::angle_constraint_type ac) } else { + trans_holder->cancel (); + // 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 (); diff --git a/src/laybasic/laybasic/layEditable.h b/src/laybasic/laybasic/layEditable.h index 6b48aa169..18d184521 100644 --- a/src/laybasic/laybasic/layEditable.h +++ b/src/laybasic/laybasic/layEditable.h @@ -32,6 +32,7 @@ #include "dbPoint.h" #include "dbBox.h" #include "dbObject.h" +#include "dbManager.h" #include #include @@ -386,8 +387,11 @@ public: /** * @brief The delete operation + * + * If a transaction is given, the operation will be appended to this pending transaction + * The Editables object takes ownership over the Transaction object. */ - void del (); + void del (db::Transaction *transaction = 0); /** * @brief "cut" operation @@ -419,8 +423,11 @@ public: * @brief transform the selection * * The transformation is given in micron units. + * + * If a transaction is given, the operation will be appended to this pending transaction. + * The Editables object takes ownership over the Transaction object. */ - void transform (const db::DCplxTrans &tr); + void transform (const db::DCplxTrans &tr, db::Transaction *transaction = 0); /** * @brief Enable or disable a certain editable @@ -494,8 +501,11 @@ public: /** * @brief End "move" operation + * + * If a transaction is given, the operation will be appended to this pending transaction + * The Editables object takes ownership over the Transaction object. */ - void end_move (const db::DPoint &p, lay::angle_constraint_type ac); + void end_move (const db::DPoint &p, lay::angle_constraint_type ac, db::Transaction *transaction = 0); /** * @brief Tell how many objects are selected. diff --git a/src/laybasic/laybasic/layLayoutView.cc b/src/laybasic/laybasic/layLayoutView.cc index 3f0c2ce78..31e7709a8 100644 --- a/src/laybasic/laybasic/layLayoutView.cc +++ b/src/laybasic/laybasic/layLayoutView.cc @@ -5090,9 +5090,9 @@ LayoutView::paste_interactive () { clear_selection (); - { - db::Transaction trans (manager (), tl::to_string (QObject::tr ("Paste"))); + std::auto_ptr trans (new db::Transaction (manager (), tl::to_string (QObject::tr ("Paste and move")))); + { // let the receivers sort out who is pasting what .. if (mp_hierarchy_panel) { mp_hierarchy_panel->paste (); @@ -5103,7 +5103,11 @@ LayoutView::paste_interactive () lay::Editables::paste (); } - if (mp_move_service->begin_move ()) { + // temporarily close the transaction and pass to the move service for appending it's own + // operations. + trans->close (); + + if (mp_move_service->begin_move (trans.release ())) { switch_mode (-1); // move mode } } diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index 7a6a5e708..8e366293c 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -163,7 +163,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)) { + if (handle_dragging (p, buttons, 0)) { return true; } } @@ -216,7 +216,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)) { + if (handle_dragging (p, buttons, 0)) { return true; } } @@ -230,8 +230,10 @@ MoveService::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool } bool -MoveService::begin_move () +MoveService::begin_move (db::Transaction *transaction) { + std::auto_ptr trans_holder (transaction); + drag_cancel (); db::DBox bbox = mp_editables->selection_bbox (); @@ -243,14 +245,18 @@ MoveService::begin_move () 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); + return handle_dragging (bbox.center (), 0, trans_holder.release ()); } bool -MoveService::handle_dragging (const db::DPoint &p, unsigned int buttons) +MoveService::handle_dragging (const db::DPoint &p, unsigned int buttons, db::Transaction *transaction) { + std::auto_ptr trans_holder (transaction); + if (! m_dragging) { + mp_transaction.reset (trans_holder.release ()); + if (mp_editables->begin_move (p, ac_from_buttons (buttons))) { lay::SelectionService *selector = mp_view->selection_service (); @@ -273,7 +279,7 @@ MoveService::handle_dragging (const db::DPoint &p, unsigned int buttons) m_dragging = false; widget ()->ungrab_mouse (this); - mp_editables->end_move (p, ac_from_buttons (buttons)); + mp_editables->end_move (p, ac_from_buttons (buttons), mp_transaction.release ()); return true; } @@ -285,9 +291,17 @@ MoveService::drag_cancel () { m_shift = db::DPoint (); if (m_dragging) { + mp_editables->edit_cancel (); widget ()->ungrab_mouse (this); + m_dragging = false; + + if (mp_transaction.get ()) { + mp_transaction->cancel (); + } + mp_transaction.reset (0); + } } diff --git a/src/laybasic/laybasic/layMove.h b/src/laybasic/laybasic/layMove.h index 0c12efaf2..b3bf39b2e 100644 --- a/src/laybasic/laybasic/layMove.h +++ b/src/laybasic/laybasic/layMove.h @@ -25,11 +25,14 @@ #ifndef HDR_layMove #define HDR_layMove +#include "dbManager.h" #include "layViewObject.h" #include #include +#include + namespace lay { class Editables; @@ -46,7 +49,7 @@ public: ~MoveService (); virtual bool configure (const std::string &name, const std::string &value); - bool begin_move (); + bool begin_move (db::Transaction *transaction = 0); private: virtual bool mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio); @@ -59,13 +62,14 @@ private: virtual void drag_cancel (); virtual void deactivated (); - bool handle_dragging (const db::DPoint &p, unsigned int buttons); + bool handle_dragging (const db::DPoint &p, unsigned int buttons, db::Transaction *transaction); bool m_dragging; lay::Editables *mp_editables; lay::LayoutView *mp_view; double m_global_grid; db::DPoint m_shift; + std::auto_ptr mp_transaction; }; }