File indexing completed on 2024-05-05 05:48:58

0001 /*
0002     SPDX-FileCopyrightText: 2007 Nicolas Ternisien <nicolas.ternisien@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "logViewSearchWidget.h"
0008 
0009 #include <QCheckBox>
0010 #include <QColor>
0011 #include <QPalette>
0012 #include <QPushButton>
0013 #include <QString>
0014 #include <QTimer>
0015 
0016 #include <KLocalizedString>
0017 #include <QIcon>
0018 
0019 #include "ksystemlog_debug.h"
0020 #include "logViewWidget.h"
0021 #include "logViewWidgetItem.h"
0022 
0023 LogViewSearchWidget::LogViewSearchWidget(QWidget *parent)
0024     : QWidget(parent)
0025 {
0026     setupUi(this);
0027 
0028     // Get the searchLine base color to be able to restore it later
0029     mSearchLineBaseColor = searchLine->palette().color(QPalette::Base);
0030     mSearchLineTextColor = searchLine->palette().color(QPalette::Text);
0031 
0032     // Default configuration of the hiding message timer
0033     mMessageHidingTimer = new QTimer(this);
0034     mMessageHidingTimer->setSingleShot(true);
0035     mMessageHidingTimer->setInterval(2000);
0036     connect(mMessageHidingTimer, &QTimer::timeout, this, &LogViewSearchWidget::hideMessage);
0037 
0038     // The message widget is hidden by default
0039     hideMessage();
0040 
0041     closeButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-close")));
0042     connect(closeButton, &QAbstractButton::clicked, this, &QWidget::hide);
0043 
0044     next->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down")));
0045     connect(next, &QAbstractButton::clicked, this, &LogViewSearchWidget::findNext);
0046 
0047     previous->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up")));
0048     connect(previous, &QAbstractButton::clicked, this, &LogViewSearchWidget::findPrevious);
0049 
0050     searchLabel->setBuddy(searchLine);
0051 
0052     connect(searchLine, SIGNAL(textEdited(QString)), this, SLOT(findFirst(QString)));
0053     connect(searchLine, &QLineEdit::textEdited, this, &LogViewSearchWidget::highlightAll);
0054 
0055     connect(searchLine, &QLineEdit::returnPressed, this, &LogViewSearchWidget::findNext);
0056 
0057     connect(caseSensitive, SIGNAL(clicked()), this, SLOT(findFirst()));
0058 
0059     connect(caseSensitive, &QAbstractButton::clicked, this, &LogViewSearchWidget::highlightAll);
0060     connect(highlightAllButton, &QAbstractButton::clicked, this, &LogViewSearchWidget::highlightAll);
0061 
0062     findFirst(searchLine->text());
0063 }
0064 
0065 LogViewSearchWidget::~LogViewSearchWidget()
0066 {
0067 }
0068 
0069 void LogViewSearchWidget::displaySearch()
0070 {
0071     searchLine->setFocus();
0072     searchLine->setSelection(0, searchLine->text().length());
0073     show();
0074 }
0075 
0076 void LogViewSearchWidget::setTreeWidget(LogViewWidget *logViewWidget)
0077 {
0078     mLogViewWidget = logViewWidget;
0079 }
0080 
0081 void LogViewSearchWidget::findFirst(const QString &text)
0082 {
0083     const bool textIsNotEmpty = !text.isEmpty();
0084     next->setEnabled(textIsNotEmpty);
0085     previous->setEnabled(textIsNotEmpty);
0086     if (textIsNotEmpty) {
0087         findFirst();
0088     }
0089 }
0090 
0091 void LogViewSearchWidget::findFirst()
0092 {
0093     internalFind(nullptr, LogViewSearchWidget::Next);
0094 }
0095 
0096 void LogViewSearchWidget::findNext()
0097 {
0098     qCDebug(KSYSTEMLOG) << "Finding next";
0099 
0100     LogViewWidgetItem *lastSelectedItem = mLogViewWidget->lastSelectedItem();
0101     internalFind(lastSelectedItem, LogViewSearchWidget::Next);
0102 }
0103 
0104 void LogViewSearchWidget::findPrevious()
0105 {
0106     qCDebug(KSYSTEMLOG) << "Finding previous";
0107 
0108     LogViewWidgetItem *firstSelectedItem = mLogViewWidget->firstSelectedItem();
0109     internalFind(firstSelectedItem, LogViewSearchWidget::Previous);
0110 }
0111 
0112 void LogViewSearchWidget::internalFind(LogViewWidgetItem *fromItem, Direction direction)
0113 {
0114     if (searchLine->text().isEmpty()) {
0115         return;
0116     }
0117 
0118     QTreeWidgetItemIterator it(mLogViewWidget, QTreeWidgetItemIterator::NotHidden);
0119     initIterator(it, direction);
0120 
0121     // Go to the selected position + 1 (if we already found an item)
0122     if (fromItem) {
0123         while (*it) {
0124             auto item = static_cast<LogViewWidgetItem *>(*it);
0125 
0126             if (item == fromItem) {
0127                 iteratorJump(it, direction);
0128                 break;
0129             }
0130 
0131             iteratorJump(it, direction);
0132         }
0133     }
0134 
0135     // Iterates to fromItem +1 to the last item of the list
0136     while (*it) {
0137         auto item = static_cast<LogViewWidgetItem *>(*it);
0138 
0139         bool const found = findItem(item);
0140         if (found) {
0141             return;
0142         }
0143 
0144         iteratorJump(it, direction);
0145     }
0146 
0147     // If we do not begin the search from the beginning, we do it now
0148     if (fromItem) {
0149         it = QTreeWidgetItemIterator(mLogViewWidget, QTreeWidgetItemIterator::NotHidden);
0150         initIterator(it, direction);
0151 
0152         LogViewWidgetItem *item = nullptr;
0153         while (*it && item != fromItem) {
0154             item = static_cast<LogViewWidgetItem *>(*it);
0155 
0156             bool const found = findItem(item);
0157             if (found) {
0158                 showMessage(i18n("Reached end of list."), QStringLiteral("dialog-information"));
0159                 return;
0160             }
0161 
0162             iteratorJump(it, direction);
0163         }
0164     }
0165 
0166     setSearchLineNotFound(true);
0167 }
0168 
0169 inline void LogViewSearchWidget::initIterator(QTreeWidgetItemIterator &it, Direction direction)
0170 {
0171     // Previous direction : Go to the last item
0172     if (direction == LogViewSearchWidget::Previous) {
0173         QTreeWidgetItemIterator testedIterator(it);
0174         while (true) {
0175             ++testedIterator;
0176             if (!(*testedIterator)) {
0177                 break;
0178             }
0179 
0180             ++it;
0181         }
0182     }
0183 }
0184 
0185 inline void LogViewSearchWidget::iteratorJump(QTreeWidgetItemIterator &it, Direction direction)
0186 {
0187     if (direction == LogViewSearchWidget::Next) {
0188         ++it;
0189     } else {
0190         --it;
0191     }
0192 }
0193 
0194 bool LogViewSearchWidget::compareItem(LogViewWidgetItem *item)
0195 {
0196     Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive;
0197     if (caseSensitive->isChecked()) {
0198         caseSensitivity = Qt::CaseSensitive;
0199     }
0200 
0201     if (searchLine->text().isEmpty()) {
0202         return false;
0203     }
0204 
0205     if (item->logLine()->exportToText().contains(searchLine->text(), caseSensitivity)) {
0206         return true;
0207     }
0208 
0209     return false;
0210 }
0211 
0212 bool LogViewSearchWidget::findItem(LogViewWidgetItem *item)
0213 {
0214     if (compareItem(item)) {
0215         unselectAll();
0216 
0217         setSearchLineNotFound(false);
0218         item->setSelected(true);
0219         mLogViewWidget->setCurrentItem(item);
0220         mLogViewWidget->scrollToItem(item);
0221         return true;
0222     }
0223 
0224     return false;
0225 }
0226 
0227 void LogViewSearchWidget::setSearchLineNotFound(bool notFound)
0228 {
0229     QPalette palette = searchLine->palette();
0230     if (notFound) {
0231         palette.setColor(QPalette::Base, QColor(255, 102, 102)); // or Qt::red or QColor(235, 0, 0)
0232         palette.setColor(QPalette::Text, QColor(255, 255, 255));
0233     } else {
0234         palette.setColor(QPalette::Base, mSearchLineBaseColor);
0235         palette.setColor(QPalette::Text, mSearchLineTextColor);
0236     }
0237 
0238     searchLine->setPalette(palette);
0239 
0240     if (notFound) {
0241         showMessage(i18n("Phrase not found."), QStringLiteral("dialog-error"));
0242     } else {
0243         hideMessage();
0244     }
0245 }
0246 
0247 void LogViewSearchWidget::unselectAll()
0248 {
0249     const QList<QTreeWidgetItem *> selectedItems = mLogViewWidget->selectedItems();
0250     for (QTreeWidgetItem *item : selectedItems) {
0251         item->setSelected(false);
0252     }
0253 }
0254 
0255 void LogViewSearchWidget::showMessage(const QString &text, const QString &iconText)
0256 {
0257     message->setText(text);
0258     message->show();
0259 
0260     messageIcon->setPixmap(QIcon::fromTheme(iconText).pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize)));
0261     messageIcon->show();
0262 
0263     mMessageHidingTimer->start();
0264 }
0265 
0266 void LogViewSearchWidget::hideMessage()
0267 {
0268     message->hide();
0269     messageIcon->hide();
0270 
0271     mMessageHidingTimer->stop();
0272 }
0273 
0274 void LogViewSearchWidget::highlightAll()
0275 {
0276     if (highlightAllButton->isChecked()) {
0277         unlightAll();
0278 
0279         qCDebug(KSYSTEMLOG) << "Highlighting all";
0280         QTreeWidgetItemIterator it(mLogViewWidget, QTreeWidgetItemIterator::All);
0281         while (*it) {
0282             auto item = static_cast<LogViewWidgetItem *>(*it);
0283 
0284             if (compareItem(item)) {
0285                 item->setBackground(item->columnCount() - 1, QColor(255, 255, 16 * 8 + 11));
0286             }
0287 
0288             ++it;
0289         }
0290     } else {
0291         unlightAll();
0292     }
0293 }
0294 
0295 void LogViewSearchWidget::unlightAll()
0296 {
0297     qCDebug(KSYSTEMLOG) << "Unlighting all";
0298 
0299     QTreeWidgetItemIterator it(mLogViewWidget, QTreeWidgetItemIterator::All);
0300     while (*it) {
0301         auto item = static_cast<LogViewWidgetItem *>(*it);
0302 
0303         // We retrieve the default column background using the first column data, where the background never
0304         // changes
0305         item->setBackground(item->columnCount() - 1, qvariant_cast<QBrush>(item->data(0, Qt::BackgroundRole)));
0306 
0307         ++it;
0308     }
0309 }
0310 
0311 #include "moc_logViewSearchWidget.cpp"