diff --git a/components/Document.cpp b/components/Document.cpp index 21acb66afea..e29b871db55 100644 --- a/components/Document.cpp +++ b/components/Document.cpp @@ -1,273 +1,258 @@ /* * This file is part of the KDE project * * Copyright (C) 2013 Arjen Hiemstra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "Document.h" #include #include #include #include #include #include #include #include #include #include #include "impl/TextDocumentImpl.h" #include "impl/SpreadsheetImpl.h" #include "impl/PresentationImpl.h" using namespace Calligra::Components; class Document::Private { public: - Private(Document* qq) : q{qq}, impl{nullptr}, status{DocumentStatus::Unloaded}, textEditor{nullptr} + Private(Document* qq) : q{qq}, impl{nullptr}, status{DocumentStatus::Unloaded} { } void updateImpl(); Document* q; QUrl source; DocumentImpl* impl; DocumentStatus::Status status; - KoTextEditor* textEditor; }; Document::Document(QObject* parent) : QObject{parent}, d{new Private{this}} { } Document::~Document() { delete d; } QUrl Document::source() const { return d->source; } void Document::setSource(const QUrl& value) { if(value != d->source) { d->source = value; emit sourceChanged(); d->status = DocumentStatus::Loading; emit statusChanged(); d->updateImpl(); emit documentTypeChanged(); if(d->impl) { if(d->impl->load(d->source)) { d->status = DocumentStatus::Loaded; connect(d->impl->canvasController()->canvas()->shapeManager(), SIGNAL(selectionChanged()), SIGNAL(textEditorChanged())); } else { d->status = DocumentStatus::Failed; } } else { d->status = DocumentStatus::Unloaded; } emit indexCountChanged(); emit statusChanged(); } } DocumentType::Type Document::documentType() const { if(d->impl) { return d->impl->documentType(); } return DocumentType::Unknown; } DocumentStatus::Status Document::status() const { return d->status; } QSize Document::documentSize() const { if(d->impl) { return d->impl->documentSize(); } return QSize{}; } int Document::currentIndex() const { if(d->impl) { return d->impl->currentIndex(); } return -1; } void Document::setCurrentIndex(int newValue) { if(d->impl) { d->impl->setCurrentIndex(newValue); } } int Document::indexCount() const { if(d->impl) { return d->impl->indexCount(); } return 0; } KoFindBase* Document::finder() const { if(d->impl) { return d->impl->finder(); } return nullptr; } QGraphicsWidget* Document::canvas() const { if(d->impl) { return d->impl->canvas(); } return nullptr; } KoCanvasController* Document::canvasController() const { if(d->impl) { return d->impl->canvasController(); } return nullptr; } KoZoomController* Document::zoomController() const { if(d->impl) { return d->impl->zoomController(); } return nullptr; } QObject* Document::part() const { return d->impl->part(); } QObject* Document::document() const { return koDocument(); } KoDocument* Document::koDocument() const { if(d->impl) { return d->impl->koDocument(); } return nullptr; } QUrl Document::urlAtPoint(const QPoint& point) { if(d->impl) return d->impl->urlAtPoint(point); return QUrl(); } QObject * Document::textEditor() { if (d->impl && d->impl->canvasController()) { - if (d->textEditor) { - disconnect(d->textEditor, SIGNAL(cursorPositionChanged()), this, SIGNAL(selectionChanged())); - } - d->textEditor = KoTextEditor::getTextEditorFromCanvas(d->impl->canvasController()->canvas()); - if (d->textEditor) { - disconnect(d->textEditor, SIGNAL(cursorPositionChanged()), this, SIGNAL(selectionChanged())); - } -// emit selectionChanged(); - return d->textEditor; + return KoTextEditor::getTextEditorFromCanvas(d->impl->canvasController()->canvas()); } return 0; } void Document::deselectEverything() { KoTextEditor* editor = KoTextEditor::getTextEditorFromCanvas(d->impl->canvasController()->canvas()); if (editor) { editor->clearSelection(); } d->impl->canvasController()->canvas()->shapeManager()->selection()->deselectAll(); emit requestViewUpdate(); } void Document::Private::updateImpl() { - if(impl) { - delete impl; - } - - if(!source.isEmpty()) { - auto type = Global::documentType(source); - switch(type) { - case DocumentType::TextDocument: - impl = new TextDocumentImpl{q}; - break; - case DocumentType::Spreadsheet: - impl = new SpreadsheetImpl{q}; - break; - case DocumentType::Presentation: - impl = new PresentationImpl{q}; - break; - default: - impl = nullptr; - break; - } - } else { - impl = nullptr; + delete impl; + impl = nullptr; + + auto type = Global::documentType(source); + switch(type) { + case DocumentType::TextDocument: + impl = new TextDocumentImpl{q}; + break; + case DocumentType::Spreadsheet: + impl = new SpreadsheetImpl{q}; + break; + case DocumentType::Presentation: + impl = new PresentationImpl{q}; + break; + default: + break; } if(impl) { connect(impl, &DocumentImpl::documentSizeChanged, q, &Document::documentSizeChanged); connect(impl, &DocumentImpl::currentIndexChanged, q, &Document::currentIndexChanged); connect(impl, &DocumentImpl::requestViewUpdate, q, &Document::requestViewUpdate); } emit q->documentChanged(); } #include "moc_Document.cpp" diff --git a/components/Document.h b/components/Document.h index 832252546fb..1f5a005ea29 100644 --- a/components/Document.h +++ b/components/Document.h @@ -1,173 +1,173 @@ /* * This file is part of the KDE project * * Copyright (C) 2013 Arjen Hiemstra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #ifndef CALLIGRA_COMPONENTS_DOCUMENT_H #define CALLIGRA_COMPONENTS_DOCUMENT_H #include #include "Global.h" class KoDocument; class KoZoomController; class KoCanvasController; class QGraphicsWidget; class KoFindBase; namespace Calligra { namespace Components { /** * \brief The Document object provides a loader for Calligra Documents. * */ class Document : public QObject { Q_OBJECT Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) Q_PROPERTY(QObject* document READ document NOTIFY documentChanged) Q_PROPERTY(QObject* part READ part NOTIFY documentChanged) Q_PROPERTY(Calligra::Components::DocumentType::Type documentType READ documentType NOTIFY documentTypeChanged) Q_PROPERTY(Calligra::Components::DocumentStatus::Status status READ status NOTIFY statusChanged) Q_PROPERTY(QSize documentSize READ documentSize NOTIFY documentSizeChanged) /** * \property currentIndex * \brief The current visible 'index', i.e. page, sheet or slide. * * Due to the abstraction of the difference between the three document types * we need some way to handle the "current visible item" based on an arbitrary * number. * * \default -1 if #source is not set or failed to load. 0 otherwise. * \get currentIndex() const * \set setcurrentIndex() * \notify currentIndexChanged() * * \todo This should probably be a property of View, but since DocumentImpl currently * creates the canvas it is currently pretty much a document property. */ Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) /** * \property indexCount * \brief The number of "indexes" in the document, i.e. pages, slides, etc. * * \default 0 * \get indexCount() const * \notify indexCountChanged() */ Q_PROPERTY(int indexCount READ indexCount NOTIFY indexCountChanged) /** * \property textEditor - * \brief The instance of QTextEditor for the currently selected object in the document, or null + * \brief The instance of KoTextEditor for the currently selected object in the document, or null * * \default null * \get textEditor() const * \notify textEditorChanged() */ Q_PROPERTY(QObject* textEditor READ textEditor NOTIFY textEditorChanged) public: explicit Document(QObject* parent = 0); ~Document(); QUrl source() const; void setSource(const QUrl& value); DocumentType::Type documentType() const; DocumentStatus::Status status() const; QSize documentSize() const; QObject* document() const; virtual QObject* part() const; /** * Getter for property #currentIndex. */ int currentIndex() const; /** * Setter for property #currentIndex. */ void setCurrentIndex(int newValue); /** * Getter for property #indexCount. */ int indexCount() const; /** * \internal * These methods are used internally by the components and not exposed * to QML. * @{ */ KoFindBase* finder() const; QGraphicsWidget* canvas() const; KoCanvasController* canvasController() const; KoZoomController* zoomController() const; KoDocument* koDocument() const; /** * \return The url of the link at point or an empty url if there is no link at point. */ virtual QUrl urlAtPoint( const QPoint& point ); QObject* textEditor(); // Deselects any text selection present in the document, and deselects all shapes // This is highly useful, as it makes navigation prettier. Q_INVOKABLE void deselectEverything(); /** * @} */ Q_SIGNALS: void sourceChanged(); void statusChanged(); void documentChanged(); void documentSizeChanged(); void documentTypeChanged(); void textEditorChanged(); /** * Notify signal for property #currentIndex. */ void currentIndexChanged(); /** * Notify signal for property #indexCount */ void indexCountChanged(); /** * \brief Emitted whenever the backend wants to update the view. */ void requestViewUpdate(); private: class Private; Private* const d; }; } // Namespace Components } // Namespace Calligra Q_DECLARE_METATYPE(Calligra::Components::Document*) #endif // CALLIGRA_COMPONENTS_DOCUMENT_H diff --git a/components/Global.cpp b/components/Global.cpp index 8c51ab3dd28..4311f8d3b4b 100644 --- a/components/Global.cpp +++ b/components/Global.cpp @@ -1,102 +1,102 @@ /* * This file is part of the KDE project * * Copyright (C) 2013 Arjen Hiemstra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "Global.h" #include #include #include #include #include #include // For the mimetype names #include #include #include using namespace Calligra::Components; static const QStringList staticTextTypes{ "application/pdf" }; Calligra::Components::Global::Global(QObject* parent) : QObject{parent} { } -void Global::loadPlugins() -{ - -} - int Global::documentType(const QUrl& document) { int result = DocumentType::Unknown; + + if (!document.isValid()) { + return result; + } + const QUrlQuery query(document); // First, check if the URL gives us specific information on this topic (such as asking for a new file) if(query.hasQueryItem("mimetype")) { QString mime = query.queryItemValue("mimetype"); if(mime == WORDS_MIME_TYPE) { result = DocumentType::TextDocument; } else if(mime == SHEETS_MIME_TYPE) { result = DocumentType::Spreadsheet; } else if(mime == STAGE_MIME_TYPE) { result = DocumentType::Presentation; } } else { QMimeType mime = QMimeDatabase{}.mimeTypeForUrl(document); // TODO: see if KoPluginLoader could provide this info via some metadata query instead QList plugins = KoPluginLoader::pluginLoaders(QStringLiteral("calligra/parts"), mime.name()); for (int i = 0; i < plugins.count(); i++) { QPluginLoader* loader = plugins.at(i); if(loader->fileName().contains("words")) { result = DocumentType::TextDocument; break; } else if(loader->fileName().contains("sheets")) { result = DocumentType::Spreadsheet; break; } else if(loader->fileName().contains("stage")) { result = DocumentType::Presentation; break; } } // cleanup qDeleteAll(plugins); // Since we don't actually have a Calligra plugin that handles these... if ((result == DocumentType::Unknown) && staticTextTypes.contains(mime.name())) { result = DocumentType::StaticTextDocument; } } return result; } diff --git a/components/Global.h b/components/Global.h index 9352cd5b629..96d9a0c2ee7 100644 --- a/components/Global.h +++ b/components/Global.h @@ -1,51 +1,50 @@ /* * This file is part of the KDE project * * Copyright (C) 2013 Arjen Hiemstra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #ifndef CALLIGRA_COMPONENTS_GLOBAL_H #define CALLIGRA_COMPONENTS_GLOBAL_H #include #include "Enums.h" namespace Calligra { namespace Components { /** * \brief Provides a singleton wrapper for global Calligra functionality. * */ class Global : public QObject { Q_OBJECT public: explicit Global(QObject* parent = 0); - static Q_INVOKABLE void loadPlugins(); static Q_INVOKABLE int documentType(const QUrl& document); }; } // Namespace Components } // Namespace Calligra #endif // CALLIGRA_COMPONENTS_GLOBAL_H diff --git a/components/ImageDataItem.cpp b/components/ImageDataItem.cpp index 9e49905ee5a..895a8d7ebc5 100644 --- a/components/ImageDataItem.cpp +++ b/components/ImageDataItem.cpp @@ -1,89 +1,87 @@ /* * This file is part of the KDE project * * Copyright (C) 2013 Arjen Hiemstra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "ImageDataItem.h" #include #include #include using namespace Calligra::Components; class ImageDataItem::Private { public: Private() { } QImage data; }; ImageDataItem::ImageDataItem(QQuickItem* parent) : QQuickItem{parent}, d{new Private} { setFlag(QQuickItem::ItemHasContents, true); } ImageDataItem::~ImageDataItem() { delete d; } QImage ImageDataItem::data() const { return d->data; } void ImageDataItem::setData(const QImage& newValue) { if(newValue != d->data) { d->data = newValue; setImplicitWidth(d->data.width()); setImplicitHeight(d->data.height()); update(); emit dataChanged(); } } QSGNode* ImageDataItem::updatePaintNode(QSGNode* node, QQuickItem::UpdatePaintNodeData*) { if(d->data.isNull()) { return node; } float w = widthValid() ? width() : d->data.width(); float h = heightValid() ? height() : d->data.height(); auto texNode = static_cast(node); if(!texNode) { texNode = new QSGSimpleTextureNode{}; } texNode->setRect(0, 0, w, h); auto texture = window()->createTextureFromImage(d->data); - if(texNode->texture()) { - delete texNode->texture(); - } + delete texNode->texture(); texNode->setTexture(texture); return texNode; } diff --git a/components/impl/PresentationImpl.cpp b/components/impl/PresentationImpl.cpp index 942b0ba469a..714740f367c 100644 --- a/components/impl/PresentationImpl.cpp +++ b/components/impl/PresentationImpl.cpp @@ -1,264 +1,262 @@ /* * This file is part of the KDE project * * Copyright (C) 2013 Arjen Hiemstra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "PresentationImpl.h" #include "PresentationKoPAView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Calligra::Components; class PresentationImpl::Private { public: Private() : part{nullptr}, document{nullptr} { } KPrPart* part; KPrDocument* document; PresentationKoPAView* koPaView; QList< QPair< QRectF, QUrl > > links; QList deepShapeFind(QList shapes) { QList allShapes; foreach(KoShape* shape, shapes) { allShapes.append(shape); KoShapeContainer *container = dynamic_cast(shape); if(container) { allShapes.append(deepShapeFind(container->shapes())); } } return allShapes; } void updateLinkTargets() { links.clear(); if(!koPaView || !koPaView->activePage()) return; foreach(const KoShape* shape, koPaView->activePage()->shapes()) { if(!shape->hyperLink().isEmpty()) { QRectF rect = shape->boundingRect(); while(KoShapeContainer* parent = shape->parent()) { rect.translate(parent->position()); } links.append(QPair(rect, QUrl(shape->hyperLink()))); } } QList texts; KoFindText::findTextInShapes(koPaView->activePage()->shapes(), texts); QList allShapes = deepShapeFind(koPaView->activePage()->shapes()); foreach(QTextDocument* text, texts) { QTextBlock block = text->rootFrame()->firstCursorPosition().block(); for (; block.isValid(); block = block.next()) { block.begin(); QTextBlock::iterator it; for (it = block.begin(); !(it.atEnd()); ++it) { QTextFragment fragment = it.fragment(); if (fragment.isValid()) { QTextCharFormat format = fragment.charFormat(); if(format.isAnchor()) { // This is an anchor, store target and position... QRectF rect = getFragmentPosition(block, fragment); foreach(KoShape* shape, allShapes) { KoTextShapeData *shapeData = dynamic_cast(shape->userData()); if (!shapeData) continue; if(shapeData->document() == text) { rect.translate(shape->position()); while(KoShapeContainer* parent = shape->parent()) { rect.translate(parent->position()); } break; } } links.append(QPair(koPaView->kopaCanvas()->viewConverter()->documentToView(rect), QUrl(format.anchorHref()))); } } } } } qDebug() << "Discovered the following links in the slide:" << links; } QRectF getFragmentPosition(QTextBlock block, QTextFragment fragment) { // TODO this only produces a position for the first part, if the link spans more than one line... // Need to sort that somehow, unfortunately probably by slapping this code into the above function. // For now leave it like this, more important things are needed. QTextLayout* layout = block.layout(); QTextLine line = layout->lineForTextPosition(fragment.position() - block.position()); if(!line.isValid()) { // fragment has no valid position and consequently no line... return QRectF(); } qreal top = line.position().y() + (line.height() / 2); qreal bottom = top + line.height(); qreal left = line.cursorToX(fragment.position() - block.position()); qreal right = line.cursorToX((fragment.position() - block.position()) + fragment.length()); QRectF fragmentPosition(QPointF(left, top), QPointF(right, bottom)); return fragmentPosition.adjusted(layout->position().x(), layout->position().y(), 0, 0); } static const float wiggleFactor; }; const float Calligra::Components::PresentationImpl::Private::wiggleFactor{ 4.f }; PresentationImpl::PresentationImpl(QObject* parent) : DocumentImpl{parent}, d{new Private} { setDocumentType(DocumentType::Presentation); } PresentationImpl::~PresentationImpl() { delete d; } bool PresentationImpl::load(const QUrl& url) { - if(d->part) { - delete d->part; - delete d->document; - } + delete d->part; + delete d->document; d->part = new KPrPart{this}; d->document = new KPrDocument{d->part}; setKoDocument(d->document); d->part->setDocument(d->document); bool retval = false; if (url.scheme() == QStringLiteral("template")) { bool ok = d->document->loadNativeFormat(url.toString().mid(11)); d->document->setModified(false); d->document->undoStack()->clear(); if (ok) { QString mimeType = QMimeDatabase().mimeTypeForUrl(url).name(); // in case this is a open document template remove the -template from the end mimeType.remove( QRegExp( "-template$" ) ); d->document->setMimeTypeAfterLoading(mimeType); d->document->resetURL(); d->document->setEmpty(); } else { // some kind of error reporting thing here... failed to load template, tell the user // why their canvas is so terribly empty. d->document->initEmpty(); } d->document->setModified(true); retval = true; } else { retval = d->document->openUrl(url); } auto canvas = static_cast(d->part->canvasItem(d->document)); createAndSetCanvasController(canvas); d->koPaView = new PresentationKoPAView(canvasController(), canvas, d->document); canvas->setView(d->koPaView); createAndSetZoomController(canvas); d->koPaView->setZoomController(zoomController()); d->koPaView->connectToZoomController(); KoPAPageBase* page = d->document->pageByIndex(0, false); if(page) { d->koPaView->doUpdateActivePage(page); } d->updateLinkTargets(); setCanvas(canvas); return retval; } int PresentationImpl::currentIndex() { if (d->document && d->koPaView && d->koPaView->activePage()) { return d->document->pageIndex(d->koPaView->activePage()); } else { return -1; } } void PresentationImpl::setCurrentIndex(int newValue) { if(newValue != currentIndex()) { d->koPaView->doUpdateActivePage(d->document->pageByIndex(newValue, false)); d->updateLinkTargets(); emit requestViewUpdate(); emit currentIndexChanged(); } } int PresentationImpl::indexCount() const { return d->document->pageCount(); } QUrl PresentationImpl::urlAtPoint(QPoint point) { for( const QPair< QRectF, QUrl >& link : d->links ) { QRectF hitTarget{ link.first.x() - Private::wiggleFactor, link.first.y() - Private::wiggleFactor, link.first.width() + Private::wiggleFactor * 2, link.first.height() + Private::wiggleFactor * 2 }; if( hitTarget.contains( point ) ) { return link.second; } } return QUrl(); } QObject* PresentationImpl::part() const { return d->part; } diff --git a/components/impl/SpreadsheetImpl.cpp b/components/impl/SpreadsheetImpl.cpp index fe6373b8f73..9512918fe45 100644 --- a/components/impl/SpreadsheetImpl.cpp +++ b/components/impl/SpreadsheetImpl.cpp @@ -1,245 +1,243 @@ /* * This file is part of the KDE project * * Copyright (C) 2013 Arjen Hiemstra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "SpreadsheetImpl.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Calligra::Components; class SpreadsheetImpl::Private { public: Private() : part{nullptr}, document{nullptr} { } Calligra::Sheets::Part* part; Calligra::Sheets::Doc* document; Calligra::Sheets::CanvasItem* canvas; int currentSheet; QList< QPair< QRectF, QUrl > > links; QList deepShapeFind(QList shapes) { QList allShapes; foreach(KoShape* shape, shapes) { allShapes.append(shape); KoShapeContainer *container = dynamic_cast(shape); if(container) { allShapes.append(deepShapeFind(container->shapes())); } } return allShapes; } void updateLinkTargets() { links.clear(); if(!canvas) return; foreach(const KoShape* shape, canvas->activeSheet()->shapes()) { if(!shape->hyperLink().isEmpty()) { QRectF rect = shape->boundingRect(); while(KoShapeContainer* parent = shape->parent()) { rect.translate(parent->position()); } links.append(QPair(rect, QUrl(shape->hyperLink()))); } } QList texts; KoFindText::findTextInShapes(canvas->activeSheet()->shapes(), texts); QList allShapes = deepShapeFind(canvas->activeSheet()->shapes()); foreach(QTextDocument* text, texts) { QTextBlock block = text->rootFrame()->firstCursorPosition().block(); for (; block.isValid(); block = block.next()) { block.begin(); QTextBlock::iterator it; for (it = block.begin(); !(it.atEnd()); ++it) { QTextFragment fragment = it.fragment(); if (fragment.isValid()) { QTextCharFormat format = fragment.charFormat(); if(format.isAnchor()) { // This is an anchor, store target and position... QRectF rect = getFragmentPosition(block, fragment); foreach(KoShape* shape, allShapes) { KoTextShapeData *shapeData = dynamic_cast(shape->userData()); if (!shapeData) continue; if(shapeData->document() == text) { rect.translate(shape->position()); while(KoShapeContainer* parent = shape->parent()) { rect.translate(parent->position()); } break; } } links.append(QPair(canvas->viewConverter()->documentToView(rect), QUrl(format.anchorHref()))); } } } } } } QRectF getFragmentPosition(QTextBlock block, QTextFragment fragment) { // TODO this only produces a position for the first part, if the link spans more than one line... // Need to sort that somehow, unfortunately probably by slapping this code into the above function. // For now leave it like this, more important things are needed. QTextLayout* layout = block.layout(); QTextLine line = layout->lineForTextPosition(fragment.position() - block.position()); if(!line.isValid()) { // fragment has no valid position and consequently no line... return QRectF(); } qreal top = line.position().y() + (line.height() / 2); qreal bottom = top + line.height(); qreal left = line.cursorToX(fragment.position() - block.position()); qreal right = line.cursorToX((fragment.position() - block.position()) + fragment.length()); QRectF fragmentPosition(QPointF(left, top), QPointF(right, bottom)); return fragmentPosition.adjusted(layout->position().x(), layout->position().y(), 0, 0); } static const float wiggleFactor; }; const float Calligra::Components::SpreadsheetImpl::Private::wiggleFactor{ 4.f }; SpreadsheetImpl::SpreadsheetImpl(QObject* parent) : DocumentImpl{parent}, d{new Private} { setDocumentType(DocumentType::Spreadsheet); } SpreadsheetImpl::~SpreadsheetImpl() { delete d; } bool SpreadsheetImpl::load(const QUrl& url) { - if(d->part) { - delete d->part; - delete d->document; - } + delete d->part; + delete d->document; d->part = new Calligra::Sheets::Part{this}; d->document = new Calligra::Sheets::Doc{d->part}; setKoDocument(d->document); d->part->setDocument(d->document); bool retval = d->document->openUrl(url); d->canvas = static_cast(d->part->canvasItem(d->document)); createAndSetCanvasController(d->canvas); createAndSetZoomController(d->canvas); connect(d->canvas, &Calligra::Sheets::CanvasItem::documentSizeChanged, this, &SpreadsheetImpl::updateDocumentSize); auto sheet = d->document->map()->sheet(0); if(sheet) { updateDocumentSize(sheet->documentSize().toSize()); } setCanvas(d->canvas); d->updateLinkTargets(); return retval; } int SpreadsheetImpl::currentIndex() { if (d->document && d->document->map() && d->canvas->activeSheet()) { return d->document->map()->indexOf(d->canvas->activeSheet()); } else { return -1; } } void SpreadsheetImpl::setCurrentIndex(int newValue) { if(newValue != currentIndex()) { d->canvas->setActiveSheet(d->document->map()->sheet(newValue)); d->updateLinkTargets(); emit currentIndexChanged(); } } void SpreadsheetImpl::updateDocumentSize(const QSize& size) { QRectF activeRect = d->canvas->viewConverter()->documentToView(d->canvas->activeSheet()->cellCoordinatesToDocument(d->canvas->activeSheet()->usedArea(true))); zoomController()->setDocumentSize(activeRect.size(), false); setDocumentSize(activeRect.size().toSize()); } int SpreadsheetImpl::indexCount() const { return d->document->map()->count(); } QUrl SpreadsheetImpl::urlAtPoint(QPoint point) { for( const QPair< QRectF, QUrl >& link : d->links ) { QRectF hitTarget{ link.first.x() - Private::wiggleFactor, link.first.y() - Private::wiggleFactor, link.first.width() + Private::wiggleFactor * 2, link.first.height() + Private::wiggleFactor * 2 }; if( hitTarget.contains( point ) ) { return link.second; } } return QUrl(); } QObject* SpreadsheetImpl::part() const { return d->part; } diff --git a/components/impl/TextDocumentImpl.cpp b/components/impl/TextDocumentImpl.cpp index 68eb719a3bd..5dd7f881aff 100644 --- a/components/impl/TextDocumentImpl.cpp +++ b/components/impl/TextDocumentImpl.cpp @@ -1,313 +1,311 @@ /* * This file is part of the KDE project * * Copyright (C) 2013 Arjen Hiemstra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "TextDocumentImpl.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ComponentsKoCanvasController.h" #include using namespace Calligra::Components; class TextDocumentImpl::Private { public: Private() : part{nullptr}, document{nullptr} { } QPointer part; QPointer document; QPointer canvas; QTimer indexChangedDelay; QList< QPair< QRectF, QUrl > > links; QList deepShapeFind(QList shapes) { QList allShapes; foreach(KoShape* shape, shapes) { allShapes.append(shape); KoShapeContainer *container = dynamic_cast(shape); if(container) { allShapes.append(deepShapeFind(container->shapes())); } } return allShapes; } void updateLinkTargets() { links.clear(); if(!canvas || !canvas->shapeManager()) return; foreach(const KoShape* shape, canvas->shapeManager()->shapes()) { if(!shape->hyperLink().isEmpty()) { QRectF rect = shape->boundingRect(); while(KoShapeContainer* parent = shape->parent()) { rect.translate(parent->position()); } links.append(QPair(rect, QUrl(shape->hyperLink()))); } } QList texts; KoFindText::findTextInShapes(canvas->shapeManager()->shapes(), texts); QList allShapes = deepShapeFind(canvas->shapeManager()->shapes()); foreach(QTextDocument* text, texts) { QTextBlock block = text->rootFrame()->firstCursorPosition().block(); for (; block.isValid(); block = block.next()) { block.begin(); QTextBlock::iterator it; for (it = block.begin(); !(it.atEnd()); ++it) { QTextFragment fragment = it.fragment(); if (fragment.isValid()) { QTextCharFormat format = fragment.charFormat(); if(format.isAnchor()) { // This is an anchor, store target and position... QRectF rect = getFragmentPosition(block, fragment); foreach(KoShape* shape, allShapes) { KoTextShapeData *shapeData = dynamic_cast(shape->userData()); if (!shapeData) continue; if(shapeData->document() == text) { rect.translate(shape->position()); while(KoShapeContainer* parent = shape->parent()) { rect.translate(parent->position()); } break; } } KWPage page = document->pageManager()->page(rect.top()); //rect.translate(page.rightMargin(), page.topMargin()); //rect = canvas->viewConverter()->documentToView(rect); //rect.translate(0, page.pageNumber() * (page.topMargin() + page.bottomMargin()) + 20); rect.translate(0, (page.pageNumber() - 1) * (page.topMargin() + 20)); links.append(QPair(canvas->viewConverter()->documentToView(rect), QUrl(format.anchorHref()))); } } } } } qDebug() << links; } QRectF getFragmentPosition(QTextBlock block, QTextFragment fragment) { // TODO this only produces a position for the first part, if the link spans more than one line... // Need to sort that somehow, unfortunately probably by slapping this code into the above function. // For now leave it like this, more important things are needed. QTextLayout* layout = block.layout(); QTextLine line = layout->lineForTextPosition(fragment.position() - block.position()); if(!line.isValid()) { // fragment has no valid position and consequently no line... return QRectF(); } qreal top = line.position().y() + (line.height() / 2); qreal bottom = top + line.height(); qreal left = line.cursorToX(fragment.position() - block.position()); qreal right = line.cursorToX((fragment.position() - block.position()) + fragment.length()); QRectF fragmentPosition(QPointF(left, top), QPointF(right, bottom)); return fragmentPosition.adjusted(layout->position().x(), layout->position().y(), 0, 0); } static const float wiggleFactor; }; const float Calligra::Components::TextDocumentImpl::Private::wiggleFactor{ 4.f }; TextDocumentImpl::TextDocumentImpl(QObject* parent) : DocumentImpl{parent}, d{new Private} { setDocumentType(DocumentType::TextDocument); d->indexChangedDelay.setInterval(0); connect(&d->indexChangedDelay, SIGNAL(timeout()), this, SIGNAL(currentIndexChanged())); } TextDocumentImpl::~TextDocumentImpl() { delete d; } bool TextDocumentImpl::load(const QUrl& url) { - if(d->part) { - delete d->part; - delete d->document; - } + delete d->part; + delete d->document; d->part = new KWPart{this}; d->document = new KWDocument{d->part}; setKoDocument(d->document); d->part->setDocument(d->document); d->document->setAutoSave(0); d->document->setCheckAutoSaveFile(false); bool retval = false; if (url.scheme() == QStringLiteral("newfile")) { QUrlQuery query(url); d->document->initEmpty(); KWPageStyle style = d->document->pageManager()->defaultPageStyle(); Q_ASSERT(style.isValid()); KoColumns columns; columns.count = query.queryItemValue("columncount").toInt(); columns.gapWidth = query.queryItemValue("columngap").toDouble(); style.setColumns(columns); KoPageLayout layout = style.pageLayout(); layout.format = KoPageFormat::formatFromString(query.queryItemValue("pageformat")); layout.orientation = (KoPageFormat::Orientation)query.queryItemValue("pageorientation").toInt(); layout.height = MM_TO_POINT(query.queryItemValue("height").toDouble()); layout.width = MM_TO_POINT(query.queryItemValue("width").toDouble()); if (query.queryItemValue("facingpages").toInt() == 1) { layout.bindingSide = MM_TO_POINT(query.queryItemValue("leftmargin").toDouble()); layout.pageEdge = MM_TO_POINT(query.queryItemValue("rightmargin").toDouble()); layout.leftMargin = layout.rightMargin = -1; } else { layout.bindingSide = layout.pageEdge = -1; layout.leftMargin = MM_TO_POINT(query.queryItemValue("leftmargin").toDouble()); layout.rightMargin = MM_TO_POINT(query.queryItemValue("rightmargin").toDouble()); } layout.topMargin = MM_TO_POINT(query.queryItemValue("topmargin").toDouble()); layout.bottomMargin = MM_TO_POINT(query.queryItemValue("bottommargin").toDouble()); style.setPageLayout(layout); d->document->setUnit(KoUnit::fromSymbol(query.queryItemValue("unit"))); d->document->relayout(); retval = true; } else if (url.scheme() == QStringLiteral("template")) { // Nip away the manually added template:// bit of the uri passed from the caller bool ok = d->document->loadNativeFormat(url.toString().mid(11)); d->document->setModified(false); d->document->undoStack()->clear(); if (ok) { QString mimeType = QMimeDatabase().mimeTypeForUrl(url).name(); // in case this is a open document template remove the -template from the end mimeType.remove( QRegExp( "-template$" ) ); d->document->setMimeTypeAfterLoading(mimeType); d->document->resetURL(); d->document->setEmpty(); } else { d->document->showLoadingErrorDialog(); d->document->initEmpty(); } retval = true; } else { retval = d->document->openUrl(url); } qDebug() << "Attempting to open" << url << "and our success was" << retval; d->canvas = static_cast(d->part->canvasItem(d->document)); createAndSetCanvasController(d->canvas); createAndSetZoomController(d->canvas); zoomController()->setPageSize(d->document->pageManager()->begin().rect().size()); connect(d->canvas, SIGNAL(documentSize(QSizeF)), zoomController(), SLOT(setDocumentSize(QSizeF))); d->canvas->updateSize(); setCanvas(d->canvas); connect(canvasController()->proxyObject, SIGNAL(moveDocumentOffset(QPoint)), &d->indexChangedDelay, SLOT(start())); d->updateLinkTargets(); return retval; } int TextDocumentImpl::currentIndex() { if(!d->canvas || !d->canvas->viewConverter()) { return 0; } QPointF newPoint = d->canvas->viewConverter()->viewToDocument(canvasController()->documentOffset()); KWPage page = d->document->pageManager()->page(newPoint.y()); return page.pageNumber(); } void TextDocumentImpl::setCurrentIndex(int newValue) { KWPage newPage = d->document->pageManager()->page(newValue + 1); QRectF newRect = d->canvas->viewConverter()->documentToView(newPage.rect()); canvasController()->setScrollBarValue(newRect.topLeft().toPoint()); emit requestViewUpdate(); emit currentIndexChanged(); } int TextDocumentImpl::indexCount() const { return d->document->pageCount(); } QUrl TextDocumentImpl::urlAtPoint(QPoint point) { qDebug() << Q_FUNC_INFO << point + (d->canvas->documentOffset() / zoomController()->zoomAction()->effectiveZoom()); for( const QPair< QRectF, QUrl >& link : d->links ) { QRectF hitTarget{ link.first.x() - Private::wiggleFactor, link.first.y() - Private::wiggleFactor, link.first.width() + Private::wiggleFactor * 2, link.first.height() + Private::wiggleFactor * 2 }; if( hitTarget.contains( point + (d->canvas->documentOffset() / zoomController()->zoomAction()->effectiveZoom()) ) ) return link.second; } return QUrl(); } QObject* TextDocumentImpl::part() const { return d->part; } diff --git a/components/models/ContentsModel.cpp b/components/models/ContentsModel.cpp index 7c4a3aeea4c..5af6b29f65a 100644 --- a/components/models/ContentsModel.cpp +++ b/components/models/ContentsModel.cpp @@ -1,194 +1,189 @@ /* * This file is part of the KDE project * * Copyright (C) 2013 Arjen Hiemstra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "ContentsModel.h" #include #include #include #include "Document.h" #include "PresentationContentsModelImpl.h" #include "SpreadsheetContentsModelImpl.h" #include "TextContentsModelImpl.h" using namespace Calligra::Components; class ContentsModel::Private { public: Private() : useToC{false}, impl{nullptr}, document{nullptr}, thumbnailSize{128, 128} { } bool useToC; ContentsModelImpl* impl; Document* document; QSize thumbnailSize; }; ContentsModel::ContentsModel(QObject* parent) : QAbstractListModel{parent}, d{new Private} { } ContentsModel::~ContentsModel() { delete d; } QVariant ContentsModel::data(const QModelIndex& index, int role) const { if(!d->impl || !index.isValid()) return QVariant(); return d->impl->data(index.row(), static_cast(role)); } int ContentsModel::rowCount(const QModelIndex& parent) const { Q_UNUSED(parent); if(d->impl) { return d->impl->rowCount(); } return 0; } Document* ContentsModel::document() const { return d->document; } void ContentsModel::setDocument(Document* newDocument) { if(newDocument != d->document) { if(d->document) { disconnect(d->document, &Document::statusChanged, this, &ContentsModel::updateImpl); } d->document = newDocument; connect(d->document, &Document::statusChanged, this, &ContentsModel::updateImpl); updateImpl(); emit documentChanged(); } } QSize ContentsModel::thumbnailSize() const { return d->thumbnailSize; } void ContentsModel::setThumbnailSize(const QSize& newValue) { if(newValue != d->thumbnailSize) { d->thumbnailSize = newValue; if(d->impl) { d->impl->setThumbnailSize(newValue); emit dataChanged(index(0), index(d->impl->rowCount() - 1), QVector{} << ThumbnailRole); } emit thumbnailSizeChanged(); } } void ContentsModel::setUseToC(bool newValue) { beginResetModel(); if(d->impl) d->impl->setUseToC(newValue); emit useToCChanged(); endResetModel(); } bool ContentsModel::useToC() const { return d->useToC; } QImage ContentsModel::thumbnail(int index, int width) const { if(!d->impl) return QImage{}; if(index < 0 || index >= d->impl->rowCount()) return QImage{}; return d->impl->thumbnail(index, width); } void ContentsModel::updateImpl() { beginResetModel(); - - if(d->impl) { - delete d->impl; - } + delete d->impl; + d->impl = nullptr; if(d->document && d->document->status() == DocumentStatus::Loaded) { switch(d->document->documentType()) { case DocumentType::TextDocument: { auto textImpl = new TextContentsModelImpl{d->document->koDocument(), dynamic_cast(d->document->canvas())}; d->impl = textImpl; connect(textImpl, &TextContentsModelImpl::listContentsCompleted, this, &ContentsModel::reset); break; } case DocumentType::Spreadsheet: d->impl = new SpreadsheetContentsModelImpl{d->document->koDocument()}; break; case DocumentType::Presentation: d->impl = new PresentationContentsModelImpl{d->document->koDocument()}; break; default: - d->impl = nullptr; break; } - } else { - d->impl = nullptr; } if(d->impl) { d->impl->setThumbnailSize(d->thumbnailSize); d->impl->setUseToC(d->useToC); } endResetModel(); } void ContentsModel::reset() { beginResetModel(); endResetModel(); } QHash ContentsModel::roleNames() const { QHash roleNames; roleNames.insert(TitleRole, "title"); roleNames.insert(LevelRole, "level"); roleNames.insert(ThumbnailRole, "thumbnail"); roleNames.insert(ContentIndexRole, "contentIndex"); return roleNames; }