File indexing completed on 2024-05-12 04:57:51

0001 /* ============================================================
0002 * Falkon - Qt web browser
0003 * Copyright (C) 2010-2017 David Rosca <nowrep@gmail.com>
0004 *
0005 * This program is free software: you can redistribute it and/or modify
0006 * it under the terms of the GNU General Public License as published by
0007 * the Free Software Foundation, either version 3 of the License, or
0008 * (at your option) any later version.
0009 *
0010 * This program is distributed in the hope that it will be useful,
0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013 * GNU General Public License for more details.
0014 *
0015 * You should have received a copy of the GNU General Public License
0016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0017 * ============================================================ */
0018 #include "adblocktreewidget.h"
0019 #include "adblocksubscription.h"
0020 
0021 #include <QMenu>
0022 #include <QKeyEvent>
0023 #include <QClipboard>
0024 #include <QApplication>
0025 #include <QInputDialog>
0026 
0027 AdBlockTreeWidget::AdBlockTreeWidget(AdBlockSubscription* subscription, QWidget* parent)
0028     : TreeWidget(parent)
0029     , m_subscription(subscription)
0030     , m_topItem(nullptr)
0031     , m_itemChangingBlock(false)
0032 {
0033     setContextMenuPolicy(Qt::CustomContextMenu);
0034     setDefaultItemShowMode(TreeWidget::ItemsExpanded);
0035     setHeaderHidden(true);
0036     setAlternatingRowColors(true);
0037     setLayoutDirection(Qt::LeftToRight);
0038 
0039     connect(this, &QWidget::customContextMenuRequested, this, &AdBlockTreeWidget::contextMenuRequested);
0040     connect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(itemChanged(QTreeWidgetItem*)));
0041     connect(m_subscription, &AdBlockSubscription::subscriptionUpdated, this, &AdBlockTreeWidget::subscriptionUpdated);
0042     connect(m_subscription, &AdBlockSubscription::subscriptionError, this, &AdBlockTreeWidget::subscriptionError);
0043 }
0044 
0045 AdBlockSubscription* AdBlockTreeWidget::subscription() const
0046 {
0047     return m_subscription;
0048 }
0049 
0050 void AdBlockTreeWidget::showRule(const AdBlockRule* rule)
0051 {
0052     if (!m_topItem && rule) {
0053         m_ruleToBeSelected = rule->filter();
0054     }
0055     else if (!m_ruleToBeSelected.isEmpty()) {
0056         QList<QTreeWidgetItem*> items = findItems(m_ruleToBeSelected, Qt::MatchRecursive);
0057         if (!items.isEmpty()) {
0058             QTreeWidgetItem* item = items.at(0);
0059 
0060             setCurrentItem(item);
0061             scrollToItem(item, QAbstractItemView::PositionAtCenter);
0062         }
0063 
0064         m_ruleToBeSelected.clear();
0065     }
0066 }
0067 
0068 void AdBlockTreeWidget::contextMenuRequested(const QPoint &pos)
0069 {
0070     if (!m_subscription->canEditRules()) {
0071         return;
0072     }
0073 
0074     QTreeWidgetItem* item = itemAt(pos);
0075     if (!item) {
0076         return;
0077     }
0078 
0079     QMenu menu;
0080     menu.addAction(tr("Add Rule"), this, &AdBlockTreeWidget::addRule);
0081     menu.addSeparator();
0082     QAction* deleteAction = menu.addAction(tr("Remove Rule"), this, &AdBlockTreeWidget::removeRule);
0083 
0084     if (!item->parent()) {
0085         deleteAction->setDisabled(true);
0086     }
0087 
0088     menu.exec(viewport()->mapToGlobal(pos));
0089 }
0090 
0091 void AdBlockTreeWidget::itemChanged(QTreeWidgetItem* item)
0092 {
0093     if (!item || m_itemChangingBlock) {
0094         return;
0095     }
0096 
0097     m_itemChangingBlock = true;
0098 
0099     int offset = item->data(0, Qt::UserRole + 10).toInt();
0100     const AdBlockRule* oldRule = m_subscription->rule(offset);
0101 
0102     if (item->checkState(0) == Qt::Unchecked && oldRule->isEnabled()) {
0103         // Disable rule
0104         const AdBlockRule* rule = m_subscription->disableRule(offset);
0105 
0106         adjustItemFeatures(item, rule);
0107     }
0108     else if (item->checkState(0) == Qt::Checked && !oldRule->isEnabled()) {
0109         // Enable rule
0110         const AdBlockRule* rule = m_subscription->enableRule(offset);
0111 
0112         adjustItemFeatures(item, rule);
0113     }
0114     else if (m_subscription->canEditRules()) {
0115         // Custom rule has been changed
0116         auto* newRule = new AdBlockRule(item->text(0), m_subscription);
0117         const AdBlockRule* rule = m_subscription->replaceRule(newRule, offset);
0118 
0119         adjustItemFeatures(item, rule);
0120     }
0121 
0122     m_itemChangingBlock = false;
0123 }
0124 
0125 void AdBlockTreeWidget::copyFilter()
0126 {
0127     QTreeWidgetItem* item = currentItem();
0128     if (!item) {
0129         return;
0130     }
0131 
0132     QApplication::clipboard()->setText(item->text(0));
0133 }
0134 
0135 void AdBlockTreeWidget::addRule()
0136 {
0137     if (!m_subscription->canEditRules()) {
0138         return;
0139     }
0140 
0141     QString newRule = QInputDialog::getText(this, tr("Add Custom Rule"), tr("Please write your rule here:"));
0142     if (newRule.isEmpty()) {
0143         return;
0144     }
0145 
0146     auto* rule = new AdBlockRule(newRule, m_subscription);
0147     int offset = m_subscription->addRule(rule);
0148 
0149     auto* item = new QTreeWidgetItem();
0150     item->setText(0, newRule);
0151     item->setData(0, Qt::UserRole + 10, offset);
0152     item->setFlags(item->flags() | Qt::ItemIsEditable);
0153 
0154     m_itemChangingBlock = true;
0155     m_topItem->addChild(item);
0156     m_itemChangingBlock = false;
0157 
0158     adjustItemFeatures(item, rule);
0159 }
0160 
0161 void AdBlockTreeWidget::removeRule()
0162 {
0163     QTreeWidgetItem* item = currentItem();
0164     if (!item || !m_subscription->canEditRules() || item == m_topItem) {
0165         return;
0166     }
0167 
0168     int offset = item->data(0, Qt::UserRole + 10).toInt();
0169 
0170     m_subscription->removeRule(offset);
0171     deleteItem(item);
0172 }
0173 
0174 void AdBlockTreeWidget::subscriptionUpdated()
0175 {
0176     refresh();
0177 
0178     m_itemChangingBlock = true;
0179     m_topItem->setText(0, tr("%1 (recently updated)").arg(m_subscription->title()));
0180     m_itemChangingBlock = false;
0181 }
0182 
0183 void AdBlockTreeWidget::subscriptionError(const QString &message)
0184 {
0185     refresh();
0186 
0187     m_itemChangingBlock = true;
0188     m_topItem->setText(0, tr("%1 (Error: %2)").arg(m_subscription->title(), message));
0189     m_itemChangingBlock = false;
0190 }
0191 
0192 void AdBlockTreeWidget::adjustItemFeatures(QTreeWidgetItem* item, const AdBlockRule* rule)
0193 {
0194     if (!rule->isEnabled()) {
0195         item->setForeground(0, QColor(Qt::gray));
0196 
0197         if (!rule->isComment()) {
0198             QFont f = font();
0199             f.setItalic(true);
0200             item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
0201             item->setCheckState(0, Qt::Unchecked);
0202             item->setFont(0, f);
0203         }
0204 
0205         return;
0206     }
0207 
0208     item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
0209     item->setCheckState(0, Qt::Checked);
0210     item->setForeground(0, palette().windowText());
0211     item->setFont(0, font());
0212 
0213     if (rule->isUnsupportedRule()) {
0214         item->setForeground(0, QColor(Qt::gray));
0215         item->setFont(0, QFont());
0216     } else if (rule->isException()) {
0217         item->setForeground(0, QColor(Qt::darkGreen));
0218         item->setFont(0, QFont());
0219     }
0220     else if (rule->isCssRule()) {
0221         item->setForeground(0, QColor(Qt::darkBlue));
0222         item->setFont(0, QFont());
0223     }
0224 }
0225 
0226 void AdBlockTreeWidget::keyPressEvent(QKeyEvent* event)
0227 {
0228     if (event->key() == Qt::Key_C && event->modifiers() & Qt::ControlModifier) {
0229         copyFilter();
0230     }
0231 
0232     if (event->key() == Qt::Key_Delete) {
0233         removeRule();
0234     }
0235 
0236     TreeWidget::keyPressEvent(event);
0237 }
0238 
0239 void AdBlockTreeWidget::refresh()
0240 {
0241     m_itemChangingBlock = true;
0242     clear();
0243 
0244     QFont boldFont;
0245     boldFont.setBold(true);
0246 
0247     m_topItem = new QTreeWidgetItem(this);
0248     m_topItem->setText(0, m_subscription->title());
0249     m_topItem->setFont(0, boldFont);
0250     m_topItem->setExpanded(true);
0251     addTopLevelItem(m_topItem);
0252 
0253     const QVector<AdBlockRule*> &allRules = m_subscription->allRules();
0254 
0255     int index = 0;
0256     for (const AdBlockRule* rule : allRules) {
0257         auto* item = new QTreeWidgetItem(m_topItem);
0258         item->setText(0, rule->filter());
0259         item->setData(0, Qt::UserRole + 10, index);
0260 
0261         if (m_subscription->canEditRules()) {
0262             item->setFlags(item->flags() | Qt::ItemIsEditable);
0263         }
0264 
0265         adjustItemFeatures(item, rule);
0266         ++index;
0267     }
0268 
0269     showRule(nullptr);
0270     m_itemChangingBlock = false;
0271 }