Warning, file /office/calligra/qtquick/CQPresentationCanvas.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 "CQPresentationCanvas.h" 0012 #include "CQPresentationView.h" 0013 #include "CQCanvasController.h" 0014 0015 #include "gemini/ViewModeSwitchEvent.h" 0016 0017 #include <QStyleOptionGraphicsItem> 0018 0019 #include <KoPluginLoader.h> 0020 #include <KoDocumentEntry.h> 0021 #include <KoDocumentResourceManager.h> 0022 #include <KoShapeManager.h> 0023 #include <KoSelection.h> 0024 #include <KoTextEditor.h> 0025 #include <KoPart.h> 0026 #include <KoFindText.h> 0027 #include <KoCanvasBase.h> 0028 #include <KoToolManager.h> 0029 #include <KoZoomController.h> 0030 #include <KoZoomHandler.h> 0031 #include <KoPADocument.h> 0032 #include <KoPACanvasItem.h> 0033 #include <KoPAPageBase.h> 0034 #include <stage/part/KPrDocument.h> 0035 0036 #include <KActionCollection> 0037 0038 #include <QPluginLoader> 0039 #include <QMimeDatabase> 0040 #include <QGraphicsWidget> 0041 #include <QTextDocument> 0042 #include <QTextFrame> 0043 #include <QTextLayout> 0044 #include <QApplication> 0045 0046 0047 0048 class CQPresentationCanvas::Private 0049 { 0050 public: 0051 Private() : canvasBase(0), view(0), document(0), part(0), currentSlide(0) { } 0052 0053 KoCanvasBase* canvasBase; 0054 CQPresentationView* view; 0055 KPrDocument* document; 0056 KoPart* part; 0057 0058 int currentSlide; 0059 QSizeF pageSize; 0060 QObjectList linkTargets; 0061 0062 void updateLinkTargets() 0063 { 0064 qDeleteAll(linkTargets); 0065 linkTargets.clear(); 0066 0067 if (!view) { 0068 return; 0069 } 0070 foreach(const KoShape* shape, view->activePage()->shapes()) { 0071 if (!shape->hyperLink().isEmpty()) { 0072 QObject * obj = new QObject(view); 0073 obj->setProperty("linkRect", shape->boundingRect()); 0074 obj->setProperty("linkTarget", QUrl(shape->hyperLink())); 0075 linkTargets.append(obj); 0076 } 0077 } 0078 0079 QList<QTextDocument*> texts; 0080 KoFindText::findTextInShapes(view->activePage()->shapes(), texts); 0081 foreach(QTextDocument* text, texts) { 0082 QTextBlock block = text->rootFrame()->firstCursorPosition().block(); 0083 for (; block.isValid(); block = block.next()) { 0084 block.begin(); 0085 QTextBlock::iterator it; 0086 for (it = block.begin(); !(it.atEnd()); ++it) { 0087 QTextFragment fragment = it.fragment(); 0088 if (fragment.isValid()) { 0089 QTextCharFormat format = fragment.charFormat(); 0090 if (format.isAnchor()) { 0091 // This is an anchor, store target and position... 0092 QObject * obj = new QObject(view); 0093 QRectF rect = getFragmentPosition(block, fragment); 0094 obj->setProperty("linkRect", canvasBase->viewConverter()->documentToView(rect)); 0095 obj->setProperty("linkTarget", QUrl(format.anchorHref())); 0096 linkTargets.append(obj); 0097 } 0098 } 0099 } 0100 } 0101 } 0102 } 0103 0104 QRectF getFragmentPosition(const QTextBlock& block, const QTextFragment& fragment) 0105 { 0106 // TODO this only produces a position for the first part, if the link spans more than one line... 0107 // Need to sort that somehow, unfortunately probably by slapping this code into the above function. 0108 // For now leave it like this, more important things are needed. 0109 QTextLayout* layout = block.layout(); 0110 QTextLine line = layout->lineForTextPosition(fragment.position() - block.position()); 0111 if (!line.isValid()) 0112 { 0113 // fragment has no valid position and consequently no line... 0114 return QRectF(); 0115 } 0116 qreal top = line.position().y(); 0117 qreal bottom = line.position().y() + line.height(); 0118 qreal left = line.cursorToX(fragment.position() - block.position()); 0119 qreal right = line.cursorToX((fragment.position() - block.position()) + fragment.length()); 0120 QRectF fragmentPosition(QPointF(top, left), QPointF(bottom, right)); 0121 return fragmentPosition.adjusted(layout->position().x(), layout->position().y(), 0, 0); 0122 } 0123 }; 0124 0125 CQPresentationCanvas::CQPresentationCanvas(QDeclarativeItem* parent) 0126 : CQCanvasBase(parent), d(new Private) 0127 { 0128 0129 } 0130 0131 CQPresentationCanvas::~CQPresentationCanvas() 0132 { 0133 d->part->removeMainWindow(d->part->currentMainwindow()); 0134 KoToolManager::instance()->removeCanvasController(d->canvasBase->canvasController()); 0135 delete d; 0136 } 0137 0138 int CQPresentationCanvas::currentSlide() const 0139 { 0140 return d->currentSlide; 0141 } 0142 0143 int CQPresentationCanvas::slideCount() const 0144 { 0145 return d->document->pageCount(); 0146 } 0147 0148 QObjectList CQPresentationCanvas::linkTargets() const 0149 { 0150 return d->linkTargets; 0151 } 0152 0153 KPrDocument* CQPresentationCanvas::document() const 0154 { 0155 return d->document; 0156 } 0157 0158 QObject* CQPresentationCanvas::doc() const 0159 { 0160 return d->document; 0161 } 0162 0163 QObject* CQPresentationCanvas::part() const 0164 { 0165 return d->part; 0166 } 0167 0168 QSizeF CQPresentationCanvas::pageSize() const 0169 { 0170 return d->pageSize; 0171 } 0172 0173 void CQPresentationCanvas::setCurrentSlide(int slide) 0174 { 0175 slide = qBound(0, slide, d->document->pageCount() - 1); 0176 if (slide != d->currentSlide) { 0177 d->currentSlide = slide; 0178 d->view->doUpdateActivePage(d->document->pageByIndex(slide, false)); 0179 d->pageSize = d->view->activePage()->size(); 0180 emit currentSlideChanged(); 0181 d->updateLinkTargets(); 0182 emit linkTargetsChanged(); 0183 } 0184 } 0185 0186 void CQPresentationCanvas::render(QPainter* painter, const QRectF& target) 0187 { 0188 QStyleOptionGraphicsItem option; 0189 option.exposedRect = target; 0190 option.rect = target.toAlignedRect(); 0191 d->canvasBase->canvasItem()->paint(painter, &option); 0192 } 0193 0194 QObject* CQPresentationCanvas::textEditor() const 0195 { 0196 if (d->canvasBase) { 0197 return KoTextEditor::getTextEditorFromCanvas(d->canvasBase); 0198 } 0199 return 0; 0200 } 0201 0202 void CQPresentationCanvas::deselectEverything() 0203 { 0204 KoTextEditor* editor = KoTextEditor::getTextEditorFromCanvas(d->canvasBase); 0205 if (editor) { 0206 editor->clearSelection(); 0207 } 0208 d->canvasBase->shapeManager()->selection()->deselectAll(); 0209 } 0210 0211 qreal CQPresentationCanvas::shapeTransparency() const 0212 { 0213 if (d->canvasBase && d->canvasBase->shapeManager()) { 0214 KoShape* shape = d->canvasBase->shapeManager()->selection()->firstSelectedShape(); 0215 if (shape) { 0216 return shape->transparency(); 0217 } 0218 } 0219 return CQCanvasBase::shapeTransparency(); 0220 } 0221 0222 void CQPresentationCanvas::setShapeTransparency(qreal newTransparency) 0223 { 0224 if (d->canvasBase && d->canvasBase->shapeManager()) { 0225 KoShape* shape = d->canvasBase->shapeManager()->selection()->firstSelectedShape(); 0226 if (shape) { 0227 if (!qFuzzyCompare(1 + shape->transparency(), 1 + newTransparency)) { 0228 shape->setTransparency(newTransparency); 0229 CQCanvasBase::setShapeTransparency(newTransparency); 0230 } 0231 } 0232 } 0233 } 0234 0235 void CQPresentationCanvas::openFile(const QString& uri) 0236 { 0237 emit loadingBegun(); 0238 0239 KoDocumentEntry entry; 0240 QList<QPluginLoader*> pluginLoaders = KoPluginLoader::pluginLoaders("calligra/parts"); 0241 Q_FOREACH (QPluginLoader *loader, pluginLoaders) { 0242 if (loader->fileName().contains(QLatin1String("stagepart"))) { 0243 entry = KoDocumentEntry(loader); 0244 pluginLoaders.removeOne(loader); 0245 break; 0246 } 0247 } 0248 qDeleteAll(pluginLoaders); 0249 if (entry.isEmpty()) { 0250 qWarning("Unable to load Stage plugin, aborting!"); 0251 return; 0252 } 0253 0254 // QT5TODO: ownership of d->part unclear 0255 d->part = entry.createKoPart(); 0256 d->document = dynamic_cast<KPrDocument*>(d->part->document()); 0257 d->document->setAutoSave(0); 0258 d->document->setCheckAutoSaveFile(false); 0259 if (uri.endsWith(QLatin1String("otp"), Qt::CaseInsensitive)) { 0260 QUrl url(uri); 0261 bool ok = d->document->loadNativeFormat(url.toLocalFile()); 0262 d->document->setModified(false); 0263 d->document->undoStack()->clear(); 0264 0265 if (ok) { 0266 QString mimeType = QMimeDatabase().mimeTypeForUrl(url).name(); 0267 // in case this is a open document template remove the -template from the end 0268 mimeType.remove( QRegExp( "-template$" ) ); 0269 d->document->setMimeTypeAfterLoading(mimeType); 0270 d->document->resetURL(); 0271 d->document->setEmpty(); 0272 } else { 0273 // some kind of error reporting thing here... failed to load template, tell the user 0274 // why their canvas is so terribly empty. 0275 d->document->initEmpty(); 0276 } 0277 } else { 0278 d->document->openUrl (QUrl (uri)); 0279 } 0280 0281 d->document->setModified(false); 0282 qApp->processEvents(); 0283 0284 KoPACanvasItem *paCanvasItem = static_cast<KoPACanvasItem*>(d->part->canvasItem(d->part->document())); 0285 d->canvasBase = paCanvasItem; 0286 createAndSetCanvasControllerOn(d->canvasBase); 0287 0288 d->view = new CQPresentationView(canvasController(), static_cast<KoPACanvasBase*>(d->canvasBase), dynamic_cast<KPrDocument*>(d->document)); 0289 paCanvasItem->setView(d->view); 0290 0291 d->canvasBase->resourceManager()->setResource(KoDocumentResourceManager::HandleRadius, 9); 0292 d->canvasBase->resourceManager()->setResource(KoDocumentResourceManager::GrabSensitivity, 9); 0293 0294 createAndSetZoomController(d->canvasBase); 0295 d->view->setZoomController(zoomController()); 0296 d->view->connectToZoomController(); 0297 0298 QGraphicsWidget *graphicsWidget = dynamic_cast<QGraphicsWidget*>(d->canvasBase); 0299 graphicsWidget->setParentItem(this); 0300 graphicsWidget->installEventFilter(this); 0301 graphicsWidget->setVisible(true); 0302 graphicsWidget->setGeometry(x(), y(), width(), height()); 0303 0304 if (d->document->pageCount() > 0) { 0305 d->view->doUpdateActivePage(d->document->pageByIndex(0, false)); 0306 d->pageSize = d->view->activePage()->size(); 0307 emit currentSlideChanged(); 0308 0309 d->updateLinkTargets(); 0310 emit linkTargetsChanged(); 0311 } 0312 0313 emit documentChanged(); 0314 emit loadingFinished(); 0315 } 0316 0317 void CQPresentationCanvas::createAndSetCanvasControllerOn(KoCanvasBase* canvas) 0318 { 0319 //TODO: pass a proper action collection 0320 CQCanvasController *controller = new CQCanvasController(new KActionCollection(this)); 0321 setCanvasController(controller); 0322 controller->setCanvas(canvas); 0323 KoToolManager::instance()->addController (controller); 0324 } 0325 0326 void CQPresentationCanvas::createAndSetZoomController(KoCanvasBase* canvas) 0327 { 0328 KoZoomHandler* zoomHandler = static_cast<KoZoomHandler*> (canvas->viewConverter()); 0329 setZoomController(new KoZoomController(canvasController(), 0330 zoomHandler, 0331 new KActionCollection(this))); 0332 0333 KoPACanvasItem* canvasItem = static_cast<KoPACanvasItem*>(canvas); 0334 0335 // update the canvas whenever we scroll, the canvas controller must emit this signal on scrolling/panning 0336 connect (canvasController()->proxyObject, 0337 SIGNAL(moveDocumentOffset(QPoint)), canvasItem, SLOT(slotSetDocumentOffset(QPoint))); 0338 // whenever the size of the document viewed in the canvas changes, inform the zoom controller 0339 connect (canvasItem, SIGNAL(documentSize(QSize)), this, SLOT(updateDocumentSize(QSize))); 0340 canvasItem->updateSize(); 0341 canvasItem->update(); 0342 } 0343 0344 void CQPresentationCanvas::updateDocumentSize(const QSize& size) 0345 { 0346 zoomController()->setDocumentSize(d->canvasBase->viewConverter()->viewToDocument(size), false); 0347 } 0348 0349 bool CQPresentationCanvas::event(QEvent* event) 0350 { switch(static_cast<int>(event->type())) { 0351 case ViewModeSwitchEvent::AboutToSwitchViewModeEvent: { 0352 ViewModeSynchronisationObject* syncObject = static_cast<ViewModeSwitchEvent*>(event)->synchronisationObject(); 0353 0354 // Simplest of transfer - no zoom transfer for presentations, just current slide 0355 syncObject->currentSlide = d->currentSlide; 0356 syncObject->shapes = d->canvasBase->shapeManager()->shapes(); 0357 syncObject->initialized = true; 0358 0359 return true; 0360 } 0361 case ViewModeSwitchEvent::SwitchedToTouchModeEvent: { 0362 ViewModeSynchronisationObject* syncObject = static_cast<ViewModeSwitchEvent*>(event)->synchronisationObject(); 0363 0364 if (syncObject->initialized) { 0365 d->canvasBase->shapeManager()->setShapes(syncObject->shapes); 0366 0367 zoomController()->setZoom(KoZoomMode::ZOOM_PAGE, 1.0); 0368 zoomController()->zoomAction()->zoomOut(); 0369 0370 setCurrentSlide(syncObject->currentSlide); 0371 qApp->processEvents(); 0372 0373 KoToolManager::instance()->switchToolRequested("InteractionTool"); 0374 } 0375 0376 return true; 0377 } 0378 // case KisTabletEvent::TabletPressEx: 0379 // case KisTabletEvent::TabletReleaseEx: 0380 // emit interactionStarted(); 0381 // d->canvas->inputManager()->eventFilter(this, event); 0382 // return true; 0383 // case KisTabletEvent::TabletMoveEx: 0384 // d->tabletEventCount++; //Note that this will wraparound at some point; This is intentional. 0385 // #ifdef Q_OS_X11 0386 // if (d->tabletEventCount % 2 == 0) 0387 // #endif 0388 // d->canvas->inputManager()->eventFilter(this, event); 0389 // return true; 0390 default: 0391 break; 0392 } 0393 return QDeclarativeItem::event( event ); 0394 } 0395 0396 void CQPresentationCanvas::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) 0397 { 0398 if (d->canvasBase) { 0399 QGraphicsWidget *widget = dynamic_cast<QGraphicsWidget*>(d->canvasBase); 0400 if (widget) { 0401 widget->setGeometry(newGeometry); 0402 } 0403 } 0404 QDeclarativeItem::geometryChanged(newGeometry, oldGeometry); 0405 }