Signature plugin: Element manipulation

This commit is contained in:
Jakub Melka
2022-03-04 19:32:33 +01:00
parent f34f10ebb7
commit fe84cdb3f2
4 changed files with 524 additions and 12 deletions

View File

@ -17,11 +17,14 @@
#include "pdfpagecontentelements.h"
#include "pdfpainterutils.h"
#include "pdfdrawwidget.h"
#include "pdfdrawspacecontroller.h"
#include <QPainter>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QSvgRenderer>
#include <QApplication>
namespace pdf
{
@ -36,6 +39,16 @@ void PDFPageContentElement::setPageIndex(PDFInteger newPageIndex)
m_pageIndex = newPageIndex;
}
PDFInteger PDFPageContentElement::getElementId() const
{
return m_elementId;
}
void PDFPageContentElement::setElementId(PDFInteger newElementId)
{
m_elementId = newElementId;
}
const QPen& PDFPageContentStyledElement::getPen() const
{
return m_pen;
@ -59,6 +72,7 @@ void PDFPageContentStyledElement::setBrush(const QBrush& newBrush)
PDFPageContentElementRectangle* PDFPageContentElementRectangle::clone() const
{
PDFPageContentElementRectangle* copy = new PDFPageContentElementRectangle();
copy->setElementId(getElementId());
copy->setPageIndex(getPageIndex());
copy->setPen(getPen());
copy->setBrush(getBrush());
@ -123,7 +137,9 @@ void PDFPageContentElementRectangle::drawPage(QPainter* painter,
PDFPageContentScene::PDFPageContentScene(QObject* parent) :
QObject(parent),
m_isActive(false)
m_firstFreeId(1),
m_isActive(false),
m_manipulator(this, nullptr)
{
}
@ -135,10 +151,22 @@ PDFPageContentScene::~PDFPageContentScene()
void PDFPageContentScene::addElement(PDFPageContentElement* element)
{
element->setElementId(m_firstFreeId++);
m_elements.emplace_back(element);
emit sceneChanged();
}
PDFPageContentElement* PDFPageContentScene::getElementById(PDFInteger id) const
{
auto it = std::find_if(m_elements.cbegin(), m_elements.cend(), [id](const auto& element) { return element->getElementId() == id; });
if (it != m_elements.cend())
{
return it->get();
}
return nullptr;
}
void PDFPageContentScene::clear()
{
if (!m_elements.empty())
@ -168,32 +196,127 @@ void PDFPageContentScene::keyReleaseEvent(QWidget* widget, QKeyEvent* event)
void PDFPageContentScene::mousePressEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
event->ignore();
if (!isActive())
{
return;
}
MouseEventInfo info = getMouseEventInfo(widget, event->pos());
if (info.isValid() || isMouseGrabbed())
{
// We to handle selecting/deselecting the active
// item. After that, accept the item.
if (info.isValid() && event->button() == Qt::LeftButton)
{
info.widgetMouseStartPos = event->pos();
info.timer.start();
if (!m_manipulator.isManipulationInProgress())
{
Qt::KeyboardModifiers keyboardModifiers = QApplication::keyboardModifiers();
const bool isCtrl = keyboardModifiers.testFlag(Qt::CTRL);
const bool isShift = keyboardModifiers.testFlag(Qt::SHIFT);
if (isCtrl && !isShift)
{
m_manipulator.select(info.hoveredElementIds);
}
else if (!isCtrl && isShift)
{
m_manipulator.deselect(info.hoveredElementIds);
}
else
{
m_manipulator.selectNew(info.hoveredElementIds);
}
}
event->accept();
}
grabMouse(info, event);
}
}
void PDFPageContentScene::mouseDoubleClickEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
event->ignore();
if (!isActive())
{
return;
}
// If mouse is grabbed, then event is accepted always (because
// we get Press event, when we grabbed the mouse, then we will
// wait for corresponding release event while all mouse move events
// will be accepted, even if editor doesn't accept them.
if (isMouseGrabbed())
{
event->accept();
}
}
void PDFPageContentScene::mouseReleaseEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
event->ignore();
if (!isActive())
{
return;
}
if (isMouseGrabbed())
{
if (event->button() == Qt::LeftButton)
{
event->accept();
m_manipulator.finishManipulation();
}
ungrabMouse(info, event);
}
}
void PDFPageContentScene::mouseMoveEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
event->ignore();
if (!isActive())
{
return;
}
MouseEventInfo info = getMouseEventInfo(widget, event->pos());
if (info.isValid())
{
}
// If mouse is grabbed, then event is accepted always (because
// we get Press event, when we grabbed the mouse, then we will
// wait for corresponding release event while all mouse move events
// will be accepted, even if editor doesn't accept them.
if (isMouseGrabbed())
{
event->accept();
}
}
void PDFPageContentScene::wheelEvent(QWidget* widget, QWheelEvent* event)
{
Q_UNUSED(widget);
event->ignore();
if (!isActive())
{
return;
}
// We will accept mouse wheel events, if we are grabbing the mouse.
// We do not want to zoom in/zoom out while grabbing.
if (isMouseGrabbed())
{
event->accept();
}
}
QString PDFPageContentScene::getTooltip() const
@ -234,6 +357,80 @@ void PDFPageContentScene::drawPage(QPainter* painter,
}
}
PDFPageContentScene::MouseEventInfo PDFPageContentScene::getMouseEventInfo(QWidget* widget, QPoint point)
{
MouseEventInfo result;
Q_ASSERT(isActive());
if (isMouseGrabbed())
{
result = m_mouseGrabInfo.info;
result.widgetMouseCurrentPos = point;
return result;
}
PDFWidgetSnapshot snapshot = m_proxy->getSnapshot();
for (const PDFWidgetSnapshot::SnapshotItem& snapshotItem : snapshot.items)
{
}
return result;
}
void PDFPageContentScene::grabMouse(const MouseEventInfo& info, QMouseEvent* event)
{
Q_ASSERT(isActive());
if (event->type() == QEvent::MouseButtonDblClick)
{
// Double clicks doesn't grab the mouse
return;
}
Q_ASSERT(event->type() == QEvent::MouseButtonPress);
if (isMouseGrabbed())
{
// If mouse is already grabbed, then when new mouse button is pressed,
// we just increase nesting level and accept the mouse event. We are
// accepting all mouse events, if mouse is grabbed.
++m_mouseGrabInfo.mouseGrabNesting;
event->accept();
}
else if (event->isAccepted())
{
// Event is accepted and we are not grabbing the mouse. We must start
// grabbing the mouse.
Q_ASSERT(m_mouseGrabInfo.mouseGrabNesting == 0);
++m_mouseGrabInfo.mouseGrabNesting;
m_mouseGrabInfo.info = info;
}
}
void PDFPageContentScene::ungrabMouse(const MouseEventInfo& info, QMouseEvent* event)
{
Q_UNUSED(info);
Q_ASSERT(isActive());
Q_ASSERT(event->type() == QEvent::MouseButtonRelease);
if (isMouseGrabbed())
{
// Mouse is being grabbed, decrease nesting level. We must also accept
// mouse release event, because mouse is being grabbed.
--m_mouseGrabInfo.mouseGrabNesting;
event->accept();
if (!isMouseGrabbed())
{
m_mouseGrabInfo.info = MouseEventInfo();
}
}
Q_ASSERT(m_mouseGrabInfo.mouseGrabNesting >= 0);
}
bool PDFPageContentScene::isActive() const
{
return m_isActive;
@ -244,6 +441,25 @@ void PDFPageContentScene::setActive(bool newIsActive)
if (m_isActive != newIsActive)
{
m_isActive = newIsActive;
if (!newIsActive)
{
m_mouseGrabInfo = MouseGrabInfo();
m_manipulator.reset();
}
emit sceneChanged();
}
}
void PDFPageContentScene::removeElementsById(const std::set<PDFInteger>& selection)
{
const size_t oldSize = m_elements.size();
m_elements.erase(std::remove_if(m_elements.begin(), m_elements.end(), [&selection](const auto& element){ return selection.count(element->getElementId()); }), m_elements.end());
const size_t newSize = m_elements.size();
if (newSize < oldSize)
{
emit sceneChanged();
}
}
@ -251,6 +467,7 @@ void PDFPageContentScene::setActive(bool newIsActive)
PDFPageContentElementLine* PDFPageContentElementLine::clone() const
{
PDFPageContentElementLine* copy = new PDFPageContentElementLine();
copy->setElementId(getElementId());
copy->setPageIndex(getPageIndex());
copy->setPen(getPen());
copy->setBrush(getBrush());
@ -328,6 +545,7 @@ PDFPageContentSvgElement::~PDFPageContentSvgElement()
PDFPageContentSvgElement* PDFPageContentSvgElement::clone() const
{
PDFPageContentSvgElement* copy = new PDFPageContentSvgElement();
copy->setElementId(getElementId());
copy->setPageIndex(getPageIndex());
copy->setRectangle(getRectangle());
copy->setContent(getContent());
@ -400,6 +618,7 @@ void PDFPageContentSvgElement::setRectangle(const QRectF& newRectangle)
PDFPageContentElementDot* PDFPageContentElementDot::clone() const
{
PDFPageContentElementDot* copy = new PDFPageContentElementDot();
copy->setElementId(getElementId());
copy->setPageIndex(getPageIndex());
copy->setPen(getPen());
copy->setBrush(getBrush());
@ -444,6 +663,7 @@ void PDFPageContentElementDot::setPoint(QPointF newPoint)
PDFPageContentElementFreehandCurve* PDFPageContentElementFreehandCurve::clone() const
{
PDFPageContentElementFreehandCurve* copy = new PDFPageContentElementFreehandCurve();
copy->setElementId(getElementId());
copy->setPageIndex(getPageIndex());
copy->setPen(getPen());
copy->setBrush(getBrush());
@ -502,4 +722,186 @@ void PDFPageContentElementFreehandCurve::clear()
m_curve = QPainterPath();
}
PDFPageContentElementManipulator::PDFPageContentElementManipulator(PDFPageContentScene* scene, QObject* parent) :
QObject(parent),
m_scene(scene),
m_isManipulationInProgress(false)
{
}
void PDFPageContentElementManipulator::reset()
{
stopManipulation();
deselectAll();
}
void PDFPageContentElementManipulator::update(PDFInteger id, SelectionModes modes)
{
bool modified = false;
if (modes.testFlag(Clear))
{
modified = !m_selection.empty();
m_selection.clear();
}
// Is id valid?
if (id > 0)
{
if (modes.testFlag(Select))
{
if (!isSelected(id))
{
modified = true;
m_selection.insert(id);
}
}
if (modes.testFlag(Deselect))
{
if (isSelected(id))
{
modified = true;
m_selection.erase(id);
}
}
if (modes.testFlag(Toggle))
{
if (isSelected(id))
{
m_selection.erase(id);
}
else
{
m_selection.insert(id);
}
// When toggle is performed, selection is always changed
modified = true;
}
}
if (modified)
{
emit selectionChanged();
}
}
void PDFPageContentElementManipulator::update(const std::set<PDFInteger>& ids, SelectionModes modes)
{
bool modified = false;
if (modes.testFlag(Clear))
{
modified = !m_selection.empty();
m_selection.clear();
}
// Is id valid?
if (!ids.empty())
{
if (modes.testFlag(Select))
{
for (auto id : ids)
{
if (!isSelected(id))
{
modified = true;
m_selection.insert(id);
}
}
}
if (modes.testFlag(Deselect))
{
for (auto id : ids)
{
if (isSelected(id))
{
modified = true;
m_selection.erase(id);
}
}
}
if (modes.testFlag(Toggle))
{
for (auto id : ids)
{
if (isSelected(id))
{
m_selection.erase(id);
}
else
{
m_selection.insert(id);
}
// When toggle is performed, selection is always changed
modified = true;
}
}
}
if (modified)
{
emit selectionChanged();
}
}
void PDFPageContentElementManipulator::select(PDFInteger id)
{
update(id, Select);
}
void PDFPageContentElementManipulator::select(const std::set<PDFInteger>& ids)
{
update(ids, Select);
}
void PDFPageContentElementManipulator::selectNew(PDFInteger id)
{
update(id, Select | Clear);
}
void PDFPageContentElementManipulator::selectNew(const std::set<PDFInteger>& ids)
{
update(ids, Select | Clear);
}
void PDFPageContentElementManipulator::deselect(PDFInteger id)
{
update(id, Deselect);
}
void PDFPageContentElementManipulator::deselect(const std::set<PDFInteger>& ids)
{
update(ids, Deselect);
}
void PDFPageContentElementManipulator::deselectAll()
{
update(-1, Clear);
}
void PDFPageContentElementManipulator::manipulateDeleteSelection()
{
stopManipulation();
m_scene->removeElementsById(m_selection);
deselectAll();
}
void PDFPageContentElementManipulator::stopManipulation()
{
if (m_isManipulationInProgress)
{
m_isManipulationInProgress = false;
m_manipulatedElements.clear();
m_manipulationModes.clear();
emit stateChanged();
}
}
} // namespace pdf