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