Warning, file /office/calligra/qtquick/CQSpreadsheetCanvas.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  * This file is part of the KDE project
0003  *
0004  * SPDX-FileCopyrightText: 2013 Shantanu Tushar <shantanu@kde.org>
0005  * SPDX-FileCopyrightText: 2013 Arjen Hiemstra <ahiemstra@heimr.nl>
0006  *
0007  * SPDX-License-Identifier: LGPL-2.0-or-later
0008  *
0009  */
0010 
0011 #include "CQSpreadsheetCanvas.h"
0012 
0013 #include <QStyleOptionGraphicsItem>
0014 
0015 #include "CQCanvasController.h"
0016 #include <KoPart.h>
0017 #include <KoFindText.h>
0018 #include <KoCanvasBase.h>
0019 #include <KoToolManager.h>
0020 #include <KoZoomController.h>
0021 #include <KoZoomHandler.h>
0022 #include <sheets/part/Doc.h>
0023 #include <sheets/part/CanvasItem.h>
0024 #include <sheets/Map.h>
0025 #include <sheets/Sheet.h>
0026 #include <KoShape.h>
0027 #include <KActionCollection>
0028 #include <QGraphicsWidget>
0029 #include <QTextFrame>
0030 #include <QTextLayout>
0031 
0032 class CQSpreadsheetCanvas::Private
0033 {
0034 public:
0035     Private() : canvas(0), document(0), currentSheet(0) { }
0036 
0037     Calligra::Sheets::CanvasItem* canvas;
0038     Calligra::Sheets::Doc * document;
0039 
0040     int currentSheet;
0041     QObjectList linkTargets;
0042 
0043     void updateLinkTargets()
0044     {
0045         qDeleteAll(linkTargets);
0046         linkTargets.clear();
0047 
0048         if (!canvas) {
0049             return;
0050         }
0051         foreach(const KoShape* shape, canvas->activeSheet()->shapes()) {
0052             if (!shape->hyperLink().isEmpty()) {
0053                 QObject * obj = new QObject(canvas);
0054                 obj->setProperty("linkRect", shape->boundingRect());
0055                 obj->setProperty("linkTarget", QUrl(shape->hyperLink()));
0056                 linkTargets.append(obj);
0057             }
0058         }
0059 
0060         QList<QTextDocument*> texts;
0061         KoFindText::findTextInShapes(canvas->activeSheet()->shapes(), texts);
0062         foreach(QTextDocument* text, texts) {
0063             QTextBlock block = text->rootFrame()->firstCursorPosition().block();
0064             for (; block.isValid(); block = block.next()) {
0065                 block.begin();
0066                 QTextBlock::iterator it;
0067                 for (it = block.begin(); !(it.atEnd()); ++it) {
0068                     QTextFragment fragment = it.fragment();
0069                     if (fragment.isValid()) {
0070                         QTextCharFormat format = fragment.charFormat();
0071                         if (format.isAnchor()) {
0072                             // This is an anchor, store target and position...
0073                             QObject * obj = new QObject(canvas);
0074                             QRectF rect = getFragmentPosition(block, fragment);
0075                             obj->setProperty("linkRect", canvas->viewConverter()->documentToView(rect));
0076                             obj->setProperty("linkTarget", QUrl(format.anchorHref()));
0077                             linkTargets.append(obj);
0078                         }
0079                     }
0080                 }
0081             }
0082         }
0083     }
0084 
0085     QRectF getFragmentPosition(const QTextBlock& block, const QTextFragment& fragment)
0086     {
0087         // TODO this only produces a position for the first part, if the link spans more than one line...
0088         // Need to sort that somehow, unfortunately probably by slapping this code into the above function.
0089         // For now leave it like this, more important things are needed.
0090         QTextLayout* layout = block.layout();
0091         QTextLine line = layout->lineForTextPosition(fragment.position() - block.position());
0092         if (!line.isValid()) {
0093             // fragment has no valid position and consequently no line...
0094             return QRectF();
0095         }
0096         qreal top = line.position().y();
0097         qreal bottom = line.position().y() + line.height();
0098         qreal left = line.cursorToX(fragment.position() - block.position());
0099         qreal right = line.cursorToX((fragment.position() - block.position()) + fragment.length());
0100         QRectF fragmentPosition(QPointF(top, left), QPointF(bottom, right));
0101         return fragmentPosition.adjusted(layout->position().x(), layout->position().y(), 0, 0);
0102     }
0103 };
0104 
0105 CQSpreadsheetCanvas::CQSpreadsheetCanvas(QDeclarativeItem* parent)
0106     : CQCanvasBase(parent), d(new Private)
0107 {
0108 
0109 }
0110 
0111 CQSpreadsheetCanvas::~CQSpreadsheetCanvas()
0112 {
0113     delete d;
0114 }
0115 
0116 int CQSpreadsheetCanvas::currentSheet() const
0117 {
0118     return d->currentSheet;
0119 }
0120 
0121 Calligra::Sheets::Map* CQSpreadsheetCanvas::documentMap() const
0122 {
0123     return d->document->map();
0124 }
0125 
0126 QObjectList CQSpreadsheetCanvas::linkTargets() const
0127 {
0128     return d->linkTargets;
0129 }
0130 
0131 void CQSpreadsheetCanvas::setCurrentSheet(int sheet)
0132 {
0133     sheet = qBound(0, sheet, d->document->map()->count() - 1);
0134     if (sheet != d->currentSheet) {
0135         d->currentSheet = sheet;
0136         d->canvas->setActiveSheet(d->document->map()->sheet(d->currentSheet));
0137         emit currentSheetChanged();
0138         d->updateLinkTargets();
0139         emit linkTargetsChanged();
0140     }
0141 }
0142 
0143 void CQSpreadsheetCanvas::render(QPainter* painter, const QRectF& target)
0144 {
0145     QStyleOptionGraphicsItem option;
0146     option.exposedRect = target;
0147     option.rect = target.toAlignedRect();
0148     d->canvas->canvasItem()->paint(painter, &option);
0149 }
0150 
0151 void CQSpreadsheetCanvas::openFile(const QString& uri)
0152 {
0153     KService::Ptr service = KService::serviceByDesktopName("sheetspart");
0154     if (service.isNull()) {
0155         qWarning("Unable to load Sheets plugin, aborting!");
0156         return;
0157     }
0158 
0159     KoPart* part = service->createInstance<KoPart>();
0160     d->document = static_cast<Calligra::Sheets::Doc*> (part->document());
0161     d->document->setAutoSave(0);
0162     d->document->setCheckAutoSaveFile(false);
0163     d->document->openUrl (QUrl (uri));
0164 
0165     d->canvas = dynamic_cast<Calligra::Sheets::CanvasItem*> (part->canvasItem(part->document()));
0166     createAndSetCanvasControllerOn(d->canvas);
0167     createAndSetZoomController(d->canvas);
0168 
0169     d->canvas->setParentItem(this);
0170     d->canvas->installEventFilter(this);
0171     d->canvas->setVisible(true);
0172     d->canvas->setGeometry(x(), y(), width(), height());
0173 
0174     d->updateLinkTargets();
0175     emit linkTargetsChanged();
0176 
0177     Calligra::Sheets::Sheet *sheet = d->document->map()->sheet(0);
0178     if (sheet) {
0179         updateDocumentSize(sheet->documentSize().toSize());
0180     }
0181 }
0182 
0183 void CQSpreadsheetCanvas::createAndSetCanvasControllerOn(KoCanvasBase* canvas)
0184 {
0185     //TODO: pass a proper action collection
0186     CQCanvasController *controller = new CQCanvasController(new KActionCollection(this));
0187     setCanvasController(controller);
0188     controller->setCanvas(canvas);
0189     KoToolManager::instance()->addController (controller);
0190 }
0191 
0192 void CQSpreadsheetCanvas::createAndSetZoomController(KoCanvasBase* canvas)
0193 {
0194     KoZoomHandler* zoomHandler = static_cast<KoZoomHandler*> (canvas->viewConverter());
0195     setZoomController(new KoZoomController(canvasController(),
0196                                            zoomHandler,
0197                                            new KActionCollection(this)));
0198 
0199     Calligra::Sheets::CanvasItem *canvasItem = dynamic_cast<Calligra::Sheets::CanvasItem*> (canvas);
0200     // update the canvas whenever we scroll, the canvas controller must emit this signal on scrolling/panning
0201     connect (canvasController()->proxyObject, SIGNAL(moveDocumentOffset(QPoint)), canvasItem, SLOT(setDocumentOffset(QPoint)));
0202     // whenever the size of the document viewed in the canvas changes, inform the zoom controller
0203     connect(canvasItem, SIGNAL(documentSizeChanged(QSize)), SLOT(updateDocumentSize(QSize)));
0204     canvasItem->update();
0205 }
0206 
0207 void CQSpreadsheetCanvas::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry)
0208 {
0209     if (d->canvas) {
0210         d->canvas->setGeometry(newGeometry);
0211     }
0212     QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
0213 }
0214 
0215 void CQSpreadsheetCanvas::updateDocumentSize(const QSize& size)
0216 {
0217     zoomController()->setDocumentSize(size, false);
0218 }