File indexing completed on 2024-04-21 15:55:46
0001 /******************************************************************************** 0002 Copyright (C) 2011-2022 by Michel Ludwig (michel.ludwig@kdemail.net) 0003 ********************************************************************************/ 0004 0005 /*************************************************************************** 0006 * * 0007 * This program is free software; you can redistribute it and/or modify * 0008 * it under the terms of the GNU General Public License as published by * 0009 * the Free Software Foundation; either version 2 of the License, or * 0010 * (at your option) any later version. * 0011 * * 0012 ***************************************************************************/ 0013 0014 #include "livepreview.h" 0015 #include "config.h" 0016 0017 #include <algorithm> 0018 0019 #include <QCryptographicHash> 0020 #include <QDir> 0021 #include <QHBoxLayout> 0022 #include <QMap> 0023 #include <QFile> 0024 #include <QFileInfo> 0025 #include <QStandardPaths> 0026 #include <QTextCodec> 0027 #include <QTextStream> 0028 #include <QTimer> 0029 #include <QTemporaryDir> 0030 0031 #include <KActionCollection> 0032 #include <KLocalizedString> 0033 #include <KTextEditor/Application> 0034 #include <KTextEditor/CodeCompletionInterface> 0035 #include <KTextEditor/Document> 0036 #include <KTextEditor/MainWindow> 0037 #include <KTextEditor/View> 0038 #include <KToolBar> 0039 #include <KParts/MainWindow> 0040 #include <KXMLGUIFactory> 0041 0042 #include <okular/interfaces/viewerinterface.h> 0043 0044 #include "errorhandler.h" 0045 #include "kiledebug.h" 0046 #include "kiletool_enums.h" 0047 #include "kiledocmanager.h" 0048 #include "kileviewmanager.h" 0049 0050 //TODO: it still has to be checked whether it is necessary to use LaTeXInfo objects 0051 0052 namespace KileTool 0053 { 0054 0055 class LivePreviewManager::PreviewInformation { 0056 public: 0057 PreviewInformation() 0058 : lastSynchronizationCursor(-1, -1) 0059 { 0060 initTemporaryDirectory(); 0061 } 0062 0063 ~PreviewInformation() { 0064 delete m_tempDir; 0065 } 0066 0067 QString getTempDir() const { 0068 return m_tempDir->path(); 0069 } 0070 0071 void clearPreviewPathMappings() { 0072 pathToPreviewPathHash.clear(); 0073 previewPathToPathHash.clear(); 0074 } 0075 0076 bool createSubDirectoriesForProject(KileProject *project, bool *containsInvalidRelativeItem = Q_NULLPTR) { 0077 if(containsInvalidRelativeItem) { 0078 *containsInvalidRelativeItem = false; 0079 } 0080 const QList<KileProjectItem*> items = project->items(); 0081 const QString tempCanonicalDir = QDir(m_tempDir->path()).canonicalPath(); 0082 if(tempCanonicalDir.isEmpty()) { 0083 return false; 0084 } 0085 for(KileProjectItem *item : items) { 0086 bool successful = true; 0087 const QString itemRelativeDir = QFileInfo(tempCanonicalDir + '/' + item->path()).path(); 0088 const QString itemAbsolutePath = QDir(itemRelativeDir).absolutePath(); 0089 if(itemAbsolutePath.isEmpty()) { 0090 successful = false; 0091 } 0092 else if(!itemAbsolutePath.startsWith(tempCanonicalDir)) { 0093 if(containsInvalidRelativeItem) { 0094 *containsInvalidRelativeItem = true; 0095 } 0096 successful = false; // we don't want to create directories below 'm_tempDir->name()' 0097 } 0098 else { 0099 successful = QDir().mkpath(itemAbsolutePath); 0100 } 0101 if(!successful) { 0102 return false; 0103 } 0104 } 0105 return true; 0106 } 0107 0108 void setLastSynchronizationCursor(int line, int col) 0109 { 0110 lastSynchronizationCursor.setLine(line); 0111 lastSynchronizationCursor.setColumn(col); 0112 } 0113 0114 private: 0115 QTemporaryDir *m_tempDir; 0116 0117 void initTemporaryDirectory() { 0118 m_tempDir = new QTemporaryDir(QDir::tempPath() + QLatin1Char('/') + "kile-livepreview"); 0119 } 0120 0121 public: 0122 QHash<QString, QString> pathToPreviewPathHash; 0123 QHash<QString, QString> previewPathToPathHash; 0124 QString previewFile; 0125 QHash<KileDocument::TextInfo*, QByteArray> textHash; 0126 KTextEditor::Cursor lastSynchronizationCursor; 0127 }; 0128 0129 LivePreviewManager::LivePreviewManager(KileInfo *ki, KActionCollection *ac) 0130 : m_ki(ki), 0131 m_bootUpMode(true), 0132 m_previewStatusLed(Q_NULLPTR), 0133 m_previewForCurrentDocumentAction(Q_NULLPTR), 0134 m_recompileLivePreviewAction(Q_NULLPTR), 0135 m_runningLaTeXInfo(Q_NULLPTR), m_runningTextView(Q_NULLPTR), m_runningProject(Q_NULLPTR), 0136 m_runningPreviewInformation(Q_NULLPTR), m_shownPreviewInformation(Q_NULLPTR), m_masterDocumentPreviewInformation(Q_NULLPTR) 0137 { 0138 connect(m_ki->viewManager(), SIGNAL(textViewActivated(KTextEditor::View*)), 0139 this, SLOT(handleTextViewActivated(KTextEditor::View*))); 0140 connect(m_ki->viewManager(), SIGNAL(textViewClosed(KTextEditor::View*,bool)), 0141 this, SLOT(handleTextViewClosed(KTextEditor::View*,bool))); 0142 connect(m_ki->toolManager(), SIGNAL(childToolSpawned(KileTool::Base*,KileTool::Base*)), 0143 this, SLOT(handleSpawnedChildTool(KileTool::Base*,KileTool::Base*))); 0144 connect(m_ki->docManager(), SIGNAL(documentSavedAs(KTextEditor::View*,KileDocument::TextInfo*)), 0145 this, SLOT(handleDocumentSavedAs(KTextEditor::View*,KileDocument::TextInfo*))); 0146 connect(m_ki->docManager(), SIGNAL(documentOpened(KileDocument::TextInfo*)), 0147 this, SLOT(handleDocumentOpened(KileDocument::TextInfo*))); 0148 connect(m_ki->docManager(), SIGNAL(projectOpened(KileProject*)), 0149 this, SLOT(handleProjectOpened(KileProject*))); 0150 0151 createActions(ac); 0152 populateViewerControlToolBar(); 0153 0154 m_ledBlinkingTimer = new QTimer(this); 0155 m_ledBlinkingTimer->setSingleShot(false); 0156 m_ledBlinkingTimer->setInterval(500); 0157 connect(m_ledBlinkingTimer, SIGNAL(timeout()), m_previewStatusLed, SLOT(toggle())); 0158 0159 m_documentChangedTimer = new QTimer(this); 0160 m_documentChangedTimer->setSingleShot(true); 0161 connect(m_documentChangedTimer, SIGNAL(timeout()), this, SLOT(handleDocumentModificationTimerTimeout())); 0162 0163 showPreviewDisabled(); 0164 } 0165 0166 LivePreviewManager::~LivePreviewManager() 0167 { 0168 KILE_DEBUG_MAIN; 0169 0170 qDeleteAll(m_livePreviewToolActionList); 0171 m_livePreviewToolActionList.clear(); 0172 0173 deleteAllLivePreviewInformation(); 0174 } 0175 0176 void LivePreviewManager::disableBootUpMode() 0177 { 0178 m_bootUpMode = false; 0179 recompileLivePreview(); 0180 } 0181 0182 void LivePreviewManager::createActions(KActionCollection *ac) 0183 { 0184 0185 m_livePreviewToolActionGroup = new QActionGroup(ac); 0186 0187 m_previewForCurrentDocumentAction = new KToggleAction(QIcon::fromTheme("document-preview"), i18n("Live Preview for Current Document or Project"), this); 0188 m_previewForCurrentDocumentAction->setChecked(true); 0189 connect(m_previewForCurrentDocumentAction, SIGNAL(triggered(bool)), this, SLOT(previewForCurrentDocumentActionTriggered(bool))); 0190 ac->addAction("live_preview_for_current_document", m_previewForCurrentDocumentAction); 0191 0192 m_recompileLivePreviewAction = new QAction(i18n("Recompile Live Preview"), this); 0193 connect(m_recompileLivePreviewAction, SIGNAL(triggered()), this, SLOT(recompileLivePreview())); 0194 ac->addAction("live_preview_recompile", m_recompileLivePreviewAction); 0195 0196 { 0197 QAction *action = new QAction(i18n("Save Compiled Document..."), this); 0198 connect(action, &QAction::triggered, m_ki->docManager(), &KileDocument::Manager::fileSaveCompiledDocument); 0199 ac->addAction("file_save_compiled_document", action); 0200 connect(this, &KileTool::LivePreviewManager::livePreviewSuccessful, action, [=]() { action->setEnabled(true); }); 0201 connect(this, &KileTool::LivePreviewManager::livePreviewRunning, action, [=]() { action->setEnabled(false); }); 0202 connect(this, &KileTool::LivePreviewManager::livePreviewStopped, action, [=]() { action->setEnabled(false); }); 0203 } 0204 } 0205 0206 void LivePreviewManager::previewForCurrentDocumentActionTriggered(bool b) 0207 { 0208 if(m_bootUpMode || !KileConfig::livePreviewEnabled()) { 0209 return; 0210 } 0211 KTextEditor::View *view = m_ki->viewManager()->currentTextView(); 0212 if(!view) { 0213 return; 0214 } 0215 KileDocument::LaTeXInfo *latexInfo = dynamic_cast<KileDocument::LaTeXInfo*>(m_ki->docManager()->textInfoFor(view->document())); 0216 if(!latexInfo) { 0217 return; 0218 } 0219 LivePreviewUserStatusHandler *userStatusHandler; 0220 findPreviewInformation(latexInfo, Q_NULLPTR, &userStatusHandler); 0221 Q_ASSERT(userStatusHandler); 0222 0223 userStatusHandler->setLivePreviewEnabled(b); 0224 0225 if(b) { 0226 showPreviewCompileIfNecessary(latexInfo, view); 0227 } 0228 else { 0229 disablePreview(); 0230 } 0231 } 0232 0233 void LivePreviewManager::livePreviewToolActionTriggered() 0234 { 0235 QAction *action = dynamic_cast<QAction*>(sender()); 0236 if(!action) { 0237 KILE_DEBUG_MAIN << "slot called from wrong object!!"; 0238 return; 0239 } 0240 if(!m_actionToLivePreviewToolHash.contains(action)) { 0241 KILE_DEBUG_MAIN << "action not found in hash!!"; 0242 return; 0243 } 0244 const ToolConfigPair p = m_actionToLivePreviewToolHash[action]; 0245 KTextEditor::View *view = m_ki->viewManager()->currentTextView(); 0246 if(!view) { 0247 KILE_DEBUG_MAIN << "no text view open!"; 0248 return; 0249 } 0250 KileDocument::LaTeXInfo *latexInfo = dynamic_cast<KileDocument::LaTeXInfo*>(m_ki->docManager()->textInfoFor(view->document())); 0251 if(!latexInfo) { 0252 KILE_DEBUG_MAIN << "current view is not LaTeX-compatible!"; 0253 return; 0254 } 0255 0256 LivePreviewUserStatusHandler *userStatusHandler; 0257 findPreviewInformation(latexInfo, Q_NULLPTR, &userStatusHandler); 0258 if(!userStatusHandler) { 0259 KILE_DEBUG_MAIN << "no preview information found!"; 0260 return; 0261 } 0262 const bool changed = userStatusHandler->setLivePreviewTool(p); 0263 if(changed) { 0264 recompileLivePreview(); 0265 } 0266 } 0267 0268 void LivePreviewManager::updateLivePreviewToolActions(LivePreviewUserStatusHandler *userStatusHandler) 0269 { 0270 setLivePreviewToolActionsEnabled(true); 0271 const ToolConfigPair p = userStatusHandler->livePreviewTool(); 0272 if(!m_livePreviewToolToActionHash.contains(p)) { 0273 return; 0274 } 0275 m_livePreviewToolToActionHash[p]->setChecked(true); 0276 } 0277 0278 void LivePreviewManager::setLivePreviewToolActionsEnabled(bool b) 0279 { 0280 for(QAction *action : m_livePreviewToolActionList) { 0281 action->setEnabled(b); 0282 } 0283 } 0284 0285 void LivePreviewManager::buildLivePreviewMenu(KConfig *config) 0286 { 0287 QMenu *menu = dynamic_cast<QMenu*>(m_ki->mainWindow()->guiFactory()->container("menu_livepreview", m_ki->mainWindow())); 0288 if(!menu) { 0289 KILE_DEBUG_MAIN << "live preview menu not found!!"; 0290 return; 0291 } 0292 0293 qDeleteAll(m_livePreviewToolActionList); 0294 m_livePreviewToolActionList.clear(); 0295 m_livePreviewToolToActionHash.clear(); 0296 m_actionToLivePreviewToolHash.clear(); 0297 0298 // necessary as it will be disabled otherwise in 'kile.cpp' (as it's empty initially) 0299 menu->setEnabled(true); 0300 menu->clear(); 0301 menu->addAction(m_previewForCurrentDocumentAction); 0302 menu->addSeparator(); 0303 0304 QList<ToolConfigPair> toolListConfig = toolsWithConfigurationsBasedOnClass(config, "LaTeXLivePreview"); 0305 std::sort(toolListConfig.begin(), toolListConfig.end()); 0306 for(QList<ToolConfigPair>::iterator i = toolListConfig.begin(); i != toolListConfig.end(); ++i) { 0307 const QString shortToolName = QString((*i).first).remove("LivePreview-"); 0308 QAction *action = new KToggleAction(ToolConfigPair::userStringRepresentation(shortToolName, (*i).second), this); 0309 0310 m_livePreviewToolActionGroup->addAction(action); 0311 connect(action, SIGNAL(triggered()), this, SLOT(livePreviewToolActionTriggered())); 0312 m_livePreviewToolActionList.push_back(action); 0313 m_livePreviewToolToActionHash[*i] = action; 0314 m_actionToLivePreviewToolHash[action] = *i; 0315 menu->addAction(action); 0316 } 0317 menu->addSeparator(); 0318 menu->addAction(m_recompileLivePreviewAction); 0319 } 0320 0321 QString LivePreviewManager::getPreviewFile() const 0322 { 0323 if(!m_shownPreviewInformation) { 0324 return QString(); 0325 } 0326 return m_shownPreviewInformation->previewFile; 0327 } 0328 0329 bool LivePreviewManager::isLivePreviewEnabledForCurrentDocument() 0330 { 0331 return m_previewForCurrentDocumentAction->isChecked(); 0332 } 0333 0334 void LivePreviewManager::setLivePreviewEnabledForCurrentDocument(bool b) 0335 { 0336 m_previewForCurrentDocumentAction->setChecked(b); 0337 previewForCurrentDocumentActionTriggered(b); 0338 } 0339 0340 void LivePreviewManager::disablePreview() 0341 { 0342 stopAndClearPreview(); 0343 setLivePreviewToolActionsEnabled(false); 0344 m_previewForCurrentDocumentAction->setChecked(false); 0345 m_ki->viewManager()->setLivePreviewModeForDocumentViewer(false); 0346 } 0347 0348 void LivePreviewManager::stopAndClearPreview() 0349 { 0350 KILE_DEBUG_MAIN; 0351 stopLivePreview(); 0352 clearLivePreview(); 0353 } 0354 0355 void LivePreviewManager::clearLivePreview() 0356 { 0357 KILE_DEBUG_MAIN; 0358 showPreviewDisabled(); 0359 0360 KParts::ReadOnlyPart *viewerPart = m_ki->viewManager()->viewerPart(); 0361 if(m_shownPreviewInformation && viewerPart->url() == QUrl::fromLocalFile(m_shownPreviewInformation->previewFile)) { 0362 viewerPart->closeUrl(); 0363 } 0364 m_shownPreviewInformation = Q_NULLPTR; 0365 emit(livePreviewStopped()); 0366 } 0367 0368 void LivePreviewManager::stopLivePreview() 0369 { 0370 m_documentChangedTimer->stop(); 0371 m_ki->toolManager()->stopLivePreview(); 0372 0373 clearRunningLivePreviewInformation(); 0374 } 0375 0376 void LivePreviewManager::clearRunningLivePreviewInformation() 0377 { 0378 m_runningPathToPreviewPathHash.clear(); 0379 m_runningPreviewPathToPathHash.clear(); 0380 m_runningPreviewFile.clear(); 0381 m_runningLaTeXInfo = Q_NULLPTR; 0382 m_runningProject = Q_NULLPTR; 0383 m_runningTextView = Q_NULLPTR; 0384 m_runningPreviewInformation = Q_NULLPTR; 0385 m_runningTextHash.clear(); 0386 } 0387 0388 void LivePreviewManager::deleteAllLivePreviewInformation() 0389 { 0390 // first, we have to make sure that nothing is shown anymore, 0391 // and that no preview is running 0392 stopAndClearPreview(); 0393 0394 disablePreview(); 0395 0396 // and now we can delete all the 'PreviewInformation' objects 0397 delete m_masterDocumentPreviewInformation; 0398 m_masterDocumentPreviewInformation = Q_NULLPTR; 0399 0400 for(QHash<KileDocument::LaTeXInfo*, PreviewInformation*>::iterator i = m_latexInfoToPreviewInformationHash.begin(); 0401 i != m_latexInfoToPreviewInformationHash.end(); ++i) { 0402 delete i.value(); 0403 } 0404 0405 for(QHash<KileProject*,PreviewInformation*>::iterator i = m_projectToPreviewInformationHash.begin(); 0406 i != m_projectToPreviewInformationHash.end(); ++i) { 0407 delete i.value(); 0408 } 0409 m_latexInfoToPreviewInformationHash.clear(); 0410 m_projectToPreviewInformationHash.clear(); 0411 } 0412 0413 void LivePreviewManager::readConfig(KConfig *config) 0414 { 0415 Q_UNUSED(config); 0416 0417 buildLivePreviewMenu(config); 0418 0419 m_previewForCurrentDocumentAction->setEnabled(KileConfig::livePreviewEnabled()); 0420 m_previewStatusLed->setEnabled(KileConfig::livePreviewEnabled()); 0421 0422 if(m_bootUpMode || !KileConfig::livePreviewEnabled()) { 0423 deleteAllLivePreviewInformation(); 0424 } 0425 else { 0426 refreshLivePreview(); // e.g. in case the live preview was disabled and no preview is 0427 // currently shown 0428 } 0429 } 0430 0431 void LivePreviewManager::writeConfig() 0432 { 0433 } 0434 0435 void LivePreviewManager::readLivePreviewStatusSettings(KConfigGroup &configGroup, LivePreviewUserStatusHandler *handler) 0436 { 0437 // the prefix 'kile_' is necessary as these settings might be written into a config group that is also modified 0438 // by KatePart 0439 if(configGroup.readEntry("kile_livePreviewStatusUserSpecified", false)) { 0440 handler->setLivePreviewEnabled(configGroup.readEntry("kile_livePreviewEnabled", true)); 0441 } 0442 0443 const QString livePreviewToolConfigString = configGroup.readEntry("kile_livePreviewTool", ""); 0444 if(livePreviewToolConfigString.isEmpty()) { 0445 // if nothing is set for this fallback to the configured global default, otherwise to the hardcoded default 0446 QString defaultToolName = KileConfig::livePreviewDefaultTool(); 0447 if(defaultToolName.isEmpty()) { 0448 defaultToolName = LIVEPREVIEW_DEFAULT_TOOL_NAME; 0449 } 0450 KileTool::ToolConfigPair defaultTool = KileTool::ToolConfigPair::fromConfigStringRepresentation(defaultToolName); 0451 handler->setLivePreviewTool(defaultTool); 0452 } 0453 else { 0454 handler->setLivePreviewTool(ToolConfigPair::fromConfigStringRepresentation(livePreviewToolConfigString)); 0455 } 0456 } 0457 0458 void LivePreviewManager::writeLivePreviewStatusSettings(KConfigGroup &configGroup, LivePreviewUserStatusHandler *handler) 0459 { 0460 configGroup.writeEntry("kile_livePreviewTool", handler->livePreviewTool().configStringRepresentation()); 0461 configGroup.writeEntry("kile_livePreviewEnabled", handler->isLivePreviewEnabled()); 0462 configGroup.writeEntry("kile_livePreviewStatusUserSpecified", handler->userSpecifiedLivePreviewStatus()); 0463 } 0464 0465 void LivePreviewManager::populateViewerControlToolBar() 0466 { 0467 KToolBar* viewerControlToolBar = m_ki->viewManager()->getViewerControlToolBar(); 0468 viewerControlToolBar->addAction(m_previewForCurrentDocumentAction); 0469 0470 m_previewStatusLed = new KLed(viewerControlToolBar); 0471 m_previewStatusLed->setShape(KLed::Circular); 0472 m_previewStatusLed->setLook(KLed::Flat); 0473 viewerControlToolBar->addWidget(m_previewStatusLed); 0474 } 0475 0476 void LivePreviewManager::handleMasterDocumentChanged() 0477 { 0478 if(m_bootUpMode || !KileConfig::livePreviewEnabled()) { 0479 return; 0480 } 0481 0482 deleteAllLivePreviewInformation(); 0483 refreshLivePreview(); 0484 } 0485 0486 void LivePreviewManager::handleTextChanged(KTextEditor::Document *doc) 0487 { 0488 if(m_bootUpMode || !KileConfig::livePreviewEnabled() 0489 || !isLivePreviewEnabledForCurrentDocument()) { 0490 return; 0491 } 0492 0493 KILE_DEBUG_MAIN; 0494 if(!isCurrentDocumentOrProject(doc)) { 0495 return; 0496 } 0497 0498 stopLivePreview(); 0499 showPreviewOutOfDate(); 0500 0501 if(!KileConfig::livePreviewCompileOnlyAfterSaving()) { 0502 m_documentChangedTimer->start(KileConfig::livePreviewCompilationDelay()); 0503 } 0504 } 0505 0506 void LivePreviewManager::handleDocumentSavedOrUploaded(KTextEditor::Document *doc, bool savedAs) 0507 { 0508 Q_UNUSED(savedAs); 0509 0510 if(m_bootUpMode || !KileConfig::livePreviewEnabled()) { 0511 return; 0512 } 0513 0514 KILE_DEBUG_MAIN; 0515 0516 if(!KileConfig::livePreviewCompileOnlyAfterSaving()) { 0517 return; 0518 } 0519 0520 if(!isCurrentDocumentOrProject(doc)) { 0521 return; 0522 } 0523 KTextEditor::View *view = m_ki->viewManager()->currentTextView(); 0524 KileDocument::LaTeXInfo *latexInfo = dynamic_cast<KileDocument::LaTeXInfo*>(m_ki->docManager()->textInfoFor(view->document())); 0525 if(!latexInfo) { 0526 return; 0527 } 0528 0529 LivePreviewUserStatusHandler *userStatusHandler; 0530 findPreviewInformation(latexInfo, Q_NULLPTR, &userStatusHandler); 0531 Q_ASSERT(userStatusHandler); 0532 if(userStatusHandler->isLivePreviewEnabled()) { 0533 showPreviewCompileIfNecessary(latexInfo, view); 0534 } 0535 } 0536 0537 void LivePreviewManager::handleDocumentModificationTimerTimeout() 0538 { 0539 if(m_bootUpMode || !KileConfig::livePreviewEnabled()) { 0540 return; 0541 } 0542 0543 KILE_DEBUG_MAIN; 0544 0545 KTextEditor::View *view = m_ki->viewManager()->currentTextView(); 0546 KileDocument::LaTeXInfo *latexInfo = dynamic_cast<KileDocument::LaTeXInfo*>(m_ki->docManager()->textInfoFor(view->document())); 0547 if(!latexInfo) { 0548 return; 0549 } 0550 0551 KTextEditor::CodeCompletionInterface *codeCompletionInterface = qobject_cast<KTextEditor::CodeCompletionInterface*>(view); 0552 0553 // if the code completion box is currently shown, we don't trigger an update of the preview 0554 // as this will cause the document to be saved and the completion box to be hidden as a consequence 0555 if(codeCompletionInterface && codeCompletionInterface->isCompletionActive()) { 0556 m_documentChangedTimer->start(); 0557 return; 0558 } 0559 0560 LivePreviewUserStatusHandler *userStatusHandler; 0561 findPreviewInformation(latexInfo, Q_NULLPTR, &userStatusHandler); 0562 Q_ASSERT(userStatusHandler); 0563 if(userStatusHandler->isLivePreviewEnabled()) { 0564 compilePreview(latexInfo, view); 0565 } 0566 } 0567 0568 void LivePreviewManager::showPreviewDisabled() 0569 { 0570 KILE_DEBUG_MAIN; 0571 m_ledBlinkingTimer->stop(); 0572 if(m_previewStatusLed) { 0573 m_previewStatusLed->off(); 0574 } 0575 } 0576 0577 void LivePreviewManager::showPreviewRunning() 0578 { 0579 KILE_DEBUG_MAIN; 0580 if(m_previewStatusLed) { 0581 m_previewStatusLed->setColor(QColor(Qt::yellow)); 0582 m_previewStatusLed->off(); 0583 } 0584 m_ledBlinkingTimer->start(); 0585 } 0586 0587 void LivePreviewManager::showPreviewFailed() 0588 { 0589 KILE_DEBUG_MAIN; 0590 m_ledBlinkingTimer->stop(); 0591 if(m_previewStatusLed) { 0592 m_previewStatusLed->on(); 0593 m_previewStatusLed->setColor(QColor(Qt::red)); 0594 } 0595 } 0596 0597 void LivePreviewManager::showPreviewSuccessful() 0598 { 0599 KILE_DEBUG_MAIN; 0600 m_ledBlinkingTimer->stop(); 0601 if(m_previewStatusLed) { 0602 m_previewStatusLed->on(); 0603 m_previewStatusLed->setColor(QColor(Qt::green)); 0604 } 0605 } 0606 0607 void LivePreviewManager::showPreviewOutOfDate() 0608 { 0609 KILE_DEBUG_MAIN; 0610 m_ledBlinkingTimer->stop(); 0611 if(m_previewStatusLed) { 0612 m_previewStatusLed->on(); 0613 m_previewStatusLed->setColor(QColor(Qt::yellow)); 0614 } 0615 0616 } 0617 0618 // If a LaTeXInfo* pointer is passed as first argument, it is guaranteed that '*userStatusHandler' won't be Q_NULLPTR. 0619 LivePreviewManager::PreviewInformation* LivePreviewManager::findPreviewInformation(KileDocument::TextInfo *textInfo, 0620 KileProject* *locatedProject, 0621 LivePreviewUserStatusHandler* *userStatusHandler, 0622 LaTeXOutputHandler* *latexOutputHandler) 0623 { 0624 const QString masterDocumentFileName = m_ki->getMasterDocumentFileName(); 0625 if(locatedProject) { 0626 *locatedProject = Q_NULLPTR; 0627 } 0628 KileDocument::LaTeXInfo *latexInfo = dynamic_cast<KileDocument::LaTeXInfo*>(textInfo); 0629 if(userStatusHandler) { 0630 *userStatusHandler = latexInfo; 0631 } 0632 if(latexOutputHandler) { 0633 *latexOutputHandler = latexInfo; 0634 } 0635 if(!masterDocumentFileName.isEmpty()) { 0636 KILE_DEBUG_MAIN << "master document defined"; 0637 return m_masterDocumentPreviewInformation; 0638 } 0639 KileProject *project = m_ki->docManager()->projectForMember(textInfo->url()); 0640 if(project) { 0641 KILE_DEBUG_MAIN << "part of a project"; 0642 if(locatedProject) { 0643 *locatedProject = project; 0644 } 0645 if(userStatusHandler) { 0646 *userStatusHandler = project; 0647 } 0648 if(latexOutputHandler) { 0649 *latexOutputHandler = project; 0650 } 0651 if(m_projectToPreviewInformationHash.contains(project)) { 0652 KILE_DEBUG_MAIN << "project found"; 0653 return m_projectToPreviewInformationHash[project]; 0654 } 0655 else { 0656 KILE_DEBUG_MAIN << "project not found"; 0657 return Q_NULLPTR; 0658 } 0659 } 0660 else if(latexInfo && m_latexInfoToPreviewInformationHash.contains(latexInfo)) { 0661 KILE_DEBUG_MAIN << "not part of a project"; 0662 return m_latexInfoToPreviewInformationHash[latexInfo]; 0663 } 0664 else { 0665 KILE_DEBUG_MAIN << "not found"; 0666 return Q_NULLPTR; 0667 } 0668 } 0669 0670 bool LivePreviewManager::isCurrentDocumentOrProject(KTextEditor::Document *doc) 0671 { 0672 const KTextEditor::View *currentView = m_ki->viewManager()->currentTextView(); 0673 0674 if(currentView->document() != doc) { 0675 const KileProject *project = m_ki->docManager()->projectForMember(doc->url()); 0676 const KileProject *currentProject = m_ki->docManager()->activeProject(); 0677 if(!currentProject || (project != currentProject)) { 0678 return false; 0679 } 0680 } 0681 0682 return true; 0683 } 0684 0685 void LivePreviewManager::showCursorPositionInDocumentViewer() 0686 { 0687 KTextEditor::View *view = m_ki->viewManager()->currentTextView(); 0688 if(!view) { 0689 return; 0690 } 0691 KileDocument::LaTeXInfo *latexInfo = dynamic_cast<KileDocument::LaTeXInfo*>(m_ki->docManager()->textInfoFor(view->document())); 0692 if(!latexInfo) { 0693 return; 0694 } 0695 LivePreviewUserStatusHandler *userStatusHandler = Q_NULLPTR; 0696 findPreviewInformation(latexInfo, Q_NULLPTR, &userStatusHandler); 0697 if(!userStatusHandler->isLivePreviewEnabled()) { 0698 return; 0699 } 0700 0701 synchronizeViewWithCursor(latexInfo, view, view->cursorPosition(), true); // called from a cursor position change 0702 } 0703 0704 // Note: this method won't open a document again if it's open already 0705 bool LivePreviewManager::ensureDocumentIsOpenInViewer(PreviewInformation *previewInformation, bool *hadToOpen) 0706 { 0707 if(hadToOpen) { 0708 *hadToOpen = false; 0709 } 0710 const QFile previewFileInfo(previewInformation->previewFile); 0711 if(!m_ki->viewManager()->viewerPart() || !previewFileInfo.exists() || previewFileInfo.size() == 0) { 0712 return false; 0713 } 0714 const QUrl previewUrl(QUrl::fromLocalFile(previewInformation->previewFile)); 0715 if(m_ki->viewManager()->viewerPart()->url().isEmpty() || m_ki->viewManager()->viewerPart()->url() != previewUrl) { 0716 KILE_DEBUG_MAIN << "loading again"; 0717 if(m_ki->viewManager()->viewerPart()->openUrl(previewUrl)) { 0718 if(hadToOpen) { 0719 *hadToOpen = true; 0720 } 0721 // don't forget this 0722 m_shownPreviewInformation = previewInformation; 0723 return true; 0724 } 0725 else { 0726 m_shownPreviewInformation = Q_NULLPTR; 0727 return false; 0728 } 0729 } 0730 return true; 0731 } 0732 0733 void LivePreviewManager::synchronizeViewWithCursor(KileDocument::TextInfo *textInfo, KTextEditor::View *view, 0734 const KTextEditor::Cursor& newPosition, 0735 bool calledFromCursorPositionChange) 0736 { 0737 Q_UNUSED(view); 0738 KILE_DEBUG_MAIN << "new position " << newPosition; 0739 0740 PreviewInformation *previewInformation = findPreviewInformation(textInfo); 0741 if(!previewInformation) { 0742 KILE_DEBUG_MAIN << "couldn't find preview information for" << textInfo; 0743 return; 0744 } 0745 0746 QFileInfo updatedFileInfo(textInfo->getDoc()->url().toLocalFile()); 0747 QString filePath; 0748 if(previewInformation->pathToPreviewPathHash.contains(updatedFileInfo.absoluteFilePath())) { 0749 KILE_DEBUG_MAIN << "found"; 0750 filePath = previewInformation->pathToPreviewPathHash[updatedFileInfo.absoluteFilePath()]; 0751 } 0752 else { 0753 KILE_DEBUG_MAIN << "not found"; 0754 filePath = textInfo->getDoc()->url().toLocalFile(); 0755 } 0756 KILE_DEBUG_MAIN << "filePath" << filePath; 0757 0758 KILE_DEBUG_MAIN << "previewFile" << previewInformation->previewFile; 0759 0760 if(!m_ki->viewManager()->viewerPart() || !QFile::exists(previewInformation->previewFile)) { 0761 return; 0762 } 0763 0764 KILE_DEBUG_MAIN << "url" << m_ki->viewManager()->viewerPart()->url(); 0765 0766 if(!ensureDocumentIsOpenInViewer(previewInformation)) { 0767 clearLivePreview(); 0768 // must happen after the call to 'clearLivePreview' only 0769 showPreviewFailed(); 0770 emit(livePreviewStopped()); 0771 return; 0772 } 0773 0774 0775 // to increase the performance, if 'calledFromCursorPositionChange' is true, we only synchronize when the cursor line 0776 // has changed from the last synchronization 0777 // NOTE: the performance of SyncTeX has to be improved if changes in cursor columns should be taken into account as 0778 // well (bug 305254) 0779 if(!calledFromCursorPositionChange || (previewInformation->lastSynchronizationCursor.line() != newPosition.line())) { 0780 m_ki->viewManager()->showSourceLocationInDocumentViewer(filePath, newPosition.line(), newPosition.column()); 0781 previewInformation->setLastSynchronizationCursor(newPosition.line(), newPosition.column()); 0782 } 0783 } 0784 0785 void LivePreviewManager::reloadDocumentInViewer() 0786 { 0787 if(!m_ki->viewManager()->viewerPart()) { 0788 return; 0789 } 0790 0791 //FIXME ideally, this method should be integrated in an interface extending Okular... 0792 QMetaObject::invokeMethod(m_ki->viewManager()->viewerPart(), "reload"); 0793 } 0794 0795 0796 static QByteArray computeHashOfDocument(KTextEditor::Document *doc) 0797 { 0798 QCryptographicHash cryptographicHash(QCryptographicHash::Sha1); 0799 cryptographicHash.addData(doc->text().toUtf8()); 0800 // allows to catch situations when the URL of the document has changed, 0801 // e.g. after a save-as operation, which breaks the handling of source 0802 // references for the displayed document 0803 cryptographicHash.addData(doc->url().toEncoded()); 0804 0805 return cryptographicHash.result(); 0806 } 0807 0808 static void fillTextHashForProject(KileProject *project, QHash<KileDocument::TextInfo*, QByteArray> &textHash) 0809 { 0810 QList<KileProjectItem*> list = project->items(); 0811 for(QList<KileProjectItem*>::iterator it = list.begin(); it != list.end(); ++it) { 0812 KileProjectItem *item = *it; 0813 0814 KileDocument::TextInfo *textInfo = item->getInfo(); 0815 if(!textInfo) { 0816 continue; 0817 } 0818 KTextEditor::Document *document = textInfo->getDoc(); 0819 if(!document) { 0820 continue; 0821 } 0822 textHash[textInfo] = computeHashOfDocument(document); 0823 } 0824 } 0825 0826 void LivePreviewManager::fillTextHashForMasterDocument(QHash<KileDocument::TextInfo*, QByteArray> &textHash) 0827 { 0828 // we compute hashes over all the opened files 0829 QList<KileDocument::TextInfo*> textDocumentInfos = m_ki->docManager()->textDocumentInfos(); 0830 for(QList<KileDocument::TextInfo*>::iterator it = textDocumentInfos.begin(); it != textDocumentInfos.end(); ++it) { 0831 KileDocument::TextInfo *textInfo = *it; 0832 if(!textInfo) { 0833 continue; 0834 } 0835 KTextEditor::Document *document = textInfo->getDoc(); 0836 if(!document) { 0837 continue; 0838 } 0839 textHash[textInfo] = computeHashOfDocument(document); 0840 } 0841 } 0842 0843 void LivePreviewManager::showPreviewCompileIfNecessary(KileDocument::LaTeXInfo *latexInfo, KTextEditor::View *view) 0844 { 0845 KILE_DEBUG_MAIN; 0846 // first, stop any running live preview 0847 stopLivePreview(); 0848 0849 KileProject *project = Q_NULLPTR; 0850 LivePreviewUserStatusHandler *userStatusHandler = Q_NULLPTR; 0851 PreviewInformation *previewInformation = findPreviewInformation(latexInfo, &project, &userStatusHandler); 0852 if(!previewInformation) { 0853 KILE_DEBUG_MAIN << "not found"; 0854 compilePreview(latexInfo, view); 0855 } 0856 else { 0857 Q_ASSERT(userStatusHandler); 0858 updateLivePreviewToolActions(userStatusHandler); 0859 QHash<KileDocument::TextInfo*, QByteArray> newHash; 0860 // QString fileName; 0861 // QFileInfo fileInfo(view->document()->url().path()); 0862 // if(previewInformation->pathToPreviewPathHash.contains(fileInfo.absoluteFilePath())) { 0863 // KILE_DEBUG_MAIN << "contains"; 0864 // fileName = previewInformation->pathToPreviewPathHash[fileInfo.absoluteFilePath()]; 0865 // } 0866 // else { 0867 // KILE_DEBUG_MAIN << "does not contain"; 0868 // fileName = fileInfo.absoluteFilePath(); 0869 // } 0870 // KILE_DEBUG_MAIN << "fileName:" << fileName; 0871 bool masterDocumentSet = !m_ki->getMasterDocumentFileName().isEmpty(); 0872 0873 if(masterDocumentSet) { 0874 fillTextHashForMasterDocument(newHash); 0875 } 0876 else if(project) { 0877 fillTextHashForProject(project, newHash); 0878 } 0879 else { 0880 newHash[latexInfo] = computeHashOfDocument(view->document()); 0881 } 0882 0883 if(newHash != previewInformation->textHash || !QFile::exists(previewInformation->previewFile)) { 0884 KILE_DEBUG_MAIN << "hashes don't match"; 0885 compilePreview(latexInfo, view); 0886 } 0887 else { 0888 KILE_DEBUG_MAIN << "hashes match"; 0889 showPreviewSuccessful(); 0890 synchronizeViewWithCursor(latexInfo, view, view->cursorPosition()); 0891 emit(livePreviewSuccessful()); 0892 } 0893 } 0894 } 0895 0896 void LivePreviewManager::compilePreview(KileDocument::LaTeXInfo *latexInfo, KTextEditor::View *view) 0897 { 0898 KILE_DEBUG_MAIN << "updating preview"; 0899 m_ki->viewManager()->setLivePreviewModeForDocumentViewer(true); 0900 m_runningPathToPreviewPathHash.clear(); 0901 m_runningPreviewPathToPathHash.clear(); 0902 0903 //CAUTION: as saving launches an event loop, we don't want 'compilePreview' 0904 // to be called from within 'compilePreview' 0905 m_documentChangedTimer->blockSignals(true); 0906 bool saveResult = m_ki->docManager()->fileSaveAll(); 0907 m_documentChangedTimer->blockSignals(false); 0908 // first, we have to save the documents 0909 if(!saveResult) { 0910 displayErrorMessage(i18n("Some documents could not be saved correctly")); 0911 return; 0912 } 0913 0914 // document is new and hasn't been saved yet at all 0915 if(view->document()->url().isEmpty()) { 0916 displayErrorMessage(i18n("The document must have been saved before the live preview can be started")); 0917 return; 0918 } 0919 0920 // first, stop any running live preview 0921 stopLivePreview(); 0922 0923 KileProject *project = Q_NULLPTR; 0924 LivePreviewUserStatusHandler *userStatusHandler; 0925 LaTeXOutputHandler *latexOutputHandler; 0926 PreviewInformation *previewInformation = findPreviewInformation(latexInfo, &project, &userStatusHandler, &latexOutputHandler); 0927 Q_ASSERT(userStatusHandler); 0928 Q_ASSERT(latexOutputHandler); 0929 if(!previewInformation) { 0930 previewInformation = new PreviewInformation(); 0931 if(!m_ki->getMasterDocumentFileName().isEmpty()) { 0932 m_masterDocumentPreviewInformation = previewInformation; 0933 } 0934 else if(project) { 0935 bool containsInvalidRelativeItem = false; 0936 // in the case of a project, we might have to create a similar subdirectory 0937 // structure as it is present in the real project in order for LaTeX 0938 // to work correctly 0939 if(!previewInformation->createSubDirectoriesForProject(project, &containsInvalidRelativeItem)) { 0940 userStatusHandler->setLivePreviewEnabled(false); 0941 if(containsInvalidRelativeItem) { 0942 displayErrorMessage(i18n("The location of one project item is not relative to the project's base directory\n" 0943 "Live preview for this project has been disabled"), true); 0944 } 0945 else { 0946 displayErrorMessage(i18n("Failed to create the subdirectory structure")); 0947 } 0948 delete previewInformation; 0949 disablePreview(); 0950 return; 0951 } 0952 m_projectToPreviewInformationHash[project] = previewInformation; 0953 } 0954 else { 0955 m_latexInfoToPreviewInformationHash[latexInfo] = previewInformation; 0956 } 0957 } 0958 0959 connect(latexInfo, SIGNAL(aboutToBeDestroyed(KileDocument::TextInfo*)), 0960 this, SLOT(removeLaTeXInfo(KileDocument::TextInfo*)), 0961 Qt::UniqueConnection); 0962 0963 if(project) { 0964 handleProjectOpened(project); // create the necessary signal-slot connections 0965 } 0966 0967 updateLivePreviewToolActions(userStatusHandler); 0968 KileTool::LivePreviewLaTeX *latex = dynamic_cast<KileTool::LivePreviewLaTeX *>(m_ki->toolManager()->createTool(userStatusHandler->livePreviewTool(), 0969 false)); 0970 if(!latex) { 0971 KILE_DEBUG_MAIN<< "couldn't create the live preview tool"; 0972 return; 0973 } 0974 0975 // important! 0976 latex->setPartOfLivePreview(); 0977 connect(latex, SIGNAL(done(KileTool::Base*,int,bool)), this, SLOT(toolDone(KileTool::Base*,int,bool))); 0978 connect(latex, SIGNAL(destroyed()), this, SLOT(toolDestroyed())); 0979 0980 QFileInfo fileInfo; 0981 const bool masterDocumentSet = !m_ki->getMasterDocumentFileName().isEmpty(); 0982 if(masterDocumentSet) { 0983 fileInfo = QFileInfo(m_ki->getMasterDocumentFileName()); 0984 } 0985 else if(project) { 0986 fileInfo = QFileInfo(m_ki->getCompileNameForProject(project)); 0987 } 0988 else { 0989 fileInfo = QFileInfo(m_ki->getCompileName()); 0990 } 0991 0992 const QString inputDir = previewInformation->getTempDir() + LIST_SEPARATOR + fileInfo.absolutePath(); 0993 0994 // set value of texinput path (only for LivePreviewManager tools) 0995 QString texInputPath = KileConfig::teXPaths(); 0996 if(!texInputPath.isEmpty()) { 0997 texInputPath = inputDir + LIST_SEPARATOR + texInputPath; 0998 } 0999 else { 1000 texInputPath = inputDir; 1001 } 1002 latex->setTeXInputPaths(texInputPath); 1003 1004 QString bibInputPath = KileConfig::bibInputPaths(); 1005 if(!bibInputPath.isEmpty()) { 1006 bibInputPath = inputDir + LIST_SEPARATOR + bibInputPath; 1007 } 1008 else { 1009 bibInputPath = inputDir; 1010 } 1011 latex->setBibInputPaths(bibInputPath); 1012 1013 QString bstInputPath = KileConfig::bstInputPaths(); 1014 if(!bstInputPath.isEmpty()) { 1015 bstInputPath = inputDir + LIST_SEPARATOR + bstInputPath; 1016 } 1017 else { 1018 bstInputPath = inputDir; 1019 } 1020 latex->setBstInputPaths(bstInputPath); 1021 1022 // m_runningPathToPreviewPathHash[fileInfo.absoluteFilePath()] = tempFile; 1023 // m_runningPreviewPathToPathHash[tempFile] = fileInfo.absoluteFilePath(); 1024 1025 // don't emit the 'requestSaveAll' signal 1026 // latex->removeFlag(EmitSaveAllSignal); 1027 1028 latex->setTargetDir(previewInformation->getTempDir()); 1029 latex->setSource(fileInfo.absoluteFilePath(), fileInfo.absolutePath()); 1030 latex->setLaTeXOutputHandler(latexOutputHandler); 1031 1032 latex->prepareToRun(); 1033 // latex->launcher()->setWorkingDirectory(previewInformation->getTempDir()); 1034 KILE_DEBUG_MAIN << "dir:" << previewInformation->getTempDir(); 1035 1036 m_runningTextView = view; 1037 m_runningLaTeXInfo = latexInfo; 1038 m_runningProject = project; 1039 m_runningPreviewFile = previewInformation->getTempDir() + '/' + latex->target(); 1040 m_runningTextHash.clear(); 1041 if(masterDocumentSet) { 1042 fillTextHashForMasterDocument(m_runningTextHash); 1043 } 1044 else if(project) { 1045 fillTextHashForProject(project, m_runningTextHash); 1046 } 1047 else { 1048 m_runningTextHash[latexInfo] = computeHashOfDocument(latexInfo->getDoc()); 1049 } 1050 m_runningPreviewInformation = previewInformation; 1051 showPreviewRunning(); 1052 1053 // finally, run the tool 1054 m_ki->toolManager()->run(latex); 1055 emit(livePreviewRunning()); 1056 } 1057 1058 bool LivePreviewManager::isLivePreviewActive() const 1059 { 1060 KParts::ReadOnlyPart *viewerPart = m_ki->viewManager()->viewerPart(); 1061 1062 return m_runningPreviewInformation 1063 || (m_shownPreviewInformation 1064 && viewerPart 1065 && viewerPart->url() == QUrl::fromLocalFile(m_shownPreviewInformation->previewFile)); 1066 } 1067 1068 bool LivePreviewManager::isLivePreviewPossible() const 1069 { 1070 return true; 1071 } 1072 1073 void LivePreviewManager::handleDocumentOpened(KileDocument::TextInfo *info) 1074 { 1075 if(m_bootUpMode || !KileConfig::livePreviewEnabled()) { 1076 return; 1077 } 1078 1079 KTextEditor::View *view = m_ki->viewManager()->currentTextView(); 1080 if(view && view->document() == info->getDoc()) { 1081 handleTextViewActivated(view); 1082 } 1083 } 1084 1085 void LivePreviewManager::handleTextViewActivated(KTextEditor::View *view, bool clearPreview, bool forceCompilation) 1086 { 1087 // when a file is currently being opened, we don't react to the view activation signal as the correct live preview 1088 // tools might not be loaded yet for the document that belongs to 'view' 1089 if(m_bootUpMode || !KileConfig::livePreviewEnabled() || m_ki->docManager()->isOpeningFile()) { 1090 return; 1091 } 1092 if(clearPreview) { 1093 stopAndClearPreview(); 1094 } 1095 else { 1096 stopLivePreview(); 1097 } 1098 KileDocument::LaTeXInfo *latexInfo = dynamic_cast<KileDocument::LaTeXInfo*>(m_ki->docManager()->textInfoFor(view->document())); 1099 if(!latexInfo) { 1100 return; 1101 } 1102 m_documentChangedTimer->stop(); 1103 1104 LivePreviewUserStatusHandler *userStatusHandler = Q_NULLPTR; 1105 findPreviewInformation(latexInfo, Q_NULLPTR, &userStatusHandler); 1106 Q_ASSERT(userStatusHandler); 1107 const bool livePreviewActive = userStatusHandler->isLivePreviewEnabled(); 1108 updateLivePreviewToolActions(userStatusHandler); 1109 // update the state of the live preview control button 1110 m_previewForCurrentDocumentAction->setChecked(livePreviewActive); 1111 1112 if(!livePreviewActive) { 1113 disablePreview(); 1114 } 1115 else { 1116 if(forceCompilation) { 1117 compilePreview(latexInfo, view); 1118 } 1119 else { 1120 showPreviewCompileIfNecessary(latexInfo, view); 1121 } 1122 } 1123 } 1124 1125 void LivePreviewManager::handleTextViewClosed(KTextEditor::View *view, bool wasActiveView) 1126 { 1127 Q_UNUSED(view); 1128 Q_UNUSED(wasActiveView); 1129 1130 if(m_bootUpMode || !KileConfig::livePreviewEnabled()) { 1131 return; 1132 } 1133 1134 // check if there is still an open editor tab 1135 if(!KTextEditor::Editor::instance()->application()->activeMainWindow()->activeView()) { 1136 stopAndClearPreview(); 1137 } 1138 } 1139 1140 void LivePreviewManager::refreshLivePreview() 1141 { 1142 KTextEditor::View *textView = m_ki->viewManager()->currentTextView(); 1143 if(!textView) { 1144 KILE_DEBUG_MAIN << "no text view is shown; hence, no preview can be shown"; 1145 return; 1146 } 1147 handleTextViewActivated(textView, false); // don't automatically clear the preview 1148 } 1149 1150 void LivePreviewManager::recompileLivePreview() 1151 { 1152 KTextEditor::View *textView = m_ki->viewManager()->currentTextView(); 1153 if(!textView) { 1154 KILE_DEBUG_MAIN << "no text view is shown; hence, no preview can be shown"; 1155 return; 1156 } 1157 handleTextViewActivated(textView, false, true); // don't automatically clear the preview but force compilation 1158 } 1159 1160 void LivePreviewManager::removeLaTeXInfo(KileDocument::TextInfo *textInfo) 1161 { 1162 KileDocument::LaTeXInfo *latexInfo = dynamic_cast<KileDocument::LaTeXInfo*>(textInfo); 1163 if(!latexInfo) { 1164 return; 1165 } 1166 1167 if(!m_latexInfoToPreviewInformationHash.contains(latexInfo)) { 1168 return; // nothing to be done 1169 } 1170 1171 PreviewInformation *previewInformation = m_latexInfoToPreviewInformationHash[latexInfo]; 1172 1173 if(m_runningLaTeXInfo == latexInfo) { 1174 stopLivePreview(); 1175 } 1176 1177 if(previewInformation == m_shownPreviewInformation) { 1178 clearLivePreview(); 1179 } 1180 1181 m_latexInfoToPreviewInformationHash.remove(latexInfo); 1182 delete previewInformation; 1183 } 1184 1185 void LivePreviewManager::removeProject(KileProject *project) 1186 { 1187 if(!m_projectToPreviewInformationHash.contains(project)) { 1188 return; // nothing to be done 1189 } 1190 1191 PreviewInformation *previewInformation = m_projectToPreviewInformationHash[project]; 1192 1193 if(m_runningProject == project) { 1194 stopLivePreview(); 1195 } 1196 1197 if(previewInformation == m_shownPreviewInformation) { 1198 clearLivePreview(); 1199 } 1200 1201 m_projectToPreviewInformationHash.remove(project); 1202 delete previewInformation; 1203 } 1204 1205 void LivePreviewManager::handleProjectOpened(KileProject *project) 1206 { 1207 if(m_bootUpMode || !KileConfig::livePreviewEnabled()) { 1208 return; 1209 } 1210 1211 connect(project, SIGNAL(aboutToBeDestroyed(KileProject*)), 1212 this, SLOT(removeProject(KileProject*)), 1213 Qt::UniqueConnection); 1214 connect(project, SIGNAL(projectItemAdded(KileProject*,KileProjectItem*)), 1215 this, SLOT(handleProjectItemAdded(KileProject*,KileProjectItem*)), 1216 Qt::UniqueConnection); 1217 connect(project, SIGNAL(projectItemRemoved(KileProject*,KileProjectItem*)), 1218 this, SLOT(handleProjectItemRemoved(KileProject*,KileProjectItem*)), 1219 Qt::UniqueConnection); 1220 } 1221 1222 void LivePreviewManager::handleProjectItemAdditionOrRemoval(KileProject *project, KileProjectItem *item) 1223 { 1224 if(m_bootUpMode || !KileConfig::livePreviewEnabled()) { 1225 return; 1226 } 1227 1228 KILE_DEBUG_MAIN; 1229 bool previewNeedsToBeRefreshed = false; 1230 1231 // we can't use TextInfo pointers here as they might not be set in 'item' yet 1232 KileDocument::LaTeXInfo *latexInfo = dynamic_cast<KileDocument::LaTeXInfo*>(m_ki->docManager()->textInfoFor(item->url())); 1233 if(latexInfo && m_latexInfoToPreviewInformationHash.contains(latexInfo)) { 1234 PreviewInformation *previewInformation = m_latexInfoToPreviewInformationHash[latexInfo]; 1235 if(previewInformation == m_shownPreviewInformation) { 1236 previewNeedsToBeRefreshed = true; 1237 } 1238 removeLaTeXInfo(latexInfo); 1239 } 1240 1241 if(m_projectToPreviewInformationHash.contains(project)) { 1242 PreviewInformation *previewInformation = m_projectToPreviewInformationHash[project]; 1243 if(previewInformation == m_shownPreviewInformation) { 1244 previewNeedsToBeRefreshed = true; 1245 } 1246 removeProject(project); 1247 } 1248 1249 // finally, check whether the currently activated text view is the 'modified' project item 1250 if(!previewNeedsToBeRefreshed) { 1251 KTextEditor::View *view = m_ki->viewManager()->currentTextView(); 1252 // we can't use TextInfo pointers here as they might not be set in 'item' yet 1253 if(view && (view->document()->url() == item->url())) { 1254 previewNeedsToBeRefreshed = true; 1255 } 1256 } 1257 1258 KILE_DEBUG_MAIN << "previewNeedsToBeRefreshed" << previewNeedsToBeRefreshed; 1259 if(previewNeedsToBeRefreshed) { 1260 // we can't do this here directly as 'item' might not be fully set up yet (e.g., if it has been added) 1261 QTimer::singleShot(0, this, SLOT(refreshLivePreview())); 1262 } 1263 } 1264 1265 void LivePreviewManager::handleProjectItemAdded(KileProject *project, KileProjectItem *item) 1266 { 1267 if(m_bootUpMode || !KileConfig::livePreviewEnabled()) { 1268 return; 1269 } 1270 KILE_DEBUG_MAIN; 1271 1272 // the directory structure in the temporary directory will be updated when 1273 // 'compilePreview' is called; 'handleProjectItemAdditionOrRemoval' will delete 1274 // PreviewInformation objects 1275 handleProjectItemAdditionOrRemoval(project, item); 1276 } 1277 1278 void LivePreviewManager::handleProjectItemRemoved(KileProject *project, KileProjectItem *item) 1279 { 1280 if(m_bootUpMode || !KileConfig::livePreviewEnabled()) { 1281 return; 1282 } 1283 1284 KILE_DEBUG_MAIN; 1285 handleProjectItemAdditionOrRemoval(project, item); 1286 } 1287 1288 void LivePreviewManager::handleDocumentSavedAs(KTextEditor::View *view, KileDocument::TextInfo *info) 1289 { 1290 Q_UNUSED(info); 1291 1292 if(m_bootUpMode || !KileConfig::livePreviewEnabled()) { 1293 return; 1294 } 1295 1296 KTextEditor::View *currentTextView = m_ki->viewManager()->currentTextView(); 1297 if(view != currentTextView) { // might maybe happen at some point... 1298 // preview will be refreshed the next time that view is activated as the hashes don't 1299 // match anymore 1300 return; 1301 } 1302 refreshLivePreview(); 1303 } 1304 1305 void LivePreviewManager::toolDestroyed() 1306 { 1307 KILE_DEBUG_MAIN << "\tLivePreviewManager: tool destroyed" << Qt::endl; 1308 } 1309 1310 void LivePreviewManager::handleSpawnedChildTool(KileTool::Base *parent, KileTool::Base *child) 1311 { 1312 Q_UNUSED(parent); 1313 1314 if(m_bootUpMode || !KileConfig::livePreviewEnabled()) { 1315 return; 1316 } 1317 1318 KILE_DEBUG_MAIN; 1319 // only connect the signal for tools that are part of live preview! 1320 if(parent->isPartOfLivePreview()) { 1321 connect(child, SIGNAL(done(KileTool::Base*,int,bool)), this, SLOT(childToolDone(KileTool::Base*,int,bool))); 1322 } 1323 } 1324 1325 void LivePreviewManager::toolDone(KileTool::Base *base, int i, bool childToolSpawned) 1326 { 1327 KILE_DEBUG_MAIN << "\t!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << i << Qt::endl; 1328 KILE_DEBUG_MAIN << "\t!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << i << Qt::endl; 1329 KILE_DEBUG_MAIN << "\tLivePreviewManager: tool done" << base->name() << i << childToolSpawned << Qt::endl; 1330 if(i != Success) { 1331 KILE_DEBUG_MAIN << "tool didn't return successfully, doing nothing"; 1332 showPreviewFailed(); 1333 clearRunningLivePreviewInformation(); 1334 emit(livePreviewStopped()); 1335 } 1336 // a LaTeX variant must have finished for the preview to be complete 1337 else if(!childToolSpawned && dynamic_cast<KileTool::LaTeX*>(base)) { 1338 updatePreviewInformationAfterCompilationFinished(); 1339 clearRunningLivePreviewInformation(); 1340 } 1341 } 1342 1343 void LivePreviewManager::childToolDone(KileTool::Base *base, int i, bool childToolSpawned) 1344 { 1345 KILE_DEBUG_MAIN << "\t!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << i << Qt::endl; 1346 KILE_DEBUG_MAIN << "\t!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << i << Qt::endl; 1347 KILE_DEBUG_MAIN << "\tLivePreviewManager: child tool done" << base->name() << i << childToolSpawned << Qt::endl; 1348 if(!m_ki->viewManager()->viewerPart()) { 1349 return; 1350 } 1351 if(i != Success) { 1352 KILE_DEBUG_MAIN << "tool didn't return successfully, doing nothing"; 1353 showPreviewFailed(); 1354 clearRunningLivePreviewInformation(); 1355 emit(livePreviewStopped()); 1356 } 1357 // a LaTeX variant must have finished for the preview to be complete 1358 else if(!childToolSpawned && dynamic_cast<KileTool::LaTeX*>(base)) { 1359 updatePreviewInformationAfterCompilationFinished(); 1360 clearRunningLivePreviewInformation(); 1361 } 1362 } 1363 1364 void LivePreviewManager::updatePreviewInformationAfterCompilationFinished() 1365 { 1366 if(!m_runningPreviewInformation) { // LivePreview has been stopped in the meantime 1367 return; 1368 } 1369 1370 m_shownPreviewInformation = m_runningPreviewInformation; 1371 m_shownPreviewInformation->pathToPreviewPathHash = m_runningPathToPreviewPathHash; 1372 m_shownPreviewInformation->previewPathToPathHash = m_runningPreviewPathToPathHash; 1373 m_shownPreviewInformation->textHash = m_runningTextHash; 1374 m_shownPreviewInformation->previewFile = m_runningPreviewFile; 1375 1376 m_runningPreviewInformation = Q_NULLPTR; 1377 1378 bool hadToOpen = false; 1379 if(!ensureDocumentIsOpenInViewer(m_shownPreviewInformation, &hadToOpen)) { 1380 clearLivePreview(); 1381 // must happen after the call to 'clearLivePreview' only 1382 showPreviewFailed(); 1383 emit(livePreviewStopped()); 1384 return; 1385 } 1386 1387 // as 'ensureDocumentIsOpenInViewer' won't reload when the document is open 1388 // already, we have to do it here 1389 if(!hadToOpen) { 1390 reloadDocumentInViewer(); 1391 } 1392 1393 if(m_ki->viewManager()->isSynchronisingCursorWithDocumentViewer()) { 1394 synchronizeViewWithCursor(m_runningLaTeXInfo, m_runningTextView, m_runningTextView->cursorPosition()); 1395 } 1396 1397 showPreviewSuccessful(); 1398 emit(livePreviewSuccessful()); 1399 } 1400 1401 void LivePreviewManager::displayErrorMessage(const QString &text, bool clearFirst) 1402 { 1403 if(clearFirst) { 1404 m_ki->errorHandler()->clearMessages(); 1405 } 1406 m_ki->errorHandler()->printMessage(KileTool::Error, text, i18n("LivePreview")); 1407 } 1408 1409 } 1410