File indexing completed on 2024-05-05 17:09:08

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