File indexing completed on 2024-05-12 13:04:46

0001 /*
0002  * This file is part of the KDE project
0003  *
0004  * SPDX-FileCopyrightText: 2013 Shantanu Tushar <shantanu@kde.org>
0005  * SPDX-FileCopyrightText: 2013 Sujith Haridasan <sujith.h@gmail.com>
0006  * SPDX-FileCopyrightText: 2013 Arjen Hiemstra <ahiemstra@heimr.nl>
0007  *
0008  * SPDX-License-Identifier: LGPL-2.0-or-later
0009  *
0010  */
0011 
0012 #include "CQTextDocumentCanvas.h"
0013 #include "CQCanvasController.h"
0014 #include "CQTextDocumentModel.h"
0015 #include "CQTextDocumentNotesModel.h"
0016 
0017 #include "gemini/ViewModeSwitchEvent.h"
0018 
0019 #include <KoPluginLoader.h>
0020 #include <KoDocumentEntry.h>
0021 #include <KoUnit.h>
0022 #include <KoDocument.h>
0023 #include <KoDocumentResourceManager.h>
0024 #include <KoPart.h>
0025 #include <KoFindText.h>
0026 #include <KoCanvasBase.h>
0027 #include <KoToolManager.h>
0028 #include <KoZoomHandler.h>
0029 #include <KoZoomController.h>
0030 #include <KoShape.h>
0031 #include <KoToolManager_p.h>
0032 #include <KoToolBase.h>
0033 #include <KoPointerEvent.h>
0034 #include <KoSelection.h>
0035 #include <KoShapeRegistry.h>
0036 #include <KoShapeAnchor.h>
0037 #include <KoShapeContainer.h>
0038 #include <KoTextEditor.h>
0039 #include <KoProperties.h>
0040 #include <KoColumns.h>
0041 #include <KWDocument.h>
0042 #include <KWPage.h>
0043 #include <KWCanvasItem.h>
0044 #include <commands/KWShapeCreateCommand.h>
0045 
0046 #include <KActionCollection>
0047 
0048 #include <QPluginLoader>
0049 #include <QMimeDatabase>
0050 #include <QStyleOptionGraphicsItem>
0051 #include <QGraphicsWidget>
0052 #include <QTextDocument>
0053 #include <QTextFrame>
0054 #include <QTextLayout>
0055 #include <QGraphicsSceneMouseEvent>
0056 #include <QTextDocumentFragment>
0057 #include <QSvgRenderer>
0058 #include <QApplication>
0059 #include <QDebug>
0060 
0061 class CQTextDocumentCanvas::Private
0062 {
0063 public:
0064     Private()
0065         : canvas(0),
0066           findText(0),
0067           documentModel(0),
0068           document(0),
0069           pageNumber(0),
0070           throttleTimer(new QTimer()),
0071           currentTool(0),
0072           notes(0),
0073           textEditor(0)
0074     {
0075         throttleTimer->setInterval(200);
0076         throttleTimer->setSingleShot(true);
0077     }
0078 
0079     KWCanvasItem *canvas;
0080     QString searchTerm;
0081     KoFindText *findText;
0082     CQTextDocumentModel *documentModel;
0083     KWDocument* document;
0084     KoPart* part;
0085     QSize documentSize;
0086     int pageNumber;
0087     QPoint currentPoint;
0088     QObjectList linkTargets;
0089     QTimer* throttleTimer;
0090     KoToolBase* currentTool;
0091     CQTextDocumentNotesModel* notes;
0092     KoTextEditor* textEditor;
0093 
0094     void updateLinkTargets()
0095     {
0096         qDeleteAll(linkTargets);
0097         linkTargets.clear();
0098 
0099         if (!canvas) {
0100             return;
0101         }
0102 
0103         foreach(const KoShape* shape, canvas->shapeManager()->shapes()) {
0104             if (!shape->hyperLink().isEmpty()) {
0105                 QObject * obj = new QObject(documentModel);
0106                 obj->setProperty("linkRect", shape->boundingRect());
0107                 obj->setProperty("linkTarget", QUrl(shape->hyperLink()));
0108                 linkTargets.append(obj);
0109             }
0110         }
0111 
0112         foreach(QTextDocument* text, findText->documents()) {
0113             QTextBlock block = text->rootFrame()->firstCursorPosition().block();
0114             for (; block.isValid(); block = block.next()) {
0115                 block.begin();
0116                 QTextBlock::iterator it;
0117                 for (it = block.begin(); !(it.atEnd()); ++it) {
0118                     QTextFragment fragment = it.fragment();
0119                     if (fragment.isValid()) {
0120                         QTextCharFormat format = fragment.charFormat();
0121                         if (format.isAnchor()) {
0122                             // This is an anchor, store target and position...
0123                             QObject * obj = new QObject(documentModel);
0124                             QRectF rect = getFragmentPosition(block, fragment);
0125                             KWPage page = document->pageManager()->page(rect.left());
0126                             rect.translate(page.topMargin(), page.rightMargin());
0127                             rect = canvas->viewMode()->documentToView(rect, canvas->viewConverter());
0128                             rect.translate(page.pageNumber() * (page.topMargin() + page.bottomMargin()) + 20, 0);
0129                             obj->setProperty("linkRect", rect);
0130                             obj->setProperty("linkTarget", QUrl(format.anchorHref()));
0131                             linkTargets.append(obj);
0132                         }
0133                     }
0134                 }
0135             }
0136         }
0137     }
0138 
0139     QRectF getFragmentPosition(QTextBlock block, QTextFragment fragment)
0140     {
0141         // TODO this only produces a position for the first part, if the link spans more than one line...
0142         // Need to sort that somehow, unfortunately probably by slapping this code into the above function.
0143         // For now leave it like this, more important things are needed.
0144         QTextLayout* layout = block.layout();
0145         QTextLine line = layout->lineForTextPosition(fragment.position() - block.position());
0146         if (!line.isValid()) {
0147             // fragment has no valid position and consequently no line...
0148             return QRectF();
0149         }
0150         qreal top = line.position().y();
0151         qreal bottom = line.position().y() + line.height();
0152         qreal left = line.cursorToX(fragment.position() - block.position());
0153         qreal right = line.cursorToX((fragment.position() - block.position()) + fragment.length());
0154         QRectF fragmentPosition(QPointF(top, left), QPointF(bottom, right));
0155         return fragmentPosition.adjusted(layout->position().x(), layout->position().y(), 0, 0);
0156     }
0157 
0158     QList<KoShape*> deepShapeFind(const QList<KoShape*>& shapes)
0159     {
0160         QList<KoShape*> allShapes;
0161         foreach(KoShape* shape, shapes) {
0162             allShapes.append(shape);
0163             KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
0164             if (container) {
0165                 allShapes.append(deepShapeFind(container->shapes()));
0166             }
0167         }
0168         return allShapes;
0169     }
0170     QRectF getCursorPosition(int position)
0171     {
0172         QPointF point;
0173         QTextBlock block = textEditor->document()->findBlock(position);
0174         QTextLayout* layout = block.layout();
0175         QTextLine line = layout->lineForTextPosition(position - block.position());
0176         if (!line.isValid()) {
0177             // fragment has no valid position and consequently no line...
0178             return QRectF();
0179         }
0180         qreal top = line.position().y();
0181         qreal left = line.cursorToX(position - block.position());
0182         point = QPointF(left + layout->position().y(), top + layout->position().x());
0183 
0184         KoShape* shape = canvas->shapeManager()->selection()->firstSelectedShape();
0185         point += shape->position();
0186         while(KoShapeContainer* parent = shape->parent()) {
0187             point += parent->position();
0188         }
0189 
0190         KWPage page = document->pageManager()->page(point.y());
0191 //         point += QPointF(page.rightMargin(), page.pageNumber() * page.topMargin()
0192 //                                            + (page.pageNumber() - 1) * page.bottomMargin());
0193 //         if (page.pageNumber() > 1)
0194 //             point += QPointF(0, 20);
0195         point += QPointF(0, (page.pageNumber() - 1) * (page.topMargin() + 20));
0196 //        point = canvas->viewConverter()->documentToView(point);
0197 
0198         return canvas->viewConverter()->documentToView(QRectF(point, QSizeF(0, line.height())));
0199     }
0200 };
0201 
0202 CQTextDocumentCanvas::CQTextDocumentCanvas(QDeclarativeItem* parent)
0203     : CQCanvasBase(parent), d(new Private)
0204 {
0205     setAcceptedMouseButtons(Qt::LeftButton);
0206     d->findText = new KoFindText(this);
0207 
0208     connect (d->findText, SIGNAL(updateCanvas()), SLOT(updateCanvas()));
0209     connect (d->findText, SIGNAL(matchFound(KoFindMatch)), SLOT(findMatchFound(KoFindMatch)));
0210     connect (d->findText, SIGNAL(noMatchFound()), SLOT(findNoMatchFound()));
0211     connect (KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)), SLOT(currentToolChanged(KoCanvasController*,int)));
0212 }
0213 
0214 CQTextDocumentCanvas::~CQTextDocumentCanvas()
0215 {
0216     d->part->removeMainWindow(d->part->currentMainwindow());
0217     KoToolManager::instance()->removeCanvasController(d->canvas->canvasController());
0218     delete d;
0219 }
0220 
0221 void CQTextDocumentCanvas::openFile(const QString& uri)
0222 {
0223     emit loadingBegun();
0224 
0225     KoDocumentEntry entry;
0226     QList<QPluginLoader*> pluginLoaders = KoPluginLoader::pluginLoaders("calligra/parts");
0227     Q_FOREACH (QPluginLoader *loader, pluginLoaders) {
0228         if (loader->fileName().contains(QLatin1String("wordspart"))) {
0229             entry = KoDocumentEntry(loader);
0230             pluginLoaders.removeOne(loader);
0231             break;
0232         }
0233     }
0234     qDeleteAll(pluginLoaders);
0235     if (entry.isEmpty()) {
0236         qWarning("Unable to load Words plugin, aborting!");
0237         return;
0238     }
0239 
0240     // QT5TODO: ownership of d->part unclear
0241     d->part = entry.createKoPart();
0242     KoDocument* document = d->part->document();
0243     document->setAutoSave(0);
0244     document->setCheckAutoSaveFile(false);
0245 
0246     QUrl url(uri);
0247     if (url.scheme() == "newfile") {
0248         KWDocument* doc = qobject_cast<KWDocument*>(document);
0249         doc->initEmpty();
0250         KWPageStyle style = doc->pageManager()->defaultPageStyle();
0251         Q_ASSERT(style.isValid());
0252 
0253         KoColumns columns;
0254         columns.count = url.queryItemValue("columncount").toInt();
0255         columns.gapWidth = url.queryItemValue("columngap").toDouble();
0256         style.setColumns(columns);
0257 
0258         KoPageLayout layout = style.pageLayout();
0259         layout.format = KoPageFormat::formatFromString(url.queryItemValue("pageformat"));
0260         layout.orientation = (KoPageFormat::Orientation)url.queryItemValue("pageorientation").toInt();
0261         layout.height = MM_TO_POINT(url.queryItemValue("height").toDouble());
0262         layout.width = MM_TO_POINT(url.queryItemValue("width").toDouble());
0263         if (url.queryItemValue("facingpages").toInt() == 1) {
0264             layout.bindingSide = MM_TO_POINT(url.queryItemValue("leftmargin").toDouble());
0265             layout.pageEdge = MM_TO_POINT(url.queryItemValue("rightmargin").toDouble());
0266             layout.leftMargin = layout.rightMargin = -1;
0267         }
0268         else {
0269             layout.bindingSide = layout.pageEdge = -1;
0270             layout.leftMargin = MM_TO_POINT(url.queryItemValue("leftmargin").toDouble());
0271             layout.rightMargin = MM_TO_POINT(url.queryItemValue("rightmargin").toDouble());
0272         }
0273         layout.topMargin = MM_TO_POINT(url.queryItemValue("topmargin").toDouble());
0274         layout.bottomMargin = MM_TO_POINT(url.queryItemValue("bottommargin").toDouble());
0275         style.setPageLayout(layout);
0276 
0277         doc->setUnit(KoUnit::fromSymbol(url.queryItemValue("unit")));
0278         doc->relayout();
0279     } else if (url.scheme() == "template") {
0280         qApp->setOverrideCursor(Qt::BusyCursor);
0281         // Nip away the manually added template:// bit of the uri passed from the caller
0282         bool ok = document->loadNativeFormat(uri.mid(11));
0283         document->setModified(false);
0284         document->undoStack()->clear();
0285 
0286         if (ok) {
0287             QString mimeType = QMimeDatabase().mimeTypeForUrl(url).name();
0288             // in case this is a open document template remove the -template from the end
0289             mimeType.remove( QRegExp( "-template$" ) );
0290             document->setMimeTypeAfterLoading(mimeType);
0291             document->resetURL();
0292             document->setEmpty();
0293         } else {
0294             document->showLoadingErrorDialog();
0295             document->initEmpty();
0296         }
0297         qApp->restoreOverrideCursor();
0298     } else {
0299         document->openUrl(url);
0300     }
0301 
0302     document->setModified(false);
0303     qApp->processEvents();
0304 
0305     d->canvas = dynamic_cast<KWCanvasItem*> (d->part->canvasItem(d->part->document()));
0306     createAndSetCanvasControllerOn(d->canvas);
0307     createAndSetZoomController(d->canvas);
0308     updateZoomControllerAccordingToDocument(document);
0309 
0310     d->canvas->resourceManager()->setResource(KoDocumentResourceManager::HandleRadius, 9);
0311     d->canvas->resourceManager()->setResource(KoDocumentResourceManager::GrabSensitivity, 9);
0312 
0313     QGraphicsWidget *graphicsWidget = dynamic_cast<QGraphicsWidget*>(d->canvas);
0314     graphicsWidget->setParentItem(this);
0315     graphicsWidget->installEventFilter(this);
0316     graphicsWidget->setVisible(true);
0317     graphicsWidget->setGeometry(x(), y(), width(), height());
0318 
0319     if (d->pageNumber >= 1) {
0320       gotoPage(d->pageNumber, document);
0321     }
0322 
0323     QList<QTextDocument*> texts;
0324     KoFindText::findTextInShapes(d->canvas->shapeManager()->shapes(), texts);
0325     d->findText->setDocuments(texts);
0326 
0327     d->document = qobject_cast<KWDocument*>(document);
0328     d->documentModel = new CQTextDocumentModel(this, d->document, d->canvas->shapeManager());
0329     emit documentModelChanged();
0330     emit thumbnailSizeChanged();
0331     connect(d->documentModel, SIGNAL(thumbnailSizeChanged()), SIGNAL(thumbnailSizeChanged()));
0332 
0333     d->updateLinkTargets();
0334     emit linkTargetsChanged();
0335 
0336     connect(d->canvas->shapeManager(), SIGNAL(selectionChanged()), SIGNAL(textEditorChanged()));
0337     connect(d->canvas->shapeManager(), SIGNAL(selectionChanged()), SIGNAL(shapeTransparencyChanged()));
0338 
0339     d->notes = new CQTextDocumentNotesModel(this);
0340     emit notesChanged();
0341 
0342     emit textEditorChanged();
0343     emit loadingFinished();
0344 }
0345 
0346 void CQTextDocumentCanvas::gotoPage(int pageNumber, KoDocument *document)
0347 {
0348     const KWDocument *kwDoc = static_cast<const KWDocument*>(document);
0349     KWPage currentTextDocPage = kwDoc->pageManager()->page(pageNumber);
0350 
0351     QRectF rect = d->canvas->viewConverter()->documentToView(currentTextDocPage.rect());
0352     canvasController()->pan(rect.topLeft().toPoint() - d->canvas->viewConverter()->documentToView(canvasController()->documentOffset()).toPoint());
0353     alignTopWith(rect.top());
0354     updateCanvas();
0355 }
0356 
0357 int CQTextDocumentCanvas::cameraY() const
0358 {
0359     return d->currentPoint.y();
0360 }
0361 
0362 qreal CQTextDocumentCanvas::pagePosition(int pageIndex)
0363 {
0364     KWPage page = d->document->pageManager()->page(pageIndex);
0365     // a very silly heuristic for ensuring the page number changes if we change pages.
0366     // this means we don't have to glue the canvas and controlleritem together too close,
0367     // but yes, it does look a bit silly.
0368     QTimer::singleShot(0, d->throttleTimer, SLOT(stop()));
0369     QTimer::singleShot(0, this, SIGNAL(currentPageNumberChanged()));
0370     return d->canvas->viewMode()->documentToView(page.rect().topLeft(), d->canvas->viewConverter()).y();
0371 }
0372 
0373 qreal CQTextDocumentCanvas::shapeTransparency() const
0374 {
0375     if (d->canvas && d->canvas->shapeManager()) {
0376         KoShape* shape = d->canvas->shapeManager()->selection()->firstSelectedShape();
0377         if (shape) {
0378             return shape->transparency();
0379         }
0380     }
0381     return CQCanvasBase::shapeTransparency();
0382 }
0383 
0384 void CQTextDocumentCanvas::setShapeTransparency(qreal newTransparency)
0385 {
0386     if (d->canvas && d->canvas->shapeManager()) {
0387         KoShape* shape = d->canvas->shapeManager()->selection()->firstSelectedShape();
0388         if (shape) {
0389             if (!qFuzzyCompare(1 + shape->transparency(), 1 + newTransparency)) {
0390                 shape->setTransparency(newTransparency);
0391                 CQCanvasBase::setShapeTransparency(newTransparency);
0392             }
0393         }
0394     }
0395 }
0396 
0397 QObject* CQTextDocumentCanvas::textEditor()
0398 {
0399     if (d->canvas) {
0400         if (d->textEditor) {
0401             disconnect(d->textEditor, SIGNAL(cursorPositionChanged()), this, SIGNAL(selectionChanged()));
0402         }
0403         d->textEditor = KoTextEditor::getTextEditorFromCanvas(d->canvas);
0404         if (d->textEditor) {
0405             disconnect(d->textEditor, SIGNAL(cursorPositionChanged()), this, SIGNAL(selectionChanged()));
0406         }
0407         emit selectionChanged();
0408         return d->textEditor;
0409     }
0410     return 0;
0411 }
0412 
0413 bool CQTextDocumentCanvas::hasSelection() const
0414 {
0415     if (d->textEditor) {
0416         return d->textEditor->hasSelection();
0417     }
0418     return false;
0419 }
0420 
0421 QRectF CQTextDocumentCanvas::selectionStartPos() const
0422 {
0423     if (d->textEditor) {
0424         return d->getCursorPosition(d->textEditor->selectionStart());
0425     }
0426     return QRectF(0,0,0,0);
0427 }
0428 
0429 QRectF CQTextDocumentCanvas::selectionEndPos() const
0430 {
0431     if (d->textEditor) {
0432         return d->getCursorPosition(d->textEditor->selectionEnd());
0433     }
0434     return QRectF(0,0,0,0);
0435 }
0436 
0437 QObject* CQTextDocumentCanvas::zoomAction() const
0438 {
0439     if (zoomController() && zoomController()->zoomAction()) {
0440         return zoomController()->zoomAction();
0441     }
0442     return 0;
0443 }
0444 
0445 QSizeF CQTextDocumentCanvas::thumbnailSize() const
0446 {
0447     if (d->documentModel) {
0448         return d->documentModel->thumbnailSize();
0449     }
0450     return QSizeF();
0451 }
0452 
0453 void CQTextDocumentCanvas::setThumbnailSize(const QSizeF& newSize)
0454 {
0455     if (d->documentModel) {
0456         d->documentModel->setThumbnailSize(newSize.toSize());
0457     }
0458     emit thumbnailSizeChanged();
0459 }
0460 
0461 void CQTextDocumentCanvas::deselectEverything()
0462 {
0463     KoTextEditor* editor = KoTextEditor::getTextEditorFromCanvas(d->canvas);
0464     if (editor) {
0465         editor->clearSelection();
0466     }
0467     d->canvas->shapeManager()->selection()->deselectAll();
0468     updateCanvas();
0469 }
0470 
0471 QObject* CQTextDocumentCanvas::notes() const
0472 {
0473     return d->notes;
0474 }
0475 
0476 void CQTextDocumentCanvas::addSticker(const QString& imageUrl)
0477 {
0478     QSvgRenderer renderer(QUrl(imageUrl).toLocalFile());
0479    // Prepare a QImage with desired characteristics
0480     QImage image(200, 200, QImage::Format_ARGB32);
0481     image.fill(Qt::transparent);
0482 
0483     // Get QPainter that paints to the image
0484     QPainter painter(&image);
0485     renderer.render(&painter);
0486     painter.end();
0487 
0488     KoProperties* params = new KoProperties();
0489     params->setProperty("qimage", image);
0490     KoShapeFactoryBase* factory = KoShapeRegistry::instance()->get("PictureShape");
0491     if (factory) {
0492         KoShape* shape = factory->createShape(params, d->document->resourceManager());
0493 
0494         QPointF pos = d->canvas->viewToDocument(d->canvas->documentOffset() + QPointF(d->canvas->size().width() / 2, d->canvas->size().height() / 2));
0495         KoShapeAnchor *anchor = new KoShapeAnchor(shape);
0496         anchor->setAnchorType(KoShapeAnchor::AnchorPage);
0497         anchor->setHorizontalPos(KoShapeAnchor::HFromLeft);
0498         anchor->setVerticalPos(KoShapeAnchor::VFromTop);
0499         anchor->setHorizontalRel(KoShapeAnchor::HPage);
0500         anchor->setVerticalRel(KoShapeAnchor::VPage);
0501         shape->setAnchor(anchor);
0502         shape->setPosition(pos);
0503         shape->scale(0.2, 0.2);
0504 
0505 //        KWShapeCreateCommand *cmd = new KWShapeCreateCommand(d->document, shape);
0506         KoSelection *selection = d->canvas->shapeManager()->selection();
0507         selection->deselectAll();
0508         selection->select(shape);
0509 //        d->canvas->addCommand(cmd);
0510         d->canvas->shapeManager()->addShape(shape);
0511 
0512         d->notes->addEntry("", imageUrl, "Neutral", shape);
0513     }
0514 }
0515 
0516 void CQTextDocumentCanvas::addNote(const QString& text, const QString& color, const QString& imageUrl)
0517 {
0518     QSvgRenderer renderer(QUrl(imageUrl).toLocalFile());
0519 
0520    // Prepare a QImage with desired characteritisc
0521     QImage image(400, 200, QImage::Format_ARGB32);
0522     image.fill(Qt::transparent);
0523 
0524     // Get QPainter that paints to the image
0525     QPainter painter(&image);
0526     painter.setRenderHint(QPainter::Antialiasing, true);
0527     painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
0528     painter.setRenderHint(QPainter::TextAntialiasing, true);
0529 
0530     renderer.render(&painter, image.rect());
0531 
0532     QFont font;
0533     font.setFamily("Permanent Marker");
0534     font.setStyle(QFont::StyleNormal);
0535     font.setPixelSize(40);
0536     painter.setPen(QPen(QColor(color), 0));
0537     painter.setFont(font);
0538     painter.drawText(image.rect().adjusted(10, 10, -20, -20), Qt::AlignCenter | Qt::TextWordWrap, text);
0539     painter.end();
0540 
0541     KoProperties* params = new KoProperties();
0542     params->setProperty("qimage", image);
0543     KoShapeFactoryBase* factory = KoShapeRegistry::instance()->get("PictureShape");
0544     if (factory) {
0545         KoShape* shape = factory->createShape(params, d->document->resourceManager());
0546 
0547         QPointF pos = d->canvas->viewToDocument(d->canvas->documentOffset() + QPointF(d->canvas->size().width() / 2, d->canvas->size().height() / 2));
0548         KoShapeAnchor *anchor = new KoShapeAnchor(shape);
0549         anchor->setAnchorType(KoShapeAnchor::AnchorPage);
0550         anchor->setHorizontalPos(KoShapeAnchor::HFromLeft);
0551         anchor->setVerticalPos(KoShapeAnchor::VFromTop);
0552         anchor->setHorizontalRel(KoShapeAnchor::HPage);
0553         anchor->setVerticalRel(KoShapeAnchor::VPage);
0554         shape->setAnchor(anchor);
0555         shape->setPosition(pos);
0556         shape->rotate(-15);
0557         shape->scale(0.3, 0.3);
0558 
0559 //        KWShapeCreateCommand *cmd = new KWShapeCreateCommand(d->document, shape);
0560         KoSelection *selection = d->canvas->shapeManager()->selection();
0561         selection->deselectAll();
0562         selection->select(shape);
0563 //        d->canvas->addCommand(cmd);
0564         d->canvas->shapeManager()->addShape(shape);
0565 
0566         d->notes->addEntry(text, "", color, shape);
0567     }
0568 }
0569 
0570 void CQTextDocumentCanvas::setCameraY(int cameraY)
0571 {
0572     d->currentPoint.setY (cameraY);
0573     emit cameraYChanged();
0574 }
0575 
0576 void CQTextDocumentCanvas::alignTopWith(int y)
0577 {
0578     d->currentPoint.setY(y);
0579     emit cameraYChanged();
0580 }
0581 
0582 int CQTextDocumentCanvas::currentPageNumber() const
0583 {
0584     if (d->document && !d->throttleTimer->isActive()) {
0585         // Can't use this at the moment, we sort of don't have the right one, because derp :P
0586         //d->canvas->resourceManager()->resource(KoCanvasResourceManager::CurrentPage).toInt();
0587         d->throttleTimer->start();
0588         const KWDocument *kwDoc = static_cast<const KWDocument*>(d->document);
0589         d->pageNumber = kwDoc->pageManager()->page(d->canvas->viewMode()->viewToDocument(d->canvas->documentOffset(), d->canvas->viewConverter())).pageNumber();
0590     }
0591     return d->pageNumber;
0592 }
0593 
0594 void CQTextDocumentCanvas::setCurrentPageNumber(const int& currentPageNumber)
0595 {
0596     if (d->pageNumber != currentPageNumber) {
0597         gotoPage(currentPageNumber, d->document);
0598     }
0599 }
0600 
0601 void CQTextDocumentCanvas::render(QPainter* painter, const QRectF& target)
0602 {
0603     Q_UNUSED(target)
0604     QStyleOptionGraphicsItem option;
0605     option.exposedRect = QRect(0, 0, width(), height());
0606     option.rect = option.exposedRect.toAlignedRect();
0607     d->canvas->canvasItem()->paint(painter, &option);
0608 }
0609 
0610 bool CQTextDocumentCanvas::event( QEvent* event )
0611 {
0612     switch(static_cast<int>(event->type())) {
0613         case ViewModeSwitchEvent::AboutToSwitchViewModeEvent: {
0614             ViewModeSynchronisationObject* syncObject = static_cast<ViewModeSwitchEvent*>(event)->synchronisationObject();
0615 
0616             if (d->canvas) {
0617                 syncObject->documentOffset = d->canvas->documentOffset();
0618                 syncObject->zoomLevel = zoomController()->zoomAction()->effectiveZoom();
0619                 syncObject->activeToolId = KoToolManager::instance()->activeToolId();
0620                 syncObject->shapes = d->canvas->shapeManager()->shapes();
0621                 syncObject->initialized = true;
0622             }
0623 
0624             return true;
0625         }
0626         case ViewModeSwitchEvent::SwitchedToTouchModeEvent: {
0627             ViewModeSynchronisationObject* syncObject = static_cast<ViewModeSwitchEvent*>(event)->synchronisationObject();
0628 
0629             if (d->canvas && syncObject->initialized) {
0630                 d->canvas->shapeManager()->setShapes(syncObject->shapes);
0631 
0632                 KoToolManager::instance()->switchToolRequested("PageToolFactory_ID");
0633                 qApp->processEvents();
0634 
0635                 zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, syncObject->zoomLevel);
0636 
0637                 qApp->processEvents();
0638                 emit positionShouldChange(syncObject->documentOffset);
0639             }
0640 
0641             return true;
0642         }
0643 //         case KisTabletEvent::TabletPressEx:
0644 //         case KisTabletEvent::TabletReleaseEx:
0645 //             emit interactionStarted();
0646 //             d->canvas->inputManager()->eventFilter(this, event);
0647 //             return true;
0648 //         case KisTabletEvent::TabletMoveEx:
0649 //             d->tabletEventCount++; //Note that this will wraparound at some point; This is intentional.
0650 // #ifdef Q_OS_X11
0651 //             if (d->tabletEventCount % 2 == 0)
0652 // #endif
0653 //                 d->canvas->inputManager()->eventFilter(this, event);
0654 //             return true;
0655         default:
0656             break;
0657     }
0658     return QDeclarativeItem::event( event );
0659 }
0660 
0661 void CQTextDocumentCanvas::currentToolChanged(KoCanvasController* controller, int uniqueToolId)
0662 {
0663     Q_UNUSED(controller)
0664     Q_UNUSED(uniqueToolId)
0665     d->currentTool = qobject_cast<KoToolBase*>(KoToolManager::instance()->toolById(d->canvas, KoToolManager::instance()->activeToolId()));
0666 }
0667 
0668 void CQTextDocumentCanvas::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* e)
0669 {
0670     QMouseEvent me(e->type(), e->pos().toPoint(), e->button(), e->buttons(), e->modifiers());
0671     KoPointerEvent pe(&me, d->canvas->viewToDocument(e->pos() + d->canvas->documentOffset()));
0672     d->currentTool->mouseDoubleClickEvent(&pe);
0673     updateCanvas();
0674     emit selectionChanged();
0675     e->setAccepted(me.isAccepted());
0676 }
0677 
0678 void CQTextDocumentCanvas::mouseMoveEvent(QGraphicsSceneMouseEvent* e)
0679 {
0680     QMouseEvent me(e->type(), e->pos().toPoint(), e->button(), e->buttons(), e->modifiers());
0681     KoPointerEvent pe(&me, d->canvas->viewToDocument(e->pos() + d->canvas->documentOffset()));
0682     d->currentTool->mouseMoveEvent(&pe);
0683     updateCanvas();
0684     emit selectionChanged();
0685     e->setAccepted(me.isAccepted());
0686 }
0687 
0688 void CQTextDocumentCanvas::mousePressEvent(QGraphicsSceneMouseEvent* e)
0689 {
0690     QMouseEvent me(e->type(), e->pos().toPoint(), e->button(), e->buttons(), e->modifiers());
0691     KoPointerEvent pe(&me, d->canvas->viewToDocument(e->pos() + d->canvas->documentOffset()));
0692     d->currentTool->mousePressEvent(&pe);
0693     updateCanvas();
0694     emit selectionChanged();
0695     e->setAccepted(me.isAccepted());
0696 }
0697 
0698 void CQTextDocumentCanvas::mouseReleaseEvent(QGraphicsSceneMouseEvent* e)
0699 {
0700     QMouseEvent me(e->type(), e->pos().toPoint(), e->button(), e->buttons(), e->modifiers());
0701     KoPointerEvent pe(&me, d->canvas->viewToDocument(e->pos() + d->canvas->documentOffset()));
0702     d->currentTool->mouseReleaseEvent(&pe);
0703     updateCanvas();
0704     emit selectionChanged();
0705     e->setAccepted(me.isAccepted());
0706 }
0707 
0708 void CQTextDocumentCanvas::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry)
0709 {
0710     if (d->canvas) {
0711         QGraphicsWidget *widget = dynamic_cast<QGraphicsWidget*>(d->canvas);
0712         if (widget) {
0713             widget->setGeometry(newGeometry);
0714         }
0715     }
0716     QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
0717 }
0718 
0719 void CQTextDocumentCanvas::createAndSetCanvasControllerOn(KoCanvasBase* canvas)
0720 {
0721     //TODO: pass a proper action collection
0722     CQCanvasController *controller = new CQCanvasController(new KActionCollection(this));
0723     setCanvasController(controller);
0724     connect (controller, SIGNAL(documentSizeChanged(QSize)), SLOT(updateDocumentSize(QSize)));
0725     controller->setCanvas(canvas);
0726     KoToolManager::instance()->addController (controller);
0727 }
0728 
0729 void CQTextDocumentCanvas::createAndSetZoomController(KoCanvasBase* canvas)
0730 {
0731     KoZoomHandler* zoomHandler = static_cast<KoZoomHandler*> (canvas->viewConverter());
0732     setZoomController(new KoZoomController(canvasController(), zoomHandler, new KActionCollection(this)));
0733 
0734     KWCanvasItem *kwCanvasItem = static_cast<KWCanvasItem*>(canvas);
0735     connect (kwCanvasItem, SIGNAL(documentSize(QSizeF)), zoomController(), SLOT(setDocumentSize(QSizeF)));
0736     connect (canvasController()->proxyObject, SIGNAL(moveDocumentOffset(QPoint)), SIGNAL(currentPageNumberChanged()));
0737     connect (canvasController()->proxyObject, SIGNAL(moveDocumentOffset(QPoint)), kwCanvasItem, SLOT(setDocumentOffset(QPoint)));
0738     connect (zoomController(), SIGNAL(zoomChanged(KoZoomMode::Mode,qreal)), SIGNAL(zoomActionChanged()));
0739     kwCanvasItem->updateSize();
0740     emit zoomActionChanged();
0741 }
0742 
0743 void CQTextDocumentCanvas::updateZoomControllerAccordingToDocument(const KoDocument* document)
0744 {
0745     const KWDocument *kwDoc = static_cast<const KWDocument*>(document);
0746     zoomController()->setPageSize (kwDoc->pageManager()->begin().rect().size());
0747 }
0748 
0749 QString CQTextDocumentCanvas::searchTerm() const
0750 {
0751     return d->searchTerm;
0752 }
0753 
0754 void CQTextDocumentCanvas::setSearchTerm(const QString& term)
0755 {
0756     d->searchTerm = term;
0757     if (!term.isEmpty()) {
0758         d->findText->find(term);
0759     }
0760     emit searchTermChanged();
0761 }
0762 
0763 void CQTextDocumentCanvas::findMatchFound(const KoFindMatch &match)
0764 {
0765     QTextCursor cursor = match.location().value<QTextCursor>();
0766     d->canvas->canvasItem()->update();
0767 
0768     d->canvas->resourceManager()->setResource (KoText::CurrentTextAnchor, cursor.anchor());
0769     d->canvas->resourceManager()->setResource (KoText::CurrentTextPosition, cursor.position());
0770 }
0771 
0772 void CQTextDocumentCanvas::findNoMatchFound()
0773 {
0774     qDebug() << "Match for " << d->searchTerm << " not found";
0775 }
0776 
0777 void CQTextDocumentCanvas::updateCanvas()
0778 {
0779     KWCanvasItem* kwCanvasItem = dynamic_cast<KWCanvasItem*> (d->canvas);
0780     kwCanvasItem->update();
0781 }
0782 
0783 void CQTextDocumentCanvas::findNext()
0784 {
0785     d->findText->findNext();
0786 }
0787 
0788 void CQTextDocumentCanvas::findPrevious()
0789 {
0790     d->findText->findPrevious();
0791 }
0792 
0793 QObject* CQTextDocumentCanvas::documentModel() const
0794 {
0795     return d->documentModel;
0796 }
0797 
0798 KWDocument* CQTextDocumentCanvas::document() const
0799 {
0800     return d->document;
0801 }
0802 
0803 QObject* CQTextDocumentCanvas::doc() const
0804 {
0805     return d->document;
0806 }
0807 
0808 QObject* CQTextDocumentCanvas::part() const
0809 {
0810     return d->part;
0811 }
0812 
0813 QObjectList CQTextDocumentCanvas::linkTargets() const
0814 {
0815     return d->linkTargets;
0816 }
0817 
0818 QSize CQTextDocumentCanvas::documentSize() const
0819 {
0820     return d->documentSize;
0821 }
0822 
0823 void CQTextDocumentCanvas::updateDocumentSize(const QSize& size)
0824 {
0825     d->documentSize = size;
0826     emit documentSizeChanged();
0827 }