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.
1205 lines
24 KiB
1205 lines
24 KiB
#include "PageView.h" |
|
|
|
#include "Cursor.h" |
|
#include "RepaintHandler.h" |
|
#include "TextEditor.h" |
|
#include "XournalView.h" |
|
|
|
#include "control/Control.h" |
|
#include "control/SearchControl.h" |
|
#include "control/settings/ButtonConfig.h" |
|
#include "control/settings/Settings.h" |
|
#include "control/jobs/BlockingJob.h" |
|
#include "control/tools/ArrowHandler.h" |
|
#include "control/tools/CircleHandler.h" |
|
#include "control/tools/EraseHandler.h" |
|
#include "control/tools/ImageHandler.h" |
|
#include "control/tools/InputHandler.h" |
|
#include "control/tools/RectangleHandler.h" |
|
#include "control/tools/RulerHandler.h" |
|
#include "control/tools/Selection.h" |
|
#include "control/tools/StrokeHandler.h" |
|
#include "control/tools/VerticalToolHandler.h" |
|
#include "model/Image.h" |
|
#include "model/Layer.h" |
|
#include "model/PageRef.h" |
|
#include "model/Stroke.h" |
|
#include "model/Text.h" |
|
#include "undo/DeleteUndoAction.h" |
|
#include "undo/InsertUndoAction.h" |
|
#include "undo/TextBoxUndoAction.h" |
|
#include "view/TextView.h" |
|
#include "widgets/XournalWidget.h" |
|
|
|
#include <config.h> |
|
#include <config-debug.h> |
|
#include <i18n.h> |
|
#include <pixbuf-utils.h> |
|
#include <Range.h> |
|
#include <Rectangle.h> |
|
|
|
#include <gdk/gdk.h> |
|
|
|
#include <stdlib.h> |
|
#include <math.h> |
|
|
|
string lastfn = ""; |
|
|
|
XojPageView::XojPageView(XournalView* xournal, PageRef page) |
|
{ |
|
XOJ_INIT_TYPE(XojPageView); |
|
|
|
this->page = page; |
|
this->registerListener(this->page); |
|
this->xournal = xournal; |
|
this->selected = false; |
|
this->settings = xournal->getControl()->getSettings(); |
|
this->lastVisibleTime = -1; |
|
|
|
g_mutex_init(&this->drawingMutex); |
|
|
|
this->rerenderComplete = false; |
|
g_mutex_init(&this->repaintRectMutex); |
|
|
|
this->crBuffer = NULL; |
|
|
|
this->inEraser = false; |
|
|
|
this->verticalSpace = NULL; |
|
|
|
this->selection = NULL; |
|
|
|
this->textEditor = NULL; |
|
|
|
// this does not have to be deleted afterwards: |
|
// (we need it for undo commands) |
|
this->oldtext = NULL; |
|
|
|
this->search = NULL; |
|
|
|
this->eraser = new EraseHandler(xournal->getControl()->getUndoRedoHandler(), xournal->getControl()->getDocument(), |
|
this->page, xournal->getControl()->getToolHandler(), this); |
|
|
|
this->inputHandler = NULL; |
|
} |
|
|
|
XojPageView::~XojPageView() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
// Unregister listener before destroying this handler |
|
this->unregisterListener(); |
|
|
|
this->xournal->getControl()->getScheduler()->removePage(this); |
|
delete this->inputHandler; |
|
this->inputHandler = NULL; |
|
delete this->eraser; |
|
this->eraser = NULL; |
|
endText(); |
|
deleteViewBuffer(); |
|
|
|
for (Rectangle* rect : this->rerenderRects) |
|
{ |
|
delete rect; |
|
} |
|
this->rerenderRects.clear(); |
|
|
|
delete this->search; |
|
this->search = NULL; |
|
|
|
XOJ_RELEASE_TYPE(XojPageView); |
|
} |
|
|
|
void XojPageView::setIsVisible(bool visible) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
if (visible) |
|
{ |
|
this->lastVisibleTime = 0; |
|
} |
|
else if (this->lastVisibleTime <= 0) |
|
{ |
|
GTimeVal val; |
|
g_get_current_time(&val); |
|
this->lastVisibleTime = val.tv_sec; |
|
} |
|
} |
|
|
|
int XojPageView::getLastVisibleTime() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
if (this->crBuffer == NULL) |
|
{ |
|
return -1; |
|
} |
|
|
|
return this->lastVisibleTime; |
|
} |
|
|
|
void XojPageView::deleteViewBuffer() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
g_mutex_lock(&this->drawingMutex); |
|
if (this->crBuffer) |
|
{ |
|
cairo_surface_destroy(this->crBuffer); |
|
this->crBuffer = NULL; |
|
} |
|
g_mutex_unlock(&this->drawingMutex); |
|
} |
|
|
|
bool XojPageView::containsPoint(int x, int y, bool local) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
if (!local) |
|
{ |
|
bool leftOk = this->layout.getLayoutAbsoluteX() <= x; |
|
bool rightOk = x <= this->layout.getLayoutAbsoluteX() + this->getDisplayWidth(); |
|
bool topOk = this->layout.getLayoutAbsoluteY() <= y; |
|
bool bottomOk = y <= this->layout.getLayoutAbsoluteY() + this->getDisplayHeight(); |
|
|
|
return leftOk && rightOk && topOk && bottomOk; |
|
} |
|
else |
|
{ |
|
return x >= 0 && y >= 0 && x <= this->getWidth() && y <= this->getHeight(); |
|
} |
|
} |
|
|
|
bool XojPageView::searchTextOnPage(string& text, int* occures, double* top) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
if (this->search == NULL) |
|
{ |
|
if (text.empty()) return true; |
|
|
|
int pNr = this->page->getPdfPageNr(); |
|
XojPdfPageSPtr pdf = NULL; |
|
if (pNr != -1) |
|
{ |
|
Document* doc = xournal->getControl()->getDocument(); |
|
|
|
doc->lock(); |
|
pdf = doc->getPdfPage(pNr); |
|
doc->unlock(); |
|
} |
|
this->search = new SearchControl(page, pdf); |
|
} |
|
|
|
bool found = this->search->search(text, occures, top); |
|
|
|
repaintPage(); |
|
|
|
return found; |
|
} |
|
|
|
void XojPageView::endText() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
if (!this->textEditor) |
|
{ |
|
return; |
|
} |
|
Text* txt = this->textEditor->getText(); |
|
Layer* layer = this->page->getSelectedLayer(); |
|
UndoRedoHandler* undo = xournal->getControl()->getUndoRedoHandler(); |
|
|
|
// Text deleted |
|
if (txt->getText().empty()) |
|
{ |
|
// old element |
|
int pos = layer->indexOf(txt); |
|
if (pos != -1) |
|
{ |
|
DeleteUndoAction* eraseDeleteUndoAction = new DeleteUndoAction(page, true); |
|
layer->removeElement(txt, false); |
|
eraseDeleteUndoAction->addElement(layer, txt, pos); |
|
undo->addUndoAction(eraseDeleteUndoAction); |
|
} |
|
} |
|
else |
|
{ |
|
// new element |
|
if (layer->indexOf(txt) == -1) |
|
{ |
|
undo->addUndoActionBefore(new InsertUndoAction(page, layer, txt), this->textEditor->getFirstUndoAction()); |
|
layer->addElement(txt); |
|
this->textEditor->textCopyed(); |
|
} |
|
// or if the file was saved and reopened |
|
// and/or if we click away from the text window |
|
else |
|
{ |
|
// TextUndoAction does not work because the textEdit object is destroyed |
|
// after endText() so we need to instead copy the information between an |
|
// old and new element that we can push and pop to recover. |
|
undo->addUndoAction(new TextBoxUndoAction(page, layer, txt, this->oldtext)); |
|
} |
|
} |
|
|
|
delete this->textEditor; |
|
this->textEditor = NULL; |
|
this->rerenderPage(); |
|
} |
|
|
|
void XojPageView::startText(double x, double y) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
this->xournal->endTextAllPages(this); |
|
this->xournal->getControl()->getSearchBar()->showSearchBar(false); |
|
|
|
if (this->textEditor == NULL) |
|
{ |
|
// Is there already a textfield? |
|
Text* text = NULL; |
|
|
|
for (Element* e : *this->page->getSelectedLayer()->getElements()) |
|
{ |
|
if (e->getType() == ELEMENT_TEXT) |
|
{ |
|
GdkRectangle matchRect = { gint(x - 10), gint(y - 10), 20, 20 }; |
|
if (e->intersectsArea(&matchRect)) |
|
{ |
|
text = (Text*) e; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
bool ownText = false; |
|
if (text == NULL) |
|
{ |
|
ToolHandler* h = xournal->getControl()->getToolHandler(); |
|
ownText = true; |
|
text = new Text(); |
|
text->setX(x); |
|
text->setY(y); |
|
text->setColor(h->getColor()); |
|
text->setFont(settings->getFont()); |
|
} |
|
else |
|
{ |
|
|
|
// We can try to add an undo action here. The initial text shows up in this |
|
// textEditor element. |
|
this->oldtext = text; |
|
// text = new Text(*oldtext); |
|
// need to clone the old text so that references still work properly. |
|
// cloning breaks things a little. do it manually |
|
text = new Text(); |
|
text->setX(oldtext->getX()); |
|
text->setY(oldtext->getY()); |
|
text->setColor(oldtext->getColor()); |
|
text->setFont(oldtext->getFont()); |
|
text->setText(oldtext->getText()); |
|
|
|
Layer* layer = this->page->getSelectedLayer(); |
|
layer->removeElement(this->oldtext, false); |
|
layer->addElement(text); |
|
// perform the old swap onto the new text drawn. |
|
} |
|
|
|
this->textEditor = new TextEditor(this, xournal->getWidget(), text, ownText); |
|
if (!ownText) |
|
{ |
|
this->textEditor->mousePressed(x - text->getX(), y - text->getY()); |
|
} |
|
|
|
this->rerenderPage(); |
|
} |
|
else |
|
{ |
|
Text* text = this->textEditor->getText(); |
|
GdkRectangle matchRect = {gint(x - 10), gint(y - 10), 20, 20}; |
|
if (!text->intersectsArea(&matchRect)) |
|
{ |
|
endText(); |
|
} |
|
else |
|
{ |
|
this->textEditor->mousePressed(x - text->getX(), y - text->getY()); |
|
} |
|
} |
|
} |
|
|
|
void XojPageView::playObjectAt(double x, double y) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
int selected = this->page->getSelectedLayerId(); |
|
GdkRectangle matchRect = |
|
{ gint(x - 10), gint(y - 10), 20, 20 }; |
|
|
|
double gap = 1000000000; |
|
|
|
// clear old selection anyway |
|
this->xournal->getControl()->clearSelection(); |
|
|
|
for (Layer* l : *this->page->getLayers()) |
|
{ |
|
for (Element* e : *l->getElements()) |
|
{ |
|
if (e->intersectsArea(&matchRect)) |
|
{ |
|
if (e->getType() == ELEMENT_STROKE) |
|
{ |
|
Stroke* s = (Stroke*) e; |
|
double tmpGap = 0; |
|
if ((s->intersects(x, y, 15, &tmpGap)) && (gap > tmpGap)) |
|
{ |
|
gap = tmpGap; |
|
|
|
int ts = s->getTimestamp(); |
|
int buffer = 5; |
|
|
|
if (ts >= buffer) |
|
{ |
|
ts -= buffer; |
|
} |
|
else |
|
{ |
|
ts = 0; |
|
} |
|
|
|
string fn = s->getAudioFilename(); |
|
|
|
if (fn != lastfn) |
|
{ |
|
if (fn != "") |
|
{ |
|
lastfn = fn; |
|
string command( |
|
"vlc --qt-start-minimized " + settings->getAudioFolder() + "/" + fn + " --start-time=" |
|
+ std::to_string(ts) + " &>/dev/null &"); |
|
system(command.c_str()); |
|
} |
|
} |
|
else |
|
{ |
|
//TODO: substitute system(..) with some c++ curl library |
|
string psw("password"); |
|
string command( |
|
"curl -s -u \"\":\""+psw+"\" --url \"http://127.0.0.1:8080/requests/status.xml?command=seek&val=" |
|
+ std::to_string(ts) + "\" >/dev/null" |
|
+ "&& curl -s -u \"\":\""+psw+"\" --url \"http://127.0.0.1:8080/requests/status.xml?command=pl_play\" >/dev/null"); |
|
system(command.c_str()); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
selected--; |
|
} |
|
} |
|
|
|
void XojPageView::selectObjectAt(double x, double y) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
int selected = this->page->getSelectedLayerId(); |
|
GdkRectangle matchRect = { gint(x - 10), gint(y - 10), 20, 20 }; |
|
|
|
Stroke* strokeMatch = NULL; |
|
double gap = 1000000000; |
|
|
|
Element* elementMatch = NULL; |
|
|
|
// clear old selection anyway |
|
this->xournal->getControl()->clearSelection(); |
|
|
|
for (Layer* l : *this->page->getLayers()) |
|
{ |
|
for (Element* e : *l->getElements()) |
|
{ |
|
if (e->intersectsArea(&matchRect)) |
|
{ |
|
if (e->getType() == ELEMENT_STROKE) |
|
{ |
|
Stroke* s = (Stroke*) e; |
|
double tmpGap = 0; |
|
if ((s->intersects(x, y, 5, &tmpGap)) && (gap > tmpGap)) |
|
{ |
|
gap = tmpGap; |
|
strokeMatch = s; |
|
} |
|
} |
|
else |
|
{ |
|
elementMatch = e; |
|
} |
|
} |
|
} |
|
|
|
selected--; |
|
} |
|
|
|
if (strokeMatch) |
|
{ |
|
elementMatch = strokeMatch; |
|
} |
|
|
|
if (elementMatch) |
|
{ |
|
xournal->setSelection(new EditSelection(xournal->getControl()->getUndoRedoHandler(), elementMatch, this, page)); |
|
|
|
repaintPage(); |
|
} |
|
} |
|
|
|
bool XojPageView::onButtonPressEvent(const PositionInputData& pos) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
if ((pos.state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) != 0) |
|
{ |
|
return false; // not handled here |
|
} |
|
|
|
if (!this->selected) |
|
{ |
|
xournal->getControl()->firePageSelected(this->page); |
|
} |
|
|
|
ToolHandler* h = xournal->getControl()->getToolHandler(); |
|
|
|
double x = pos.x; |
|
double y = pos.y; |
|
|
|
if (x < 0 || y < 0) |
|
{ |
|
return false; |
|
} |
|
|
|
double zoom = xournal->getZoom(); |
|
x /= zoom; |
|
y /= zoom; |
|
|
|
Cursor* cursor = xournal->getCursor(); |
|
cursor->setMouseDown(true); |
|
|
|
if (h->getToolType() == TOOL_PEN || h->getToolType() == TOOL_HILIGHTER || |
|
(h->getToolType() == TOOL_ERASER && h->getEraserType() == ERASER_TYPE_WHITEOUT)) |
|
{ |
|
delete this->inputHandler; |
|
this->inputHandler = NULL; |
|
|
|
if (h->getDrawingType() == DRAWING_TYPE_LINE) |
|
{ |
|
this->inputHandler = new RulerHandler(this->xournal, this, getPage()); |
|
} |
|
else if (h->getDrawingType() == DRAWING_TYPE_RECTANGLE) |
|
{ |
|
this->inputHandler = new RectangleHandler(this->xournal, this, getPage()); |
|
} |
|
else if (h->getDrawingType() == DRAWING_TYPE_CIRCLE) |
|
{ |
|
this->inputHandler = new CircleHandler(this->xournal, this, getPage()); |
|
} |
|
else if (h->getDrawingType() == DRAWING_TYPE_ARROW) |
|
{ |
|
this->inputHandler = new ArrowHandler(this->xournal, this, getPage()); |
|
} |
|
else |
|
{ |
|
this->inputHandler = new StrokeHandler(this->xournal, this, getPage()); |
|
} |
|
|
|
this->inputHandler->onButtonPressEvent(pos); |
|
} |
|
else if(h->getToolType() == TOOL_ERASER) |
|
{ |
|
this->eraser->erase(x, y); |
|
this->inEraser = true; |
|
} |
|
else if (h->getToolType() == TOOL_VERTICAL_SPACE) |
|
{ |
|
this->verticalSpace = new VerticalToolHandler(this, this->page, y, zoom); |
|
} |
|
else if (h->getToolType() == TOOL_SELECT_RECT || |
|
h->getToolType() == TOOL_SELECT_REGION || |
|
h->getToolType() == TOOL_PLAY_OBJECT || |
|
h->getToolType() == TOOL_SELECT_OBJECT) |
|
{ |
|
if (h->getToolType() == TOOL_SELECT_RECT) |
|
{ |
|
if (this->selection) |
|
{ |
|
delete this->selection; |
|
this->selection = NULL; |
|
repaintPage(); |
|
} |
|
this->selection = new RectSelection(x, y, this); |
|
} |
|
else if (h->getToolType() == TOOL_SELECT_REGION) |
|
{ |
|
if (this->selection) |
|
{ |
|
delete this->selection; |
|
this->selection = NULL; |
|
repaintPage(); |
|
} |
|
this->selection = new RegionSelect(x, y, this); |
|
} |
|
else if (h->getToolType() == TOOL_SELECT_OBJECT) |
|
{ |
|
selectObjectAt(x, y); |
|
} |
|
else if (h->getToolType() == TOOL_PLAY_OBJECT) |
|
{ |
|
playObjectAt(x, y); |
|
} |
|
} |
|
else if (h->getToolType() == TOOL_TEXT) |
|
{ |
|
startText(x, y); |
|
} |
|
else if (h->getToolType() == TOOL_IMAGE) |
|
{ |
|
ImageHandler imgHandler(xournal->getControl(), this); |
|
imgHandler.insertImage(x, y); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void XojPageView::resetShapeRecognizer() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
if (this->inputHandler != NULL) |
|
{ |
|
this->inputHandler->resetShapeRecognizer(); |
|
} |
|
} |
|
|
|
bool XojPageView::onMotionNotifyEvent(const PositionInputData& pos) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
double zoom = xournal->getZoom(); |
|
double x = pos.x / zoom; |
|
double y = pos.y / zoom; |
|
|
|
ToolHandler* h = xournal->getControl()->getToolHandler(); |
|
|
|
if (containsPoint(x, y, true) && |
|
this->inputHandler && |
|
this->inputHandler->onMotionNotifyEvent(pos)) |
|
{ |
|
//input handler used this event |
|
} |
|
else if (this->selection) |
|
{ |
|
this->selection->currentPos(x, y); |
|
} |
|
else if (this->verticalSpace) |
|
{ |
|
this->verticalSpace->currentPos(x, y); |
|
} |
|
else if (this->textEditor) |
|
{ |
|
Cursor* cursor = getXournal()->getCursor(); |
|
cursor->setInvisible(false); |
|
|
|
Text* text = this->textEditor->getText(); |
|
this->textEditor->mouseMoved(x - text->getX(), y - text->getY()); |
|
} |
|
else if (h->getToolType() == TOOL_ERASER && h->getEraserType() != ERASER_TYPE_WHITEOUT && this->inEraser) |
|
{ |
|
this->eraser->erase(x, y); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
bool XojPageView::onButtonReleaseEvent(const PositionInputData& pos) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
Control* control = xournal->getControl(); |
|
|
|
if (this->inputHandler) |
|
{ |
|
this->inputHandler->onButtonReleaseEvent(pos); |
|
delete this->inputHandler; |
|
this->inputHandler = NULL; |
|
} |
|
|
|
if (this->inEraser) |
|
{ |
|
this->inEraser = false; |
|
Document* doc = this->xournal->getControl()->getDocument(); |
|
doc->lock(); |
|
this->eraser->finalize(); |
|
doc->unlock(); |
|
} |
|
|
|
if (this->verticalSpace) |
|
{ |
|
MoveUndoAction* undo = this->verticalSpace->finalize(); |
|
delete this->verticalSpace; |
|
this->verticalSpace = NULL; |
|
control->getUndoRedoHandler()->addUndoAction(undo); |
|
} |
|
|
|
if (this->selection) |
|
{ |
|
if (this->selection->finalize(this->page)) |
|
{ |
|
xournal->setSelection(new EditSelection(control->getUndoRedoHandler(), this->selection, this)); |
|
delete this->selection; |
|
this->selection = NULL; |
|
} |
|
else |
|
{ |
|
delete this->selection; |
|
this->selection = NULL; |
|
|
|
repaintPage(); |
|
} |
|
|
|
} |
|
else if (this->textEditor) |
|
{ |
|
this->textEditor->mouseReleased(); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
bool XojPageView::onKeyPressEvent(GdkEventKey* event) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
// Esc leaves text edition |
|
if (event->keyval == GDK_KEY_Escape) |
|
{ |
|
if (this->textEditor) |
|
{ |
|
endText(); |
|
return true; |
|
} |
|
else if (xournal->getSelection()) |
|
{ |
|
xournal->clearSelection(); |
|
return true; |
|
} |
|
else |
|
{ |
|
return false; |
|
} |
|
} |
|
|
|
if (this->textEditor && this->textEditor->onKeyPressEvent(event)) |
|
{ |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
bool XojPageView::onKeyReleaseEvent(GdkEventKey* event) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
if (this->textEditor && this->textEditor->onKeyReleaseEvent(event)) |
|
{ |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
void XojPageView::rerenderPage() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
this->rerenderComplete = true; |
|
this->xournal->getControl()->getScheduler()->addRerenderPage(this); |
|
} |
|
|
|
void XojPageView::repaintPage() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
xournal->getRepaintHandler()->repaintPage(this); |
|
} |
|
|
|
void XojPageView::repaintArea(double x1, double y1, double x2, double y2) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
double zoom = xournal->getZoom(); |
|
xournal->getRepaintHandler()->repaintPageArea(this, x1 * zoom - 10, y1 * zoom - 10, x2 * zoom + 20, y2 * zoom + 20); |
|
} |
|
|
|
void XojPageView::rerenderRect(double x, double y, double width, double height) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
int rx = (int) MAX(x - 10, 0); |
|
int ry = (int) MAX(y - 10, 0); |
|
int rwidth = (int) (width + 20); |
|
int rheight = (int) (height + 20); |
|
|
|
addRerenderRect(rx, ry, rwidth, rheight); |
|
} |
|
|
|
void XojPageView::addRerenderRect(double x, double y, double width, double height) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
if (this->rerenderComplete) |
|
{ |
|
return; |
|
} |
|
|
|
Rectangle* rect = new Rectangle(x, y, width, height); |
|
|
|
g_mutex_lock(&this->repaintRectMutex); |
|
|
|
for (Rectangle* r : this->rerenderRects) |
|
{ |
|
// its faster to redraw only one rect than repaint twice the same area |
|
// so loop through the rectangles to be redrawn, if new rectangle |
|
// intersects any of them, replace it by the union with the new one |
|
if (r->intersects(*rect)) |
|
{ |
|
r->add(*rect); |
|
|
|
delete rect; |
|
|
|
g_mutex_unlock(&this->repaintRectMutex); |
|
return; |
|
} |
|
} |
|
|
|
this->rerenderRects.push_back(rect); |
|
g_mutex_unlock(&this->repaintRectMutex); |
|
|
|
this->xournal->getControl()->getScheduler()->addRerenderPage(this); |
|
} |
|
|
|
void XojPageView::setSelected(bool selected) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
this->selected = selected; |
|
|
|
if (selected) |
|
{ |
|
this->xournal->requestFocus(); |
|
this->xournal->getRepaintHandler()->repaintPageBorder(this); |
|
} |
|
} |
|
|
|
bool XojPageView::cut() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
if (this->textEditor) |
|
{ |
|
this->textEditor->cutToClipboard(); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
bool XojPageView::copy() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
if (this->textEditor) |
|
{ |
|
this->textEditor->copyToCliboard(); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
bool XojPageView::paste() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
if (this->textEditor) |
|
{ |
|
this->textEditor->pasteFromClipboard(); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
bool XojPageView::actionDelete() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
if (this->textEditor) |
|
{ |
|
this->textEditor->deleteFromCursor(GTK_DELETE_CHARS, 1); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
void XojPageView::drawLoadingPage(cairo_t* cr) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
static const string txtLoading = _("Loading..."); |
|
|
|
double zoom = xournal->getZoom(); |
|
int dispWidth = getDisplayWidth(); |
|
int dispHeight = getDisplayHeight(); |
|
|
|
cairo_set_source_rgb(cr, 1, 1, 1); |
|
cairo_rectangle(cr, 0, 0, dispWidth, dispHeight); |
|
cairo_fill(cr); |
|
|
|
cairo_scale(cr, zoom, zoom); |
|
|
|
cairo_set_source_rgb(cr, 0.5, 0.5, 0.5); |
|
cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); |
|
cairo_set_font_size(cr, 32.0); |
|
cairo_text_extents_t ex; |
|
cairo_text_extents(cr, txtLoading.c_str(), &ex); |
|
cairo_move_to(cr, (page->getWidth() - ex.width) / 2 - ex.x_bearing, |
|
(page->getHeight() - ex.height) / 2 - ex.y_bearing); |
|
cairo_show_text(cr, txtLoading.c_str()); |
|
|
|
rerenderPage(); |
|
} |
|
|
|
/** |
|
* Does the painting, called in synchronized block |
|
*/ |
|
void XojPageView::paintPageSync(cairo_t* cr, GdkRectangle* rect) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
if (this->crBuffer == NULL) |
|
{ |
|
drawLoadingPage(cr); |
|
return; |
|
} |
|
|
|
double zoom = xournal->getZoom(); |
|
int dispWidth = getDisplayWidth(); |
|
|
|
cairo_save(cr); |
|
|
|
double width = cairo_image_surface_get_width(this->crBuffer); |
|
if (width != dispWidth) |
|
{ |
|
double scale = ((double) dispWidth) / ((double) width); |
|
|
|
// Scale current image to fit the zoom level |
|
cairo_scale(cr, scale, scale); |
|
cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_FAST); |
|
|
|
cairo_set_source_surface(cr, this->crBuffer, 0, 0); |
|
|
|
rerenderPage(); |
|
|
|
rect = NULL; |
|
} |
|
else |
|
{ |
|
cairo_set_source_surface(cr, this->crBuffer, 0, 0); |
|
} |
|
|
|
if (rect) |
|
{ |
|
cairo_rectangle(cr, rect->x, rect->y, rect->width, rect->height); |
|
cairo_fill(cr); |
|
|
|
#ifdef DEBUG_SHOW_PAINT_BOUNDS |
|
cairo_set_source_rgb(cr, 1.0, 0.5, 1.0); |
|
cairo_set_line_width(cr, 1. / zoom); |
|
cairo_rectangle(cr, rect->x, rect->y, rect->width, rect->height); |
|
cairo_stroke(cr); |
|
#endif |
|
} |
|
else |
|
{ |
|
cairo_paint(cr); |
|
} |
|
|
|
cairo_restore(cr); |
|
|
|
// don't paint this with scale, because it needs a 1:1 zoom |
|
if (this->verticalSpace) |
|
{ |
|
this->verticalSpace->paint(cr, rect, zoom); |
|
} |
|
|
|
if (this->textEditor) |
|
{ |
|
cairo_scale(cr, zoom, zoom); |
|
this->textEditor->paint(cr, rect, zoom); |
|
} |
|
if (this->selection) |
|
{ |
|
cairo_scale(cr, zoom, zoom); |
|
this->selection->paint(cr, rect, zoom); |
|
} |
|
|
|
if (this->search) |
|
{ |
|
cairo_scale(cr, zoom, zoom); |
|
this->search->paint(cr, rect, zoom, getSelectionColor()); |
|
} |
|
|
|
if (this->inputHandler) |
|
{ |
|
this->inputHandler->draw(cr); |
|
} |
|
} |
|
|
|
bool XojPageView::paintPage(cairo_t* cr, GdkRectangle* rect) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
g_mutex_lock(&this->drawingMutex); |
|
|
|
paintPageSync(cr, rect); |
|
|
|
g_mutex_unlock(&this->drawingMutex); |
|
return true; |
|
} |
|
|
|
bool XojPageView::containsY(int y) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
return (y >= this->getY() && y <= (this->getY() + this->getDisplayHeight())); |
|
} |
|
|
|
/** |
|
* GETTER / SETTER |
|
*/ |
|
|
|
bool XojPageView::isSelected() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
return selected; |
|
} |
|
|
|
int XojPageView::getBufferPixels() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
if (crBuffer) |
|
{ |
|
return cairo_image_surface_get_width(crBuffer) * cairo_image_surface_get_height(crBuffer); |
|
} |
|
return 0; |
|
} |
|
|
|
GtkColorWrapper XojPageView::getSelectionColor() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
XOJ_CHECK_TYPE(XojPageView); |
|
GtkWidget* widget = getXournal()->getWidget(); |
|
|
|
GtkStyleContext *context = gtk_widget_get_style_context(widget); |
|
|
|
gtk_style_context_save(context); |
|
gtk_style_context_add_class(context, GTK_STYLE_CLASS_RUBBERBAND); |
|
|
|
GdkRGBA col; |
|
gtk_style_context_get_border_color(context, |
|
gtk_widget_get_state_flags(widget), |
|
&col); |
|
|
|
// TODO: The correct border color is not sufficient, |
|
// we should also get the actual color and the GtkBorder |
|
// to draw the selection correctly |
|
|
|
/* |
|
gtk_style_context_get_border (context, state, |
|
&border); |
|
*/ |
|
|
|
gtk_style_context_restore(context); |
|
return col; |
|
} |
|
|
|
TextEditor* XojPageView::getTextEditor() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
return textEditor; |
|
} |
|
|
|
int XojPageView::getX() const |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
return this->layout.getLayoutAbsoluteX(); |
|
} |
|
|
|
int XojPageView::getY() const |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
return this->layout.getLayoutAbsoluteY(); |
|
} |
|
|
|
PageRef XojPageView::getPage() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
return page; |
|
} |
|
|
|
XournalView* XojPageView::getXournal() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
return this->xournal; |
|
} |
|
|
|
double XojPageView::getHeight() const |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
return this->page->getHeight(); |
|
} |
|
|
|
double XojPageView::getWidth() const |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
return this->page->getWidth(); |
|
} |
|
|
|
int XojPageView::getDisplayWidth() const |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
return this->page->getWidth() * this->xournal->getZoom(); |
|
} |
|
|
|
int XojPageView::getDisplayHeight() const |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
return this->page->getHeight() * this->xournal->getZoom(); |
|
} |
|
|
|
TexImage* XojPageView::getSelectedTex() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
EditSelection* theSelection = this->xournal->getSelection(); |
|
if (!theSelection) |
|
{ |
|
return NULL; |
|
} |
|
|
|
for (Element* e : *theSelection->getElements()) |
|
{ |
|
if (e->getType() == ELEMENT_TEXIMAGE) |
|
{ |
|
return (TexImage*) e; |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
Text* XojPageView::getSelectedText() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
EditSelection* theSelection = this->xournal->getSelection(); |
|
if (!theSelection) |
|
{ |
|
return NULL; |
|
} |
|
|
|
for (Element* e : *theSelection->getElements()) |
|
{ |
|
if (e->getType() == ELEMENT_TEXT) |
|
{ |
|
return (Text*) e; |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
Rectangle* XojPageView::getVisibleRect() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
Rectangle* rect = xournal->getVisibleRect(this); |
|
|
|
if (!rect) |
|
{ |
|
return NULL; |
|
} |
|
|
|
(*rect) *= xournal->getZoom(); |
|
|
|
(*rect) = rect->translated(-getX(), -getY()); |
|
|
|
return rect; |
|
} |
|
|
|
Rectangle XojPageView::getRect() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
return Rectangle(getX(), getY(), getDisplayWidth(), getDisplayHeight()); |
|
} |
|
|
|
void XojPageView::rectChanged(Rectangle& rect) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
rerenderRect(rect.x, rect.y, rect.width, rect.height); |
|
} |
|
|
|
void XojPageView::rangeChanged(Range &range) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
rerenderRange(range); |
|
} |
|
|
|
void XojPageView::pageChanged() |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
rerenderPage(); |
|
} |
|
|
|
void XojPageView::elementChanged(Element* elem) |
|
{ |
|
XOJ_CHECK_TYPE(XojPageView); |
|
|
|
if (this->inputHandler && elem == this->inputHandler->getStroke()) |
|
{ |
|
g_mutex_lock(&this->drawingMutex); |
|
|
|
cairo_t* cr = cairo_create(this->crBuffer); |
|
|
|
this->inputHandler->draw(cr); |
|
|
|
cairo_destroy(cr); |
|
|
|
g_mutex_unlock(&this->drawingMutex); |
|
} |
|
else |
|
{ |
|
rerenderElement(elem); |
|
} |
|
}
|
|
|