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