Files
qelectrotech-source-mirror/sources/diagramevent/diagrameventaddpaste.h
T
ispyisail dd0c194a3c Paste under the cursor, and give the context menu back (#913)
Two fixes in the placement tool Ctrl+V starts.

Paste appeared on top of the original, not under the cursor. 55c2c0df9
added the placement tool precisely so the copy would not land invisibly
on top of what was copied, but 53a0f07ca then warped the pointer to the
group's grid-snapped origin -- which is the original's position -- so the
copy reappeared exactly over the original until the mouse was moved. The
start_pos the caller computes from the cursor was left unread: three
mentions in the file, all declaration or comment. Reported on #913,
where it reads as Ctrl+V pasting in place.

Move the group to the cursor instead of the cursor to the group. Both put
the copy under the pointer; only one of them takes the pointer away from
where the user put it. start_pos is honoured, the items are translated
once at construction, their conductors re-routed before anything is
drawn, and the movement baseline is set there too rather than waiting for
the first mouse move.

That made the baseline sentinel matter, so it is now the
m_baseline_captured flag the header already declared and nothing used,
rather than m_initial_cursor.isNull() -- which cannot tell "not set yet"
from a baseline that is legitimately scene (0,0).

Separately: one Ctrl+V killed the folio's right-click menu for the rest
of the session. init() sets Qt::NoContextMenu so a right click cancels
the placement instead of opening a menu over it, and nothing ever set it
back, taking "Coller ici", "Collage multiple", the folio properties and
add/remove column/row with it. Every other DiagramEvent* class restores
the policy in its destructor; this one did not. Confirmed against an
unmodified master build: the menu opens before a paste and not after one.
It matters here because "use the right-click menu instead" is the answer
people are given when Ctrl+V does not place where they wanted.

Verified on a built binary driven through a virtual X display, against
examples/741.qet and convertisseur.qet:

- the pointer does not move across Ctrl+V (1300,870 before and after;
  on master it jumps to the original at 798,455), and the copy is under it
- a multi-element selection keeps its layout and its conductors re-route
- Escape and right-click both cancel, leaving nothing behind and nothing
  on the undo stack; click and Return both commit; one undo removes the
  whole paste and redo restores it
- with the pointer outside the viewport the copy lands at the viewport
  centre, visible, and follows correctly once the mouse enters
- pasting into a different folio from the one copied from works
- the context menu opens before a paste, after a cancelled one, and after
  a committed one, with "Coller ici" present

ctest 9/9.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 19:57:59 +12:00

100 lines
3.6 KiB
C++

/*
Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech 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.
QElectroTech 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 QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DIAGRAMEVENTADDPASTE_H
#define DIAGRAMEVENTADDPASTE_H
#include "diagrameventinterface.h"
#include "../diagramcontent.h"
#include <QHash>
#include <QPointer>
class QStatusBar;
/**
@brief The DiagramEventAddPaste class
Paste the clipboard as a placement you can still move.
The pasted items are added straight away and follow the cursor until a
left click drops them; Escape or a right click takes them away again.
This is the same interaction as placing a new element, so a paste behaves
like every other "put something on the folio" action.
The items are the real ones from the start, not a preview: Diagram::fromXml
creates them, this class moves them, and PasteDiagramCommand is pushed only
once they are dropped. That keeps one copy of the paste logic rather than
two, and means a cancelled paste leaves nothing on the undo stack.
*/
class DiagramEventAddPaste : public DiagramEventInterface
{
Q_OBJECT
public:
///Items with a position of their own. Conductors are left out
///deliberately: they are drawn from their terminals, so they
///follow when the elements they attach to move.
static const int MovableItems =
DiagramContent::Elements
| DiagramContent::TextFields
| DiagramContent::Images
| DiagramContent::Shapes
| DiagramContent::Tables
| DiagramContent::TerminalStrip;
DiagramEventAddPaste(Diagram *diagram, const QPointF &start_pos);
~DiagramEventAddPaste() override;
void mouseMoveEvent (QGraphicsSceneMouseEvent *event) override;
void mousePressEvent (QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent (QGraphicsSceneMouseEvent *event) override;
void keyPressEvent (QKeyEvent *event) override;
void init() override;
///@return true if the clipboard holds something this can paste.
static bool clipboardHasDiagram();
private:
void moveTo(const QPointF &scene_pos);
void commit();
void cancel();
void showHint();
void removeItems();
DiagramContent m_content;
///Each movable item's position relative to the group's top left,
///taken once so repeated moves cannot accumulate rounding drift.
QHash<QGraphicsItem *, QPointF> m_relative_pos;
///Where the group's grid-snapped top left was put when the paste
///started, in scene coordinates -- the cursor, so the copy
///appears under the pointer rather than on top of what was
///copied.
QPointF m_group_origin;
///Cursor position (scene coords) the delta-based movement in
///moveTo() measures from. Equal to m_group_origin, since the
///group is placed at the cursor.
QPointF m_initial_cursor;
///Whether m_initial_cursor holds a usable baseline. A flag rather
///than testing m_initial_cursor.isNull(), which cannot tell "not
///set yet" from a baseline that is legitimately scene (0,0).
bool m_baseline_captured{false};
QPointer<QStatusBar> m_status_bar;
bool m_finished{false};
};
#endif // DIAGRAMEVENTADDPASTE_H