File indexing completed on 2024-04-21 15:55:34
0001 /************************************************************************* 0002 Copyright (C) 2003 by Jeroen Wijnhout (Jeroen.Wijnhout@kdemail.net) 0003 2008-2016 by Michel Ludwig (michel.ludwig@kdemail.net) 0004 *************************************************************************/ 0005 0006 /*************************************************************************** 0007 * * 0008 * This program is free software; you can redistribute it and/or modify * 0009 * it under the terms of the GNU General Public License as published by * 0010 * the Free Software Foundation; either version 2 of the License, or * 0011 * (at your option) any later version. * 0012 * * 0013 ***************************************************************************/ 0014 0015 #include "errorhandler.h" 0016 0017 #include <QFileInfo> 0018 #include <QHash> 0019 #include <QLabel> 0020 #include <QMenu> 0021 #include <QRegExp> 0022 #include <QTabWidget> 0023 #include <QToolBar> 0024 #include <QToolButton> 0025 0026 #include <KActionCollection> 0027 #include <KLocalizedString> 0028 #include <QUrl> 0029 #include <KTextEditor/Document> 0030 #include <KTextEditor/View> 0031 #include <KSelectAction> 0032 #include "kiledocmanager.h" 0033 #include "kileinfo.h" 0034 #include "kileproject.h" 0035 #include "kiletool_enums.h" 0036 #include "kileviewmanager.h" 0037 #include "outputinfo.h" 0038 #include "utilities.h" 0039 #include "widgets/logwidget.h" 0040 #include "widgets/outputview.h" 0041 #include "widgets/sidebar.h" 0042 0043 KileErrorHandler::KileErrorHandler(QObject *parent, KileInfo *info, KActionCollection *ac) 0044 : QObject(parent), m_ki(info), m_errorHanderToolBar(Q_NULLPTR), m_currentLaTeXOutputHandler(Q_NULLPTR) 0045 { 0046 setObjectName("ErrorHandler"); 0047 0048 createActions(ac); 0049 setOutputActionsEnabled(false); 0050 0051 m_compilationResultLabel = new QLabel(); 0052 0053 m_mainLogWidget = new KileWidget::LogWidget(); 0054 m_mainLogWidget->setFocusPolicy(Qt::ClickFocus); 0055 m_mainLogWidget->setMinimumHeight(40); 0056 0057 connect(m_mainLogWidget, SIGNAL(outputInfoSelected(OutputInfo)), 0058 this, SLOT(jumpToProblem(OutputInfo))); 0059 connect(m_mainLogWidget, SIGNAL(showingErrorMessage(QWidget*)), 0060 this, SIGNAL(showingErrorMessage(QWidget*))); 0061 connect(m_mainLogWidget, SIGNAL(showingErrorMessage(QWidget*)), 0062 this, SLOT(showMessagesOutput())); 0063 m_errorLogWidget = new KileWidget::LogWidget(KileWidget::LogWidget::NoHideActions); 0064 connect(m_errorLogWidget, SIGNAL(outputInfoSelected(OutputInfo)), 0065 this, SLOT(jumpToProblem(OutputInfo))); 0066 m_warningLogWidget = new KileWidget::LogWidget(KileWidget::LogWidget::NoHideActions); 0067 connect(m_warningLogWidget, SIGNAL(outputInfoSelected(OutputInfo)), 0068 this, SLOT(jumpToProblem(OutputInfo))); 0069 m_badBoxLogWidget = new KileWidget::LogWidget(KileWidget::LogWidget::NoHideActions); 0070 connect(m_badBoxLogWidget, SIGNAL(outputInfoSelected(OutputInfo)), 0071 this, SLOT(jumpToProblem(OutputInfo))); 0072 0073 // FIXME: suggestions for icons: utilities-log-viewer, script-error, dialog-warning 0074 m_outputTabWidget = new QTabWidget(); 0075 m_outputTabWidget->setTabPosition(QTabWidget::South); 0076 m_outputTabWidget->setTabsClosable(false); 0077 m_outputTabWidget->addTab(m_mainLogWidget, i18n("Messages")); 0078 m_outputTabWidget->addTab(m_errorLogWidget, i18n("Errors")); 0079 m_outputTabWidget->addTab(m_warningLogWidget, i18n("Warnings")); 0080 m_outputTabWidget->addTab(m_badBoxLogWidget, i18n("BadBoxes")); 0081 0082 connect(m_ki->viewManager(), SIGNAL(textViewActivated(KTextEditor::View*)), 0083 this, SLOT(updateCurrentLaTeXOutputHandler())); 0084 connect(m_ki->viewManager(), SIGNAL(textViewClosed(KTextEditor::View*,bool)), 0085 this, SLOT(updateCurrentLaTeXOutputHandler())); 0086 connect(m_ki->docManager(), SIGNAL(documentOpened(KileDocument::TextInfo*)), 0087 this, SLOT(updateCurrentLaTeXOutputHandler())); 0088 connect(m_ki->docManager(), SIGNAL(projectOpened(KileProject*)), 0089 this, SLOT(handleProjectOpened(KileProject*))); 0090 0091 showMessagesOutput(); 0092 } 0093 0094 KileErrorHandler::~KileErrorHandler() 0095 { 0096 } 0097 0098 void KileErrorHandler::createActions(KActionCollection *ac) 0099 { 0100 m_viewLogAction = ac->addAction("ViewLog", this, SLOT(ViewLog())); 0101 m_viewLogAction->setText(i18n("View Log File")); 0102 ac->setDefaultShortcut(m_viewLogAction, QKeySequence(Qt::ALT + Qt::Key_0)); 0103 m_viewLogAction->setIcon(QIcon::fromTheme("viewlog")); 0104 0105 m_previousErrorAction = ac->addAction("PreviousError", this, SLOT(PreviousError())); 0106 m_previousErrorAction->setText(i18n("Previous LaTeX Error")); 0107 m_previousErrorAction->setIcon(QIcon::fromTheme("errorprev")); 0108 0109 m_nextErrorAction = ac->addAction("NextError", this, SLOT(NextError())); 0110 m_nextErrorAction->setText(i18n("Next LaTeX Error")); 0111 m_nextErrorAction->setIcon(QIcon::fromTheme("errornext")); 0112 0113 m_previousWarningAction = ac->addAction("PreviousWarning", this, SLOT(PreviousWarning())); 0114 m_previousWarningAction->setText(i18n("Previous LaTeX Warning")); 0115 m_previousWarningAction->setIcon(QIcon::fromTheme("warnprev")); 0116 0117 m_nextWarningAction = ac->addAction("NextWarning", this, SLOT(NextWarning())); 0118 m_nextWarningAction->setText(i18n("Next LaTeX Warnings")); 0119 m_nextWarningAction->setIcon(QIcon::fromTheme("warnnext")); 0120 0121 m_previousBadBoxAction = ac->addAction("PreviousBadBox", this, SLOT(PreviousBadBox())); 0122 m_previousBadBoxAction->setText(i18n("Previous LaTeX BadBox")); 0123 m_previousBadBoxAction->setIcon(QIcon::fromTheme("bboxprev")); 0124 0125 m_nextBadBoxAction = ac->addAction("NextBadBox", this, SLOT(NextBadBox())); 0126 m_nextBadBoxAction->setText(i18n("Next LaTeX BadBox")); 0127 m_nextBadBoxAction->setIcon(QIcon::fromTheme("bboxnext")); 0128 } 0129 0130 void KileErrorHandler::setErrorHandlerToolBar(QToolBar *toolBar) 0131 { 0132 m_errorHanderToolBar = toolBar; 0133 toolBar->addAction(m_viewLogAction); 0134 toolBar->addAction(m_previousErrorAction); 0135 toolBar->addAction(m_nextErrorAction); 0136 toolBar->addAction(m_previousWarningAction); 0137 toolBar->addAction(m_nextWarningAction); 0138 toolBar->addAction(m_previousBadBoxAction); 0139 toolBar->addAction(m_nextBadBoxAction); 0140 0141 } 0142 0143 void KileErrorHandler::setOutputActionsEnabled(bool b) 0144 { 0145 m_viewLogAction->setEnabled(b); 0146 m_previousErrorAction->setEnabled(b); 0147 m_nextErrorAction->setEnabled(b); 0148 m_previousWarningAction->setEnabled(b); 0149 m_nextWarningAction->setEnabled(b); 0150 m_previousBadBoxAction->setEnabled(b); 0151 m_nextBadBoxAction->setEnabled(b); 0152 } 0153 0154 QLabel* KileErrorHandler::compilationResultLabel() 0155 { 0156 return m_compilationResultLabel; 0157 } 0158 0159 QWidget* KileErrorHandler::outputWidget() 0160 { 0161 return m_outputTabWidget; 0162 } 0163 0164 bool KileErrorHandler::areMessagesShown() const 0165 { 0166 return m_mainLogWidget->isShowingOutput(); 0167 } 0168 0169 void KileErrorHandler::addEmptyLineToMessages() 0170 { 0171 m_mainLogWidget->addEmptyLine(); 0172 } 0173 0174 void KileErrorHandler::startToolLogOutput() 0175 { 0176 m_mainLogWidget->startToolLogOutput(); 0177 } 0178 0179 void KileErrorHandler::endToolLogOutput() 0180 { 0181 m_mainLogWidget->endToolLogOutput(); 0182 } 0183 0184 void KileErrorHandler::printMessage(const QString& message) 0185 { 0186 m_mainLogWidget->printMessage(message); 0187 } 0188 0189 void KileErrorHandler::printMessage(int type, const QString& message, const QString &tool, 0190 const OutputInfo& outputInfo, bool allowSelection, 0191 bool scroll) 0192 { 0193 m_mainLogWidget->printMessage(type, message, tool, outputInfo, allowSelection, scroll); 0194 } 0195 0196 void KileErrorHandler::printProblem(int type, const QString& problem, const OutputInfo& outputInfo) 0197 { 0198 m_mainLogWidget->printProblem(type, problem, outputInfo); 0199 } 0200 0201 void KileErrorHandler::clearMessages() 0202 { 0203 m_mainLogWidget->clear(); 0204 } 0205 0206 int KileErrorHandler::currentOutputTabIndex() 0207 { 0208 return m_outputTabWidget->currentIndex(); 0209 } 0210 0211 void KileErrorHandler::setCurrentOutputTab(int i) 0212 { 0213 if(i < 0 || i >= m_outputTabWidget->count()) { 0214 return; 0215 } 0216 m_outputTabWidget->setCurrentIndex(i); 0217 } 0218 0219 void KileErrorHandler::showMessagesOutput() 0220 { 0221 m_outputTabWidget->setCurrentWidget(m_mainLogWidget); 0222 } 0223 0224 void KileErrorHandler::showErrorsOutput() 0225 { 0226 m_outputTabWidget->setCurrentWidget(m_errorLogWidget); 0227 } 0228 0229 void KileErrorHandler::showWarningsOutput() 0230 { 0231 m_outputTabWidget->setCurrentWidget(m_warningLogWidget); 0232 } 0233 0234 void KileErrorHandler::showBadBoxesOutput() 0235 { 0236 m_outputTabWidget->setCurrentWidget(m_badBoxLogWidget); 0237 } 0238 0239 void KileErrorHandler::handleProjectOpened(KileProject *project) 0240 { 0241 connect(project, SIGNAL(aboutToBeDestroyed(KileProject*)), 0242 this, SLOT(updateCurrentLaTeXOutputHandler()), 0243 Qt::UniqueConnection); 0244 connect(project, SIGNAL(projectItemAdded(KileProject*,KileProjectItem*)), 0245 this, SLOT(updateCurrentLaTeXOutputHandler()), 0246 Qt::UniqueConnection); 0247 connect(project, SIGNAL(projectItemRemoved(KileProject*,KileProjectItem*)), 0248 this, SLOT(updateCurrentLaTeXOutputHandler()), 0249 Qt::UniqueConnection); 0250 } 0251 0252 void KileErrorHandler::handleLaTeXToolDone(KileTool::Base *tool, int i, bool childToolSpawned) 0253 { 0254 Q_UNUSED(i); 0255 0256 KileTool::LaTeX *latex = dynamic_cast<KileTool::LaTeX*>(tool); 0257 if(!latex) { 0258 return; 0259 } 0260 if(childToolSpawned) { 0261 return; 0262 } 0263 if(latex->latexOutputHandler() == m_currentLaTeXOutputHandler) { 0264 updateForCompilationResult(); 0265 } 0266 } 0267 0268 void KileErrorHandler::handleSpawnedChildTool(KileTool::Base *parent, KileTool::Base *child) 0269 { 0270 if(!dynamic_cast<KileTool::LaTeX*>(parent) || !dynamic_cast<KileTool::LaTeX*>(child)) { 0271 return; 0272 } 0273 0274 connect(child, SIGNAL(done(KileTool::Base*,int,bool)), 0275 this, SLOT(handleLaTeXToolDone(KileTool::Base*,int,bool))); 0276 } 0277 0278 void KileErrorHandler::updateCurrentLaTeXOutputHandler() 0279 { 0280 LaTeXOutputHandler *h = Q_NULLPTR; 0281 m_ki->getCompileName(false, &h); 0282 if(h == m_currentLaTeXOutputHandler) { 0283 return; 0284 } 0285 m_currentLaTeXOutputHandler = h; 0286 0287 if(!m_currentLaTeXOutputHandler) { 0288 setOutputActionsEnabled(false); 0289 clearErrorOutput(); 0290 } 0291 else { 0292 setOutputActionsEnabled(true); 0293 updateForCompilationResult(); 0294 } 0295 emit(currentLaTeXOutputHandlerChanged(m_currentLaTeXOutputHandler)); 0296 } 0297 0298 void KileErrorHandler::updateForCompilationResult() 0299 { 0300 if(!m_currentLaTeXOutputHandler) { 0301 return; 0302 } 0303 m_errorLogWidget->clear(); 0304 displayProblemsInLogWidget(m_errorLogWidget, m_currentLaTeXOutputHandler->outputList(), KileErrorHandler::OnlyErrors); 0305 m_warningLogWidget->clear(); 0306 displayProblemsInLogWidget(m_warningLogWidget, m_currentLaTeXOutputHandler->outputList(), KileErrorHandler::OnlyWarnings); 0307 m_badBoxLogWidget->clear(); 0308 displayProblemsInLogWidget(m_badBoxLogWidget, m_currentLaTeXOutputHandler->outputList(), KileErrorHandler::OnlyBadBoxes); 0309 0310 const int nErrors = m_currentLaTeXOutputHandler->numberOfErrors(); 0311 const int nWarnings = m_currentLaTeXOutputHandler->numberOfWarnings(); 0312 const int nBadBoxes = m_currentLaTeXOutputHandler->numberOfBadBoxes(); 0313 QString errorString, warningString, badBoxString; 0314 0315 if(nErrors >= 0) { 0316 errorString = i18n("Errors: %1", nErrors); 0317 } 0318 if(nWarnings >= 0) { 0319 warningString = i18n("Warnings: %1", nWarnings); 0320 } 0321 if(nBadBoxes >= 0) { 0322 badBoxString = i18n("BadBoxes: %1", nBadBoxes); 0323 } 0324 0325 m_compilationResultLabel->setText(i18nc("Result of the compilation w.r.t. number of errors/warnings/badboxes", 0326 "%1 %2 %3", errorString, warningString, badBoxString)); 0327 } 0328 0329 void KileErrorHandler::clearErrorOutput() 0330 { 0331 m_compilationResultLabel->clear(); 0332 m_errorLogWidget->clear(); 0333 m_warningLogWidget->clear(); 0334 m_badBoxLogWidget->clear(); 0335 } 0336 0337 void KileErrorHandler::setMostRecentLogInformation(const QString& logFile, const LatexOutputInfoArray& outputInfoList) 0338 { 0339 Q_UNUSED(logFile); 0340 0341 // add them to the log widget 0342 displayProblemsInMainLogWidget(outputInfoList); 0343 } 0344 0345 void KileErrorHandler::displayProblemsInMainLogWidget(const LatexOutputInfoArray& infoList) 0346 { 0347 displayProblemsInLogWidget(m_mainLogWidget, infoList); 0348 } 0349 0350 void KileErrorHandler::displayProblemsInLogWidget(KileWidget::LogWidget *logWidget, const LatexOutputInfoArray& infoList, ProblemType problemType) 0351 { 0352 QString message; 0353 int type = KileTool::Info; 0354 0355 //print detailed error info 0356 logWidget->setUpdatesEnabled(false); 0357 0358 for(QList<LatexOutputInfo>::const_iterator i = infoList.begin(); 0359 i != infoList.end(); ++i) { 0360 0361 const LatexOutputInfo& info = *i; 0362 message = info.source() + ':' + QString::number(info.sourceLine()) + ':' + info.message(); 0363 switch(info.type()) { 0364 case LatexOutputInfo::itmBadBox: 0365 if(problemType == AllProblems || problemType == OnlyBadBoxes) { 0366 type = KileTool::ProblemBadBox; 0367 } 0368 else { 0369 continue; 0370 } 0371 break; 0372 case LatexOutputInfo::itmError: 0373 if(problemType == AllProblems || problemType == OnlyErrors) { 0374 type = KileTool::ProblemError; 0375 } 0376 else { 0377 continue; 0378 } 0379 break; 0380 case LatexOutputInfo::itmWarning: 0381 if(problemType == AllProblems || problemType == OnlyWarnings) { 0382 type = KileTool::ProblemWarning; 0383 } 0384 else { 0385 continue; 0386 } 0387 break; 0388 default: 0389 type = KileTool::Info; 0390 break; 0391 } 0392 KileWidget::LogWidget::ProblemInformation problem; 0393 problem.type = type; 0394 problem.message = message; 0395 problem.outputInfo = info; 0396 logWidget->printMessage(type, message, QString(), info, false, false); 0397 } 0398 0399 logWidget->setUpdatesEnabled(true); 0400 logWidget->scrollToBottom(); 0401 } 0402 0403 void KileErrorHandler::printNoInformationAvailable() 0404 { 0405 m_mainLogWidget->printMessage(i18n("No information about warnings or errors is available.")); 0406 } 0407 0408 void KileErrorHandler::ViewLog() 0409 { 0410 if(!m_currentLaTeXOutputHandler) { 0411 printNoInformationAvailable(); 0412 return; 0413 } 0414 0415 const LatexOutputInfoArray& outputInfoList = m_currentLaTeXOutputHandler->outputList(); 0416 0417 KileWidget::LogWidget *logWidget = m_mainLogWidget; 0418 m_ki->focusLog(); 0419 0420 QFile logFile(m_currentLaTeXOutputHandler->logFile()); 0421 if(!m_currentLaTeXOutputHandler->logFile().isEmpty() && logFile.open(QIODevice::ReadOnly | QIODevice::Text)) { 0422 QHash<int, OutputInfo> hash; 0423 0424 for(QList<LatexOutputInfo>::const_iterator i = outputInfoList.begin(); 0425 i != outputInfoList.end(); ++i) { 0426 LatexOutputInfo info = *i; 0427 hash[info.outputLine()] = info; 0428 } 0429 0430 QTextStream textStream(&logFile); 0431 0432 for(int lineNumber = 0; !textStream.atEnd(); ++lineNumber) { 0433 int type = -1; 0434 const QString line = textStream.readLine(); 0435 if(hash.find(lineNumber) != hash.end()) { 0436 switch(hash[lineNumber].type()) { 0437 case LatexOutputInfo::itmError: 0438 type = KileTool::Error; 0439 break; 0440 case LatexOutputInfo::itmWarning: 0441 type = KileTool::Warning; 0442 break; 0443 case LatexOutputInfo::itmBadBox: 0444 type = KileTool::ProblemBadBox; 0445 break; 0446 } 0447 } 0448 // don't scroll to the item as this will lead to severely degraded performance 0449 logWidget->printMessage(type, line, QString(), hash[lineNumber], true, false); 0450 } 0451 0452 logWidget->scrollToBottom(); 0453 } 0454 else { 0455 logWidget->printProblem(KileTool::Error, i18n("Cannot open log file; did you run LaTeX?")); 0456 } 0457 } 0458 0459 void KileErrorHandler::jumpToFirstError() 0460 { 0461 if(!m_currentLaTeXOutputHandler) { 0462 printNoInformationAvailable(); 0463 return; 0464 } 0465 0466 const LatexOutputInfoArray& outputInfoList = m_currentLaTeXOutputHandler->outputList(); 0467 0468 int sz = outputInfoList.size(); 0469 for(int i = 0; i < sz; ++i) { 0470 if(outputInfoList[i].type() == LatexOutputInfo::itmError) { 0471 // this has to be before calling 'jumpToProblem' as this might change 'm_currentLaTeXOutputHandler' 0472 m_currentLaTeXOutputHandler->setCurrentError(i); 0473 jumpToProblem(outputInfoList[i]); 0474 break; 0475 } 0476 } 0477 } 0478 0479 void KileErrorHandler::jumpToProblem(const OutputInfo& info) 0480 { 0481 QString file = m_ki->getFullFromPrettyName(info, info.source()); 0482 0483 if(!file.isEmpty()) { 0484 m_ki->docManager()->fileOpen(QUrl::fromLocalFile(file)); 0485 int line = (info.sourceLine() > 0) ? (info.sourceLine() - 1) : 0; 0486 0487 KTextEditor::Document *doc = m_ki->docManager()->docFor(QUrl::fromLocalFile(file)); 0488 if(doc) { 0489 KTextEditor::View* view = doc->views().first(); 0490 if(view) { 0491 view->setCursorPosition(KTextEditor::Cursor(line, 0)); 0492 } 0493 } 0494 } 0495 } 0496 0497 void KileErrorHandler::jumpToProblem(int type, bool forward) 0498 { 0499 if(!m_currentLaTeXOutputHandler) { 0500 printNoInformationAvailable(); 0501 return; 0502 } 0503 0504 const LatexOutputInfoArray& outputInfoList = m_currentLaTeXOutputHandler->outputList(); 0505 0506 if (!outputInfoList.isEmpty()) { 0507 int sz = outputInfoList.size(); 0508 int pl = forward ? 1 : -1; 0509 bool found = false; 0510 0511 //look for next problem of requested type 0512 for(int i = 0; i < sz; ++i) { 0513 //always look at the whole outputInfo array, but start 0514 //at the problem adjacent to the current error 0515 //if we go beyond the bounds of the array we use 0516 //a simple "modulo" calculation to get within bounds again 0517 int index = (m_currentLaTeXOutputHandler->currentError() + (i + 1) *pl) % sz; 0518 while(index < 0) { 0519 index += sz; 0520 } 0521 0522 if(outputInfoList[index].type() == type) { 0523 m_currentLaTeXOutputHandler->setCurrentError(index); 0524 found = true; 0525 break; 0526 } 0527 } 0528 0529 if(!found) { 0530 return; 0531 } 0532 0533 //If the log file is being viewed, use this to jump to the errors, 0534 //otherwise, use the error summary display 0535 m_mainLogWidget->highlight(outputInfoList[m_currentLaTeXOutputHandler->currentError()]); 0536 0537 jumpToProblem(outputInfoList[m_currentLaTeXOutputHandler->currentError()]); 0538 } 0539 0540 if(outputInfoList.isEmpty()) { 0541 m_mainLogWidget->printMessage(i18n("No LaTeX warnings/errors detected.")); 0542 } 0543 } 0544 0545 void KileErrorHandler::NextError() 0546 { 0547 jumpToProblem(LatexOutputInfo::itmError, true); 0548 } 0549 0550 void KileErrorHandler::PreviousError() 0551 { 0552 jumpToProblem(LatexOutputInfo::itmError, false); 0553 } 0554 0555 void KileErrorHandler::NextWarning() 0556 { 0557 jumpToProblem(LatexOutputInfo::itmWarning, true); 0558 } 0559 0560 void KileErrorHandler::PreviousWarning() 0561 { 0562 jumpToProblem(LatexOutputInfo::itmWarning, false); 0563 } 0564 0565 void KileErrorHandler::NextBadBox() 0566 { 0567 jumpToProblem(LatexOutputInfo::itmBadBox, true); 0568 } 0569 0570 void KileErrorHandler::PreviousBadBox() 0571 { 0572 jumpToProblem(LatexOutputInfo::itmBadBox, false); 0573 } 0574