You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

267 lines
8.7 KiB

#include "BaseStrokeHandler.h"
#include <cmath>
#include <memory>
#include "control/Control.h"
#include "control/layer/LayerController.h"
#include "gui/XournalView.h"
#include "gui/XournalppCursor.h"
#include "undo/InsertUndoAction.h"
guint32 BaseStrokeHandler::lastStrokeTime; // persist for next stroke
BaseStrokeHandler::BaseStrokeHandler(XournalView* xournal, XojPageView* redrawable, const PageRef& page, bool flipShift,
bool flipControl):
InputHandler(xournal, redrawable, page) {
this->flipShift = flipShift;
this->flipControl = flipControl;
}
void BaseStrokeHandler::snapToGrid(double& x, double& y) {
if (!xournal->getControl()->getSettings()->isSnapGrid()) {
return;
}
/**
* Snap points to a grid:
* If x/y coordinates are under a certain tolerance,
* fix the point to the grid intersection value
*/
double gridSize = 14.17 / 4.0;
double t = xournal->getControl()->getSettings()->getSnapGridTolerance();
double tolerance = (gridSize / 2) - (1 / t);
double xRem = fmod(x, gridSize);
double yRem = fmod(y, gridSize);
bool snapX = false;
bool snapY = false;
double tmpX = 0;
double tmpY = 0;
if (xRem < tolerance) {
tmpX = x - xRem;
snapX = true;
}
if (xRem > gridSize - tolerance) {
tmpX = x + (gridSize - xRem);
snapX = true;
}
if (yRem < tolerance) {
tmpY = y - yRem;
snapY = true;
}
if (yRem > gridSize - tolerance) {
tmpY = y + (gridSize - yRem);
snapY = true;
}
if (snapX && snapY) {
x = tmpX;
y = tmpY;
}
}
BaseStrokeHandler::~BaseStrokeHandler() = default;
void BaseStrokeHandler::draw(cairo_t* cr) {
double zoom = xournal->getZoom();
int dpiScaleFactor = xournal->getDpiScaleFactor();
cairo_scale(cr, zoom * dpiScaleFactor, zoom * dpiScaleFactor);
view.drawStroke(cr, stroke, 0);
}
auto BaseStrokeHandler::onKeyEvent(GdkEventKey* event) -> bool {
if (event->is_modifier) {
Rectangle<double> rect = stroke->boundingRect();
PositionInputData pos{};
pos.x = pos.y = pos.pressure = 0; // not used in redraw
if (event->keyval == GDK_KEY_Shift_L || event->keyval == GDK_KEY_Shift_R) {
pos.state = static_cast<GdkModifierType>(
event->state ^ GDK_SHIFT_MASK); // event state does not include current this modifier keypress - so
// ^toggle will work for press and release.
} else if (event->keyval == GDK_KEY_Control_L || event->keyval == GDK_KEY_Control_R) {
pos.state = static_cast<GdkModifierType>(event->state ^ GDK_CONTROL_MASK);
} else if (event->keyval == GDK_KEY_Alt_L || event->keyval == GDK_KEY_Alt_R) {
pos.state = static_cast<GdkModifierType>(event->state ^ GDK_MOD1_MASK);
} else {
return false;
}
this->redrawable->repaintRect(stroke->getX(), stroke->getY(), stroke->getElementWidth(),
stroke->getElementHeight());
Point malleablePoint = this->currPoint; // make a copy as it might get snapped to grid.
this->drawShape(malleablePoint, pos);
rect.unite(stroke->boundingRect());
double w = stroke->getWidth();
redrawable->repaintRect(rect.x - w, rect.y - w, rect.width + 2 * w, rect.height + 2 * w);
return true;
}
return false;
}
auto BaseStrokeHandler::onMotionNotifyEvent(const PositionInputData& pos) -> bool {
if (!stroke) {
return false;
}
double zoom = xournal->getZoom();
double x = pos.x / zoom;
double y = pos.y / zoom;
int pointCount = stroke->getPointCount();
Point currentPoint(x, y);
Rectangle<double> rect = stroke->boundingRect();
if (pointCount > 0) {
if (!validMotion(currentPoint, stroke->getPoint(pointCount - 1))) {
return true;
}
}
this->redrawable->repaintRect(stroke->getX(), stroke->getY(), stroke->getElementWidth(),
stroke->getElementHeight());
drawShape(currentPoint, pos);
rect.unite(stroke->boundingRect());
double w = stroke->getWidth();
redrawable->repaintRect(rect.x - w, rect.y - w, rect.width + 2 * w, rect.height + 2 * w);
return true;
}
void BaseStrokeHandler::onButtonReleaseEvent(const PositionInputData& pos) {
xournal->getCursor()->activateDrawDirCursor(false); // in case released within fixate_Dir_Mods_Dist
if (stroke == nullptr) {
return;
}
Control* control = xournal->getControl();
Settings* settings = control->getSettings();
if (settings->getStrokeFilterEnabled()) // Note: For simple strokes see StrokeHandler which has a slightly
// different version of this filter. See //!
{
int strokeFilterIgnoreTime = 0, strokeFilterSuccessiveTime = 0;
double strokeFilterIgnoreLength = NAN;
settings->getStrokeFilter(&strokeFilterIgnoreTime, &strokeFilterIgnoreLength, &strokeFilterSuccessiveTime);
double dpmm = settings->getDisplayDpi() / 25.4;
double zoom = xournal->getZoom();
double lengthSqrd = (pow(((pos.x / zoom) - (this->buttonDownPoint.x)), 2) +
pow(((pos.y / zoom) - (this->buttonDownPoint.y)), 2)) *
pow(xournal->getZoom(), 2);
if (lengthSqrd < pow((strokeFilterIgnoreLength * dpmm), 2) &&
pos.timestamp - this->startStrokeTime < strokeFilterIgnoreTime) {
if (pos.timestamp - BaseStrokeHandler::lastStrokeTime > strokeFilterSuccessiveTime) {
// stroke not being added to layer... delete here.
delete stroke;
stroke = nullptr;
this->userTapped = true;
BaseStrokeHandler::lastStrokeTime = pos.timestamp;
xournal->getCursor()->updateCursor();
return;
}
}
BaseStrokeHandler::lastStrokeTime = pos.timestamp;
}
// This is not a valid stroke
if (stroke->getPointCount() < 2) {
g_warning("Stroke incomplete!");
delete stroke;
stroke = nullptr;
return;
}
stroke->freeUnusedPointItems();
control->getLayerController()->ensureLayerExists(page);
Layer* layer = page->getSelectedLayer();
UndoRedoHandler* undo = control->getUndoRedoHandler();
undo->addUndoAction(std::make_unique<InsertUndoAction>(page, layer, stroke));
layer->addElement(stroke);
page->fireElementChanged(stroke);
stroke = nullptr;
xournal->getCursor()->updateCursor();
}
void BaseStrokeHandler::onButtonPressEvent(const PositionInputData& pos) {
double zoom = xournal->getZoom();
this->buttonDownPoint.x = pos.x / zoom;
this->buttonDownPoint.y = pos.y / zoom;
if (!stroke) {
createStroke(Point(this->buttonDownPoint.x, this->buttonDownPoint.y));
}
this->startStrokeTime = pos.timestamp;
}
void BaseStrokeHandler::onButtonDoublePressEvent(const PositionInputData& pos) {
// nothing to do
}
void BaseStrokeHandler::modifyModifiersByDrawDir(double width, double height, bool changeCursor) {
bool gestureShift = this->flipShift;
bool gestureControl = this->flipControl;
if (this->drawModifierFixed ==
NONE) { // User hasn't dragged out past DrawDirModsRadius i.e. modifier not yet locked.
gestureShift = (width < 0) != gestureShift;
gestureControl = (height < 0) != gestureControl;
this->modShift = this->modShift == !gestureShift;
this->modControl = this->modControl == !gestureControl;
double zoom = xournal->getZoom();
double fixate_Dir_Mods_Dist =
std::pow(xournal->getControl()->getSettings()->getDrawDirModsRadius() / zoom, 2.0);
if (std::pow(width, 2.0) > fixate_Dir_Mods_Dist || std::pow(height, 2.0) > fixate_Dir_Mods_Dist) {
this->drawModifierFixed = static_cast<DIRSET_MODIFIERS>(SET | (gestureShift ? SHIFT : NONE) |
(gestureControl ? CONTROL : NONE));
if (changeCursor) {
xournal->getCursor()->activateDrawDirCursor(false);
}
} else {
if (changeCursor) {
xournal->getCursor()->activateDrawDirCursor(true, this->modShift, this->modControl);
}
}
} else {
gestureShift = gestureShift == !(this->drawModifierFixed & SHIFT);
gestureControl = gestureControl == !(this->drawModifierFixed & CONTROL);
this->modShift = this->modShift == !gestureShift;
this->modControl = this->modControl == !gestureControl;
}
}