File indexing completed on 2024-04-28 16:26:32
0001 /************************************************************************************** 0002 Copyright (C) 2003 by Jeroen Wijnhout (Jeroen.Wijnhout@kdemail.net) 0003 2008-2012 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 "widgets/logwidget.h" 0016 0017 #include <QAbstractTextDocumentLayout> 0018 #include <QClipboard> 0019 #include <QHash> 0020 #include <QMenu> 0021 #include <QPainter> 0022 #include <QTextDocument> 0023 #include <QTextStream> 0024 0025 #include <QAction> 0026 #include <KLocalizedString> 0027 #include <KColorScheme> 0028 #include <KStandardAction> 0029 #include <QUrl> 0030 0031 #include "kileconfig.h" 0032 #include "kiledebug.h" 0033 #include "kileinfo.h" 0034 #include "kiletool_enums.h" 0035 0036 namespace KileWidget 0037 { 0038 LogWidgetItemDelegate::LogWidgetItemDelegate(QObject* parent) 0039 : QItemDelegate(parent) 0040 { 0041 } 0042 0043 QSize LogWidgetItemDelegate::sizeHint(const QStyleOptionViewItem& /* option */, 0044 const QModelIndex& index) const 0045 { 0046 QTextDocument *textDocument = constructTextDocument(index); 0047 QSize size = textDocument->documentLayout()->documentSize().toSize(); 0048 delete textDocument; 0049 return size; 0050 } 0051 0052 void LogWidgetItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, 0053 const QModelIndex& index) const 0054 { 0055 painter->save(); 0056 QAbstractTextDocumentLayout::PaintContext context; 0057 QVector<QAbstractTextDocumentLayout::Selection> selectionVector; 0058 0059 painter->translate(option.rect.x(), option.rect.y()); 0060 QTextDocument *textDocument = constructTextDocument(index); 0061 0062 if(option.state & QStyle::State_MouseOver && index.data(Qt::UserRole).isValid()) { 0063 QTextCursor cursor(textDocument); 0064 cursor.select(QTextCursor::Document); 0065 QTextCharFormat format; 0066 format.setFontUnderline(true); 0067 cursor.mergeCharFormat(format); 0068 } 0069 0070 if(option.state & QStyle::State_Selected) { 0071 QTextCursor cursor(textDocument); 0072 cursor.setPosition(0); 0073 cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); 0074 QTextCharFormat selectionTextCharFormat; 0075 selectionTextCharFormat.setFontWeight(QFont::Bold); 0076 selectionTextCharFormat.setBackground(option.palette.highlight()); 0077 selectionTextCharFormat.setForeground(option.palette.highlightedText()); 0078 QAbstractTextDocumentLayout::Selection selection; 0079 selection.cursor = cursor; 0080 selection.format = selectionTextCharFormat; 0081 selectionVector.push_back(selection); 0082 context.selections = selectionVector; 0083 } 0084 0085 textDocument->documentLayout()->draw(painter, context); 0086 delete textDocument; 0087 0088 painter->restore(); 0089 } 0090 0091 QTextDocument* LogWidgetItemDelegate::constructTextDocument(const QModelIndex& index) const 0092 { 0093 QTextDocument *textDocument = new QTextDocument(); 0094 textDocument->setHtml(index.data().toString()); 0095 return textDocument; 0096 } 0097 0098 LogWidget::LogWidget(PopupType popupType, QWidget *parent, const char *name) : 0099 QListWidget(parent), m_popupType(popupType) 0100 { 0101 setObjectName(name); 0102 connect(this, SIGNAL(itemClicked(QListWidgetItem*)), 0103 this, SLOT(slotItemClicked(QListWidgetItem*))); 0104 QPalette customPalette = palette(); 0105 customPalette.setColor(QPalette::Window, QColor(Qt::white)); 0106 setPalette(customPalette); 0107 m_itemDelegate = new LogWidgetItemDelegate(this); 0108 setSelectionMode(QAbstractItemView::MultiSelection); 0109 QAbstractItemDelegate *delegate = itemDelegate(); 0110 if(delegate) { 0111 delete delegate; 0112 } 0113 setItemDelegate(m_itemDelegate); 0114 setMouseTracking(true); 0115 } 0116 0117 LogWidget::~LogWidget() 0118 { 0119 } 0120 0121 bool LogWidget::isShowingOutput() const 0122 { 0123 return (count() > 0); 0124 } 0125 0126 void LogWidget::highlight(const OutputInfo& info, bool startFromBottom) 0127 { 0128 for(int i = 0; i < count(); ++i) { 0129 QListWidgetItem *listItem = item(startFromBottom ? count() - 1 - i : i); 0130 QVariant variant = listItem->data(Qt::UserRole); 0131 if(!variant.isValid()) { 0132 continue; 0133 } 0134 OutputInfo info2 = variant.value<OutputInfo>(); 0135 if(info == info2) { 0136 deselectAllItems(); 0137 scrollToItem(listItem); 0138 listItem->setSelected(true); 0139 break; 0140 } 0141 } 0142 } 0143 0144 void LogWidget::slotItemClicked(QListWidgetItem *item) 0145 { 0146 QVariant variant = item->data(Qt::UserRole); 0147 if(!variant.isValid()) { 0148 return; 0149 } 0150 0151 OutputInfo info = variant.value<OutputInfo>(); 0152 0153 emit(outputInfoSelected(info)); 0154 } 0155 0156 void LogWidget::enterEvent(QEvent *) 0157 { 0158 adaptMouseCursor(mapFromGlobal(QCursor::pos())); 0159 } 0160 0161 void LogWidget::leaveEvent(QEvent *) 0162 { 0163 unsetCursor(); 0164 } 0165 0166 void LogWidget::mouseMoveEvent(QMouseEvent* event) 0167 { 0168 QPoint p = event->pos(); 0169 adaptMouseCursor(p); 0170 } 0171 0172 void LogWidget::adaptMouseCursor(const QPoint& p) 0173 { 0174 QListWidgetItem *item = itemAt(p); 0175 if(!item) { 0176 unsetCursor(); 0177 return; 0178 } 0179 0180 QVariant variant = item->data(Qt::UserRole); 0181 if(variant.isValid()) { 0182 setCursor(Qt::PointingHandCursor); 0183 } 0184 else { 0185 unsetCursor(); 0186 } 0187 } 0188 0189 void LogWidget::keyPressEvent(QKeyEvent *event) 0190 { 0191 QAbstractScrollArea::keyPressEvent(event); 0192 } 0193 0194 void LogWidget::deselectAllItems() 0195 { 0196 QList<QListWidgetItem*> items = selectedItems(); 0197 for(QList<QListWidgetItem*>::iterator i = items.begin(); 0198 i != items.end(); ++i) { 0199 QListWidgetItem *item = *i; 0200 item->setSelected(false); 0201 } 0202 } 0203 0204 void LogWidget::printMessage(const QString& message) 0205 { 0206 KILE_DEBUG_MAIN << "\t" << message; 0207 printMessage(-1, message, QString()); 0208 } 0209 0210 void LogWidget::printMessage(int type, const QString& message, const QString &tool, 0211 const OutputInfo& outputInfo, bool allowSelection, 0212 bool scroll) 0213 { 0214 QStringList messageList = message.split('\n'); 0215 for(QStringList::iterator it = messageList.begin(); it != messageList.end(); ++it) { 0216 printMessageLine(type, *it, tool, outputInfo, allowSelection, scroll); 0217 } 0218 } 0219 0220 void LogWidget::printMessageLine(int type, const QString& message, const QString &tool, 0221 const OutputInfo& outputInfo, bool allowSelection, 0222 bool scroll) 0223 { 0224 if(type == KileTool::Error) { 0225 KILE_DEBUG_MAIN << "showing error message emitted"; 0226 emit showingErrorMessage(this); 0227 } 0228 0229 QString myMsg = message.toHtmlEscaped(); 0230 QString fontColor; 0231 0232 switch(type) { 0233 case KileTool::Warning : 0234 fontColor = "<font color='" + KStatefulBrush(KColorScheme::View, KColorScheme::NeutralText).brush(this->palette()).color().name() + "'>"; 0235 break; 0236 case KileTool::ProblemWarning : 0237 if(KileConfig::hideProblemWarning()) { 0238 return; 0239 } 0240 fontColor = "<font color='" + KStatefulBrush(KColorScheme::View, KColorScheme::NeutralText).brush(this->palette()).color().name() + "'>"; 0241 break; 0242 case KileTool::Error: // fall through 0243 case KileTool::ProblemError: 0244 fontColor = "<font color='" + KStatefulBrush(KColorScheme::View, KColorScheme::NegativeText).brush(this->palette()).color().name() + "'>"; 0245 break; 0246 case KileTool::ProblemBadBox: 0247 if (KileConfig::hideProblemBadBox()) { 0248 return; 0249 } 0250 { 0251 // 'KColorScheme::scheme' doesn't take the background colour into account, so we have to do it manually 0252 const QColor color = (KStatefulBrush(KColorScheme::View, KColorScheme::NormalBackground).brush(this->palette()).color().lightnessF() > 0.5) 0253 ? KColorScheme::shade(KStatefulBrush(KColorScheme::View, KColorScheme::NeutralText).brush(this->palette()).color(), KColorScheme::DarkShade) 0254 : KColorScheme::shade(KStatefulBrush(KColorScheme::View, KColorScheme::NeutralText).brush(this->palette()).color(), KColorScheme::LightShade); 0255 fontColor = "<font color='" + color.name() + "'>"; 0256 } 0257 break; 0258 default: 0259 fontColor = "<font color='" + KStatefulBrush(KColorScheme::View, KColorScheme::NormalText).brush(this->palette()).color().name() + "'>"; 0260 break; 0261 } 0262 0263 QListWidgetItem *item = new QListWidgetItem(this); 0264 0265 if(tool.isEmpty()) { 0266 item->setText(fontColor + myMsg + "</font>"); 0267 } 0268 else { 0269 item->setText(fontColor + "<b>[" + tool + "]</b> " + myMsg + "</font>"); 0270 } 0271 0272 0273 if(outputInfo.isValid()) { 0274 item->setData(Qt::UserRole, QVariant::fromValue(outputInfo)); 0275 } 0276 if(!allowSelection) { 0277 // Don't allow the user to manually select this item 0278 item->setFlags(item->flags() & ~Qt::ItemIsSelectable); 0279 } 0280 0281 if((type == KileTool::Error || type == KileTool::ProblemError) 0282 && !m_firstErrorMessgeInToolLog.isValid()) { 0283 m_firstErrorMessgeInToolLog = outputInfo; 0284 } 0285 0286 if(scroll) { 0287 scrollToItem(item); 0288 } 0289 } 0290 0291 void LogWidget::printProblem(int type, const QString& problem, const OutputInfo& outputInfo) 0292 { 0293 KILE_DEBUG_MAIN << "\t" << problem; 0294 printMessage(type, problem, QString(), outputInfo); 0295 } 0296 0297 void LogWidget::printProblems(const QList<KileWidget::LogWidget::ProblemInformation>& list) 0298 { 0299 setUpdatesEnabled(false); 0300 for(QList<ProblemInformation>::const_iterator i = list.begin(); i != list.end(); ++i) { 0301 printMessage((*i).type, (*i).message, QString(), (*i).outputInfo, false, false); 0302 } 0303 setUpdatesEnabled(true); 0304 scrollToBottom(); 0305 } 0306 0307 void LogWidget::addEmptyLine() 0308 { 0309 printMessage(-1, QString(), QString()); 0310 } 0311 0312 void LogWidget::copy() 0313 { 0314 QList<QListWidgetItem*> selectedList = selectedItems(); 0315 QString toCopy; 0316 int maxIndex = 0; 0317 QHash<int, QListWidgetItem*> itemHash; 0318 for(QList<QListWidgetItem*>::iterator i = selectedList.begin(); 0319 i != selectedList.end(); ++i) { 0320 QListWidgetItem* item = *i; 0321 int row = indexFromItem(item).row(); 0322 itemHash[row] = item; 0323 maxIndex = qMax(maxIndex, row); 0324 } 0325 for(int i = 0; i <= maxIndex; ++i) { 0326 QHash<int, QListWidgetItem*>::iterator it = itemHash.find(i); 0327 if(it != itemHash.end()) { 0328 toCopy += (*it)->data(Qt::UserRole).value<OutputInfo>().message() + '\n'; 0329 } 0330 } 0331 if(!toCopy.isEmpty()) { 0332 QApplication::clipboard()->setText(toCopy); 0333 } 0334 } 0335 0336 void LogWidget::startToolLogOutput() 0337 { 0338 m_firstErrorMessgeInToolLog = OutputInfo(); 0339 } 0340 0341 void LogWidget::endToolLogOutput() 0342 { 0343 if(m_firstErrorMessgeInToolLog.isValid()) { 0344 highlight(m_firstErrorMessgeInToolLog, true); 0345 } 0346 } 0347 0348 void LogWidget::contextMenuEvent(QContextMenuEvent *event) 0349 { 0350 QMenu popup; 0351 0352 QAction *action = KStandardAction::copy(this, SLOT(copy()), this); 0353 action->setShortcuts(QList<QKeySequence>()); 0354 if(selectedItems().size() == 0) { 0355 action->setEnabled(false); 0356 } 0357 popup.addAction(action); 0358 0359 action = KStandardAction::selectAll(this, SLOT(selectAll()), this); 0360 action->setShortcuts(QList<QKeySequence>()); 0361 if(!containsSelectableItems()) { 0362 action->setEnabled(false); 0363 } 0364 popup.addAction(action); 0365 0366 0367 if(!(m_popupType & NoHideActions)) { 0368 popup.addSeparator(); 0369 0370 action = new QAction(i18n("Hide &Bad Boxes"), &popup); 0371 action->setCheckable(true); 0372 action->setChecked(KileConfig::hideProblemBadBox()); 0373 connect(action, SIGNAL(triggered()), this, SLOT(toggleBadBoxHiding())); 0374 popup.addAction(action); 0375 0376 action = new QAction(i18n("Hide (La)TeX &Warnings"), &popup); 0377 action->setCheckable(true); 0378 action->setChecked(KileConfig::hideProblemWarning()); 0379 connect(action, SIGNAL(triggered()), this, SLOT(toggleWarningsHiding())); 0380 popup.addAction(action); 0381 } 0382 0383 popup.exec(event->globalPos()); 0384 } 0385 0386 void LogWidget::toggleBadBoxHiding() 0387 { 0388 KileConfig::setHideProblemBadBox(!KileConfig::hideProblemBadBox()); 0389 } 0390 0391 void LogWidget::toggleWarningsHiding() 0392 { 0393 KileConfig::setHideProblemWarning(!KileConfig::hideProblemWarning()); 0394 } 0395 0396 bool LogWidget::containsSelectableItems() const 0397 { 0398 for(int i = 0; i < count(); ++i) { 0399 if(item(i)->flags() & Qt::ItemIsSelectable) { 0400 return true; 0401 } 0402 } 0403 0404 return false; 0405 } 0406 0407 } 0408