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.
 
 
 
 
 
 

802 lines
18 KiB

#include "EditSelection.h"
#include "EditSelectionContents.h"
#include "Selection.h"
#include "control/Control.h"
#include "gui/pageposition/PagePositionHandler.h"
#include "gui/PageView.h"
#include "gui/XournalView.h"
#include "model/Document.h"
#include "model/Layer.h"
#include "model/Element.h"
#include "model/Stroke.h"
#include "model/Text.h"
#include "undo/ColorUndoAction.h"
#include "undo/FontUndoAction.h"
#include "undo/SizeUndoAction.h"
#include "undo/UndoRedoHandler.h"
#include "util/GtkColorWrapper.h"
#include "view/DocumentView.h"
#include <serializing/ObjectOutputStream.h>
#include <serializing/ObjectInputStream.h>
#include <iostream>
#include <math.h>
using std::cout;
using std::endl;
EditSelection::EditSelection(UndoRedoHandler* undo, PageRef page, XojPageView* view)
{
XOJ_INIT_TYPE(EditSelection);
this->x = 0;
this->y = 0;
this->rotation = 0;
this->width = 0;
this->height = 0;
contstruct(undo, view, page);
}
EditSelection::EditSelection(UndoRedoHandler* undo, Selection* selection, XojPageView* view)
{
XOJ_INIT_TYPE(EditSelection);
selection->getSelectedRect(this->x, this->y, this->width, this->height);
contstruct(undo, view, view->getPage());
for (Element* e : selection->selectedElements)
{
this->sourceLayer->removeElement(e, false);
addElement(e);
}
view->rerenderPage();
}
EditSelection::EditSelection(UndoRedoHandler* undo, Element* e, XojPageView* view, PageRef page)
{
XOJ_INIT_TYPE(EditSelection);
this->x = e->getX();
this->y = e->getY();
this->width = e->getElementWidth();
this->height = e->getElementHeight();
contstruct(undo, view, page);
addElement(e);
this->sourceLayer->removeElement(e, false);
view->rerenderElement(e);
}
/**
* Our internal constructor
*/
void EditSelection::contstruct(UndoRedoHandler* undo, XojPageView* view, PageRef sourcePage)
{
XOJ_CHECK_TYPE(EditSelection);
this->view = view;
this->undo = undo;
this->sourcePage = sourcePage;
this->sourceLayer = this->sourcePage->getSelectedLayer();
this->aspectRatio = false;
this->relMousePosX = 0;
this->relMousePosY = 0;
this->mouseDownType = CURSOR_SELECTION_NONE;
this->contents = new EditSelectionContents(this->x, this->y, this->width, this->height,
this->sourcePage, this->sourceLayer, this->view);
}
EditSelection::~EditSelection()
{
XOJ_CHECK_TYPE(EditSelection);
finalizeSelection();
this->sourcePage = NULL;
this->sourceLayer = NULL;
delete this->contents;
this->contents = NULL;
this->view = NULL;
this->undo = NULL;
XOJ_RELEASE_TYPE(EditSelection);
}
/**
* Finishes all pending changes, move the elements, scale the elements and add
* them to new layer if any or to the old if no new layer
*/
void EditSelection::finalizeSelection()
{
XOJ_CHECK_TYPE(EditSelection);
XojPageView* v = getBestMatchingPageView();
if (v == NULL)
{
this->view->getXournal()->deleteSelection(this);
}
else
{
this->view = v;
PageRef page = this->view->getPage();
Layer* layer = page->getSelectedLayer();
this->contents->finalizeSelection(this->x, this->y, this->width, this->height,
this->aspectRatio, layer, page, this->view, this->undo);
this->view->rerenderRect(this->x, this->y, this->width, this->height);
// This is needed if the selection not was 100% on a page
this->view->getXournal()->repaintSelection(true);
}
}
/**
* get the X coordinate relative to the provided view (getView())
* in document coordinates
*/
double EditSelection::getXOnView()
{
XOJ_CHECK_TYPE(EditSelection);
return this->x;
}
/**
* get the Y coordinate relative to the provided view (getView())
* in document coordinates
*/
double EditSelection::getYOnView()
{
XOJ_CHECK_TYPE(EditSelection);
return this->y;
}
/**
* get the width in document coordinates (multiple with zoom)
*/
double EditSelection::getWidth()
{
XOJ_CHECK_TYPE(EditSelection);
return this->width;
}
/**
* get the height in document coordinates (multiple with zoom)
*/
double EditSelection::getHeight()
{
XOJ_CHECK_TYPE(EditSelection);
return this->height;
}
/**
* get the source page (where the selection was done)
*/
PageRef EditSelection::getSourcePage()
{
XOJ_CHECK_TYPE(EditSelection);
return this->sourcePage;
}
/**
* Get the X coordinate in View coordinates (absolute)
*/
int EditSelection::getXOnViewAbsolute()
{
XOJ_CHECK_TYPE(EditSelection);
double zoom = view->getXournal()->getZoom();
return this->view->getX() + this->getXOnView() * zoom;
}
/**
* Get the Y coordinate in View coordinates (absolute)
*/
int EditSelection::getYOnViewAbsolute()
{
XOJ_CHECK_TYPE(EditSelection);
double zoom = view->getXournal()->getZoom();
return this->view->getY() + this->getYOnView() * zoom;
}
/**
* Get the width in View coordinates
*/
int EditSelection::getViewWidth()
{
XOJ_CHECK_TYPE(EditSelection);
double zoom = view->getXournal()->getZoom();
return this->width * zoom;
}
/**
* Get the height in View coordinates
*/
int EditSelection::getViewHeight()
{
XOJ_CHECK_TYPE(EditSelection);
double zoom = view->getXournal()->getZoom();
return this->height * zoom;
}
/**
* Sets the tool size for pen or eraser, returs an undo action
* (or NULL if nothing is done)
*/
UndoAction* EditSelection::setSize(ToolSize size,
const double* thicknessPen,
const double* thicknessHilighter,
const double* thicknessEraser)
{
XOJ_CHECK_TYPE(EditSelection);
return this->contents->setSize(size, thicknessPen, thicknessHilighter, thicknessEraser);
}
/**
* Set the color of all elements, return an undo action
* (Or NULL if nothing done, e.g. because there is only an image)
*/
UndoAction* EditSelection::setColor(int color)
{
XOJ_CHECK_TYPE(EditSelection);
return this->contents->setColor(color);
}
/**
* Sets the font of all containing text elements, return an undo action
* (or NULL if there are no Text elements)
*/
UndoAction* EditSelection::setFont(XojFont& font)
{
XOJ_CHECK_TYPE(EditSelection);
return this->contents->setFont(font);
}
/**
* Fills de undo item if the selection is deleted
* the selection is cleared after
*/
void EditSelection::fillUndoItem(DeleteUndoAction* undo)
{
this->contents->fillUndoItem(undo);
}
/**
* Add an element to the this selection
*/
void EditSelection::addElement(Element* e)
{
XOJ_CHECK_TYPE(EditSelection);
this->contents->addElement(e);
if (e->rescaleOnlyAspectRatio())
{
this->aspectRatio = true;
}
}
/**
* Returns all containig elements of this selections
*/
ElementVector* EditSelection::getElements()
{
XOJ_CHECK_TYPE(EditSelection);
return this->contents->getElements();
}
/**
* Finish the current movement
* (should be called in the mouse-button-released event handler)
*/
void EditSelection::mouseUp()
{
XOJ_CHECK_TYPE(EditSelection);
PageRef page = this->view->getPage();
Layer* layer = page->getSelectedLayer();
snapRotation();
this->contents->updateContent(this->x, this->y, this->rotation, this->width, this->height, this->aspectRatio,
layer, page, this->view, this->undo, this->mouseDownType);
this->mouseDownType = CURSOR_SELECTION_NONE;
}
/**
* Handles mouse input for moving and resizing, coordinates are relative to "view"
*/
void EditSelection::mouseDown(CursorSelectionType type, double x, double y)
{
double zoom = this->view->getXournal()->getZoom();
x /= zoom;
y /= zoom;
this->mouseDownType = type;
this->relMousePosX = x - this->x;
this->relMousePosY = y - this->y;
}
/**
* Handles mouse input for moving and resizing, coordinates are relative to "view"
*/
void EditSelection::mouseMove(double x, double y)
{
double zoom = this->view->getXournal()->getZoom();
x /= zoom;
y /= zoom;
if (this->mouseDownType == CURSOR_SELECTION_MOVE)
{
this->x = x - this->relMousePosX;
this->y = y - this->relMousePosY;
}
else if (this->mouseDownType == CURSOR_SELECTION_TOP_LEFT)
{
double dx = x - this->x;
double dy = y - this->y;
double f;
if (ABS(dy) < ABS(dx))
{
f = (this->height + dy) / this->height;
}
else
{
f = (this->width + dx) / this->width;
}
double oldW = this->width;
double oldH = this->height;
this->width /= f;
this->height /= f;
this->x += oldW - this->width;
this->y += oldH - this->height;
}
else if (this->mouseDownType == CURSOR_SELECTION_TOP_RIGHT)
{
double dx = x - this->x - this->width;
double dy = y - this->y;
double f;
if (ABS(dy) < ABS(dx))
{
f = this->height / (this->height + dy);
}
else
{
f = (this->width + dx) / this->width;
}
double oldH = this->height;
this->width *= f;
this->height *= f;
this->y += oldH - this->height;
}
else if (this->mouseDownType == CURSOR_SELECTION_BOTTOM_LEFT)
{
double dx = x - this->x;
double dy = y - this->y - this->height;
double f;
if (ABS(dy) < ABS(dx))
{
f = (this->height + dy) / this->height;
}
else
{
f = this->width / (this->width + dx);
}
double oldW = this->width;
this->width *= f;
this->height *= f;
this->x += oldW - this->width;
}
else if (this->mouseDownType == CURSOR_SELECTION_BOTTOM_RIGHT)
{
double dx = x - this->x - this->width;
double dy = y - this->y - this->height;
double f;
if (ABS(dy) < ABS(dx))
{
f = (this->height + dy) / this->height;
}
else
{
f = (this->width + dx) / this->width;
}
this->width *= f;
this->height *= f;
}
else if (this->mouseDownType == CURSOR_SELECTION_TOP)
{
double dy = y - this->y;
this->height -= dy;
this->y += dy;
}
else if (this->mouseDownType == CURSOR_SELECTION_BOTTOM)
{
double dy = y - this->y - this->height;
this->height += dy;
}
else if (this->mouseDownType == CURSOR_SELECTION_LEFT)
{
double dx = x - this->x;
this->width -= dx;
this->x += dx;
}
else if (this->mouseDownType == CURSOR_SELECTION_RIGHT)
{
double dx = x - this->x - this->width;
this->width += dx;
}
else if (this->mouseDownType == CURSOR_SELECTION_ROTATE) //catch rotation here
{
double dx = x - this->x - this->width/2;
double dy = y - this->y - this->height/2;
double angle = atan2(dy,dx);
this->rotation = angle;
}
this->view->getXournal()->repaintSelection();
XojPageView* v = getBestMatchingPageView();
if (v && v != this->view)
{
XournalView* xournal = this->view->getXournal();
int pageNr = xournal->getControl()->getDocument()->indexOf(v->getPage());
xournal->pageSelected(pageNr);
translateToView(v);
}
}
XojPageView* EditSelection::getBestMatchingPageView()
{
PagePositionHandler* pp = this->view->getXournal()->getPagePositionHandler();
int rx = this->getXOnViewAbsolute();
int ry = this->getYOnViewAbsolute();
XojPageView* v = pp->getBestMatchingView(rx, ry, this->getViewWidth(), this->getViewHeight());
return v;
}
/**
* Translate all coordinates which are relative to the current view to the new view,
* and set the attribute view to the new view
*/
void EditSelection::translateToView(XojPageView* v)
{
double zoom = view->getXournal()->getZoom();
int aX1 = getXOnViewAbsolute();
int aY1 = getYOnViewAbsolute();
this->x = (aX1 - v->getX()) / zoom;
this->y = (aY1 - v->getY()) / zoom;
this->view = v;
int aX2 = getXOnViewAbsolute();
int aY2 = getYOnViewAbsolute();
if (aX1 != aX2)
{
cout << "aX1 != aX2!! " << aX1 << " / " << aX2 << endl;
}
if (aY1 != aY2)
{
cout << "aY1 != aY2!! " << aY1 << " / " << aY2 << endl;
}
}
void EditSelection::copySelection()
{
undo->addUndoAction(contents->copySelection(this->view->getPage(), this->view, this->x, this->y));
}
/**
* If the selection should moved (or rescaled)
*/
bool EditSelection::isMoving()
{
return this->mouseDownType != CURSOR_SELECTION_NONE;
}
/**
* Move the selection
*/
void EditSelection::moveSelection(double dx, double dy)
{
XOJ_CHECK_TYPE(EditSelection);
this->x -= dx;
this->y -= dy;
ensureWithinVisibleArea();
this->view->getXournal()->repaintSelection();
}
/**
* If the selection is outside the visible area correct the coordinates
*/
void EditSelection::ensureWithinVisibleArea()
{
XOJ_CHECK_TYPE(EditSelection);
int viewx = this->view->getX();
int viewy = this->view->getY();
double zoom = this->view->getXournal()->getZoom();
// need to modify this to take into account the position
// of the object, plus typecast because XojPageView takes ints
this->view->getXournal()->ensureRectIsVisible((int) (viewx + this->x * zoom), (int) (viewy + this->y * zoom),
(int) (this->width * zoom), (int) (this->height * zoom));
}
/**
* Get the cursor type for the current position (if 0 then the default cursor should be used)
*/
CursorSelectionType EditSelection::getSelectionTypeForPos(double x, double y, double zoom)
{
XOJ_CHECK_TYPE(EditSelection);
double x1 = getXOnView() * zoom;
double x2 = x1 + (this->width * zoom);
double y1 = getYOnView() * zoom;
double y2 = y1 + (this->height * zoom);
const int EDGE_PADDING = 5;
const int BORDER_PADDING = 3;
if (x1 - EDGE_PADDING <= x && x <= x1 + EDGE_PADDING && y1 - EDGE_PADDING <= y && y <= y1 + EDGE_PADDING)
{
return CURSOR_SELECTION_TOP_LEFT;
}
if (x2 - EDGE_PADDING <= x && x <= x2 + EDGE_PADDING && y1 - EDGE_PADDING <= y && y <= y1 + EDGE_PADDING)
{
return CURSOR_SELECTION_TOP_RIGHT;
}
if (x1 - EDGE_PADDING <= x && x <= x1 + EDGE_PADDING && y2 - EDGE_PADDING <= y && y <= y2 + EDGE_PADDING)
{
return CURSOR_SELECTION_BOTTOM_LEFT;
}
if (x2 - EDGE_PADDING <= x && x <= x2 + EDGE_PADDING && y2 - EDGE_PADDING <= y && y <= y2 + EDGE_PADDING)
{
return CURSOR_SELECTION_BOTTOM_RIGHT;
}
if (x2 + BORDER_PADDING + 8 <= x && x <= x2 + BORDER_PADDING + 16 && (y2 + y1)/2 - 4 <= y && (y2 + y1)/2 + 4 >= y )
{
return CURSOR_SELECTION_ROTATE;
}
if (!this->aspectRatio)
{
if (x1 <= x && x2 >= x)
{
if (y1 - BORDER_PADDING <= y && y <= y1 + BORDER_PADDING)
{
return CURSOR_SELECTION_TOP;
}
if (y2 - BORDER_PADDING <= y && y <= y2 + BORDER_PADDING)
{
return CURSOR_SELECTION_BOTTOM;
}
}
if (y1 <= y && y2 >= y)
{
if (x1 - BORDER_PADDING <= x && x <= x1 + BORDER_PADDING)
{
return CURSOR_SELECTION_LEFT;
}
if (x2 - BORDER_PADDING <= x && x <= x2 + BORDER_PADDING)
{
return CURSOR_SELECTION_RIGHT;
}
}
}
if (x1 <= x && x <= x2 && y1 <= y && y <= y2)
{
return CURSOR_SELECTION_MOVE;
}
return CURSOR_SELECTION_NONE;
}
void EditSelection::snapRotation()
{
bool snapping = this->view->getXournal()->getControl()->isRotationSnapping();
if (!snapping)
{
return;
}
double epsilon = 0.1;
const double ROTATION_LOCK[8] = {0, M_PI / 2.0, M_PI, M_PI / 4.0, 3.0 * M_PI / 4.0,
- M_PI / 4.0, - 3.0 * M_PI / 4.0, - M_PI / 2.0};
for ( int i = 0; i < sizeof(ROTATION_LOCK) / sizeof(ROTATION_LOCK[0]); i++ )
{
if (std::abs(this->rotation - ROTATION_LOCK[i]) < epsilon)
{
this->rotation = ROTATION_LOCK[i];
}
}
}
/**
* Paints the selection to cr, with the given zoom factor. The coordinates of cr
* should be relative to the provideded view by getView() (use translateEvent())
*/
void EditSelection::paint(cairo_t* cr, double zoom)
{
XOJ_CHECK_TYPE(EditSelection);
double x = this->x;
double y = this->y;
if (abs(this->rotation) > __DBL_EPSILON__)
{
snapRotation();
double rx = (x + width / 2) * zoom;
double ry = (y + height / 2) * zoom;
cairo_translate(cr, rx, ry);
cairo_rotate(cr, this->rotation);
// Draw the rotation point for debugging
cairo_set_source_rgb(cr, 0, 1, 0);
cairo_rectangle(cr, 0, 0, 10, 10);
cairo_stroke(cr);
cairo_translate(cr, -rx, -ry);
}
this->contents->paint(cr, x, y, this->rotation, this->width, this->height, zoom);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
GtkColorWrapper selectionColor = view->getSelectionColor();
// set the line always the same size on display
cairo_set_line_width(cr, 1);
const double dashes[] = {10.0, 10.0};
cairo_set_dash(cr, dashes, sizeof(dashes) / sizeof(dashes[0]), 0);
selectionColor.apply(cr);
cairo_rectangle(cr, x * zoom, y * zoom, width * zoom, height * zoom);
cairo_stroke_preserve(cr);
selectionColor.applyWithAlpha(cr, 0.3);
cairo_fill(cr);
cairo_set_dash(cr, NULL, 0, 0);
if (!this->aspectRatio)
{
// top
drawAnchorRect(cr, x + width / 2, y, zoom);
// bottom
drawAnchorRect(cr, x + width / 2, y + height, zoom);
// left
drawAnchorRect(cr, x, y + height / 2, zoom);
// right
drawAnchorRect(cr, x + width, y + height / 2, zoom);
// rotation handle
drawAnchorRotation(cr, x + width +12/zoom, y + height / 2, zoom);
}
// top left
drawAnchorRect(cr, x, y, zoom);
// top right
drawAnchorRect(cr, x + width, y, zoom);
// bottom left
drawAnchorRect(cr, x, y + height, zoom);
// bottom right
drawAnchorRect(cr, x + width, y + height, zoom);
}
void EditSelection::drawAnchorRotation(cairo_t* cr, double x, double y, double zoom)
{
XOJ_CHECK_TYPE(EditSelection);
GtkColorWrapper selectionColor = view->getSelectionColor();
selectionColor.apply(cr);
cairo_rectangle(cr, x * zoom - 4, y * zoom - 4, 8, 8);
cairo_stroke_preserve(cr);
cairo_set_source_rgb(cr, 1, 0, 0);
cairo_fill(cr);
}
/**
* draws an idicator where you can scale the selection
*/
void EditSelection::drawAnchorRect(cairo_t* cr, double x, double y, double zoom)
{
XOJ_CHECK_TYPE(EditSelection);
GtkColorWrapper selectionColor = view->getSelectionColor();
selectionColor.apply(cr);
cairo_rectangle(cr, x * zoom - 4, y * zoom - 4, 8, 8);
cairo_stroke_preserve(cr);
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_fill(cr);
}
XojPageView* EditSelection::getView()
{
XOJ_CHECK_TYPE(EditSelection);
return this->view;
}
void EditSelection::serialize(ObjectOutputStream& out)
{
out.writeObject("EditSelection");
out.writeDouble(this->x);
out.writeDouble(this->y);
out.writeDouble(this->width);
out.writeDouble(this->height);
out << this->contents;
out.endObject();
out.writeInt(this->getElements()->size());
for (Element* e : *this->getElements()) out << e;
}
void EditSelection::readSerialized(ObjectInputStream& in) throw (InputStreamException)
{
in.readObject("EditSelection");
this->x = in.readDouble();
this->y = in.readDouble();
this->width = in.readDouble();
this->height = in.readDouble();
in >> this->contents;
in.endObject();
}