diff options
author | Aqua-sama <aqua@iserlohn-fortress.net> | 2017-02-28 10:18:08 +0100 |
---|---|---|
committer | Aqua-sama <aqua@iserlohn-fortress.net> | 2017-02-28 10:18:08 +0100 |
commit | 7f6c9b22d1016aa0dba709495fabf41397676039 (patch) | |
tree | 35898077b84be953c0645262aaa9470802b5267b /src/blocker | |
parent | Fixed crash when closing the first tab (diff) | |
download | smolbote-7f6c9b22d1016aa0dba709495fabf41397676039.tar.xz |
Blocker rewrites
Some code commenting
Moved Blocker files for src/blocker
Keyboard shortcut for Blocker dialog
Diffstat (limited to 'src/blocker')
-rw-r--r-- | src/blocker/blockermanager.cpp | 45 | ||||
-rw-r--r-- | src/blocker/blockermanager.h | 42 | ||||
-rw-r--r-- | src/blocker/blockerrule.cpp | 209 | ||||
-rw-r--r-- | src/blocker/blockerrule.h | 79 | ||||
-rw-r--r-- | src/blocker/blockersubscription.cpp | 138 | ||||
-rw-r--r-- | src/blocker/blockersubscription.h | 55 | ||||
-rw-r--r-- | src/blocker/regexp.cpp | 70 | ||||
-rw-r--r-- | src/blocker/regexp.h | 38 | ||||
-rw-r--r-- | src/blocker/subscriptiondialog.ui | 67 | ||||
-rw-r--r-- | src/blocker/subscriptionform.ui | 149 |
10 files changed, 892 insertions, 0 deletions
diff --git a/src/blocker/blockermanager.cpp b/src/blocker/blockermanager.cpp new file mode 100644 index 0000000..3dd516c --- /dev/null +++ b/src/blocker/blockermanager.cpp @@ -0,0 +1,45 @@ +/** LICENSE ******************************************************************** + ** + ** smolbote: yet another qute browser + ** Copyright (C) 2017 Xian Nox + ** + ** This program is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** This program is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with this program. If not, see <http://www.gnu.org/licenses/>. + ** + ******************************************************************************/ + +#include "blockermanager.h" +#include "ui_subscriptiondialog.h" + +#include "browser.h" +#include <QLabel> +#include <QListWidget> + +#include "blocker/blockersubscription.h" + +BlockerManager::BlockerManager(QWidget *parent) : + QDialog(parent), + ui(new Ui::UrlInterceptorDialog) +{ + ui->setupUi(this); + + for(QString listUrl : sSettings->value("blocker.subscriptions").toStringList()) { + BlockerSubscription *sub = new BlockerSubscription(listUrl, this); + ui->tabWidget->addTab(sub, sub->name()); + } +} + +BlockerManager::~BlockerManager() +{ + delete ui; +} diff --git a/src/blocker/blockermanager.h b/src/blocker/blockermanager.h new file mode 100644 index 0000000..eb5e04e --- /dev/null +++ b/src/blocker/blockermanager.h @@ -0,0 +1,42 @@ +/** LICENSE ******************************************************************** + ** + ** smolbote: yet another qute browser + ** Copyright (C) 2017 Xian Nox + ** + ** This program is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** This program is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with this program. If not, see <http://www.gnu.org/licenses/>. + ** + ******************************************************************************/ + +#ifndef URLINTERCEPTORDIALOG_H +#define URLINTERCEPTORDIALOG_H + +#include <QDialog> + +namespace Ui { +class UrlInterceptorDialog; +} + +class BlockerManager : public QDialog +{ + Q_OBJECT + +public: + explicit BlockerManager(QWidget *parent = 0); + ~BlockerManager(); + +private: + Ui::UrlInterceptorDialog *ui; +}; + +#endif // URLINTERCEPTORDIALOG_H diff --git a/src/blocker/blockerrule.cpp b/src/blocker/blockerrule.cpp new file mode 100644 index 0000000..1c118e2 --- /dev/null +++ b/src/blocker/blockerrule.cpp @@ -0,0 +1,209 @@ +/** LICENSE ******************************************************************** + ** + ** smolbote: yet another qute browser + ** Copyright (C) 2017 Xian Nox + ** + ** This program is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** This program is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with this program. If not, see <http://www.gnu.org/licenses/>. + ** + ******************************************************************************/ + +#include "blockerrule.h" + +/* AdBlock filter reference + * https://adblockplus.org/en/filters + * https://adblockplus.org/en/filter-cheatsheet + */ + +BlockerRule::BlockerRule(QString rule, QObject *parent) : + QObject(parent) +{ + QString pattern = rule; + + // Empty rule or comment + if(pattern.trimmed().isEmpty() || pattern.startsWith("!")) { + m_valid = false; + return; + } + + // Ignore element hiding rules for now + if(pattern.contains("##") || pattern.contains("#@#")) { + m_valid = false; + return; + } + + // Exception + if(pattern.startsWith("@@")) { + m_exception = true; + pattern.remove(0, 2); + } + + // Options + if(pattern.contains("$")) { + QString opts = pattern.mid(pattern.indexOf("$")+1); + for(QString opt : opts.split(',')) { + if(opt.endsWith("script")) { + if(opt.startsWith("~")) { + m_whitelistOptions.setFlag(RuleOption::script, true); + } else { + m_blacklistOptions.setFlag(RuleOption::script, true); + } + } else if(opt.endsWith("image")) { + if(opt.startsWith("~")) { + m_whitelistOptions.setFlag(RuleOption::image, true); + } else { + m_blacklistOptions.setFlag(RuleOption::image, true); + } + } else if(opt.endsWith("stylesheet")) { + if(opt.startsWith("~")) { + m_whitelistOptions.setFlag(RuleOption::stylesheet, true); + } else { + m_blacklistOptions.setFlag(RuleOption::stylesheet, true); + } + } else if(opt.endsWith("object")) { + if(opt.startsWith("~")) { + m_whitelistOptions.setFlag(RuleOption::object, true); + } else { + m_blacklistOptions.setFlag(RuleOption::object, true); + } + } else if(opt.endsWith("object-subrequest")) { + if(opt.startsWith("~")) { + m_whitelistOptions.setFlag(RuleOption::objectsubrequest, true); + } else { + m_blacklistOptions.setFlag(RuleOption::objectsubrequest, true); + } + } else if(opt.endsWith("subdocument")) { + if(opt.startsWith("~")) { + m_whitelistOptions.setFlag(RuleOption::subdocument, true); + } else { + m_blacklistOptions.setFlag(RuleOption::subdocument, true); + } + } + } + + pattern.remove(pattern.indexOf("$"), pattern.length()); + } + + // Regular expression + if(rule.startsWith("/") && rule.endsWith("/")) { + m_valid = true; + ruleExpression.setPattern(pattern); + return; + } + + // Domain rules + if(pattern.startsWith("||")) { + pattern.remove(0, 2); + // find the end point for the domain + int end = pattern.indexOf(QRegularExpression("(?:[^\\w\\d\\_\\-\\.\\%]|$)"), 0); + domainExpression.setPattern(pattern.mid(0, end)); + pattern.remove(0, end+1); + } else if(pattern.startsWith("|") && pattern.endsWith("|")) { + pattern.remove(0, 1); + pattern.chop(1); + domainExpression.setPattern(pattern); + } else { + domainExpression.setPattern(".*"); + } + + // Regular rule + ruleExpression.setWildcardPattern(pattern); + m_valid = true; +} + +bool BlockerRule::match(const QWebEngineUrlRequestInfo &info) +{ + if(!m_valid) { + return false; + } + + // if both domain and rule match + if(domainExpression.match(info.requestUrl().host()) && ruleExpression.match(info.requestUrl().toString())) { + + // option explicitly allows + if(matchOptions(info, m_whitelistOptions)) { + return false; + } + + // option explicitly bans + if(matchOptions(info, m_blacklistOptions)) { + return true; + } + + // no options, but both domain and rule match --> rule matches + return true; + } + + // domain and/or rule do not match + return false; +} + +bool BlockerRule::isValid() +{ + return m_valid; +} +bool BlockerRule::isException() +{ + return m_exception; +} + +QString BlockerRule::toString() const +{ + return QString("On [%1]: %2 %3").arg(domainExpression.pattern()).arg(ruleExpression.pattern()).arg(QString::number(m_blacklistOptions, 2)); +} + +bool BlockerRule::matchOptions(const QWebEngineUrlRequestInfo &info, const RuleOptions &options) +{ + // no options are defined + if(options == 0) { + return false; + } + + bool hasOption = false; + switch (info.resourceType()) { + case QWebEngineUrlRequestInfo::ResourceTypeScript: + if(options.testFlag(RuleOption::script)) { + hasOption = true; + } + break; + case QWebEngineUrlRequestInfo::ResourceTypeImage: + if(options.testFlag(RuleOption::image)) { + hasOption = true; + } + break; + case QWebEngineUrlRequestInfo::ResourceTypeStylesheet: + if(options.testFlag(RuleOption::stylesheet)) { + hasOption = true; + } + break; + case QWebEngineUrlRequestInfo::ResourceTypeObject: + if(options.testFlag(RuleOption::object)) { + hasOption = true; + } + break; + case QWebEngineUrlRequestInfo::ResourceTypePluginResource: + if(options.testFlag(RuleOption::objectsubrequest)) { + hasOption = true; + } + break; + case QWebEngineUrlRequestInfo::ResourceTypeSubFrame: + if(options.testFlag(RuleOption::subdocument)) { + hasOption = true; + } + break; + default: + break; + } + + return hasOption; +} diff --git a/src/blocker/blockerrule.h b/src/blocker/blockerrule.h new file mode 100644 index 0000000..0981514 --- /dev/null +++ b/src/blocker/blockerrule.h @@ -0,0 +1,79 @@ +/** LICENSE ******************************************************************** + ** + ** smolbote: yet another qute browser + ** Copyright (C) 2017 Xian Nox + ** + ** This program is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** This program is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with this program. If not, see <http://www.gnu.org/licenses/>. + ** + ******************************************************************************/ + +#ifndef ADBLOCKRULE_H +#define ADBLOCKRULE_H + +#include <QObject> +#include <QUrl> +#include "regexp.h" +#include <QWebEngineUrlRequestInfo> + +class BlockerRule : public QObject +{ + Q_OBJECT +public: + enum RuleOption { + script = 1, + image = 2, + stylesheet = 4, + object = 8, + xmlhttprequest = 16, + objectsubrequest = 32, + subdocument = 64, + ping = 128, + websocket = 256, + document = 512, + + elemhide = 1024, + generichide = 2048, + genericblock = 4096, + + other = 8192 + }; + + Q_DECLARE_FLAGS(RuleOptions, RuleOption) + + explicit BlockerRule(QString rule, QObject *parent = 0); + + bool match(const QWebEngineUrlRequestInfo &info); + bool isValid(); + bool isException(); + QString toString() const; + +signals: + +public slots: + +private: + bool matchOptions(const QWebEngineUrlRequestInfo &info, const RuleOptions &options); + + bool m_valid; + bool m_exception = false; + QStringList hostsBlacklist; + QStringList hostsWhitelist; + RegExp domainExpression; + RegExp ruleExpression; + + RuleOptions m_blacklistOptions; + RuleOptions m_whitelistOptions; +}; + +#endif // ADBLOCKRULE_H diff --git a/src/blocker/blockersubscription.cpp b/src/blocker/blockersubscription.cpp new file mode 100644 index 0000000..02def48 --- /dev/null +++ b/src/blocker/blockersubscription.cpp @@ -0,0 +1,138 @@ +/** LICENSE ******************************************************************** + ** + ** smolbote: yet another qute browser + ** Copyright (C) 2017 Xian Nox + ** + ** This program is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** This program is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with this program. If not, see <http://www.gnu.org/licenses/>. + ** + ******************************************************************************/ + +#include "blockersubscription.h" +#include "ui_subscriptionform.h" + +#include "browser.h" +#include <QFile> +#include <QNetworkRequest> +#include <QNetworkReply> + +BlockerSubscription::BlockerSubscription(const QString url, QWidget *parent) : + QWidget(parent), + ui(new Ui::SubscriptionForm) +{ + ui->setupUi(this); + QUrl _url = QUrl::fromUserInput(url); + m_name = _url.fileName(); + + if(!sSettings->value("blocker.path").toString().isEmpty()) { + QString cacheName = sSettings->value("blocker.path").toString() + m_name; + QFile *cache = new QFile(cacheName); + if(cache->exists()) { + load(cache); + } + + } else { + + // no cache path specified - pull the subscription + QNetworkRequest request; + request.setUrl(QUrl::fromUserInput(url)); + + QNetworkReply *reply = sNetwork->get(request); + connect(reply, &QNetworkReply::finished, [this, reply]() { + this->load(reply); + }); + } +} + +BlockerSubscription::~BlockerSubscription() +{ + delete ui; +} + +QString BlockerSubscription::name() const +{ + return m_name; +} + +void BlockerSubscription::load(QIODevice *dev) +{ + QTextStream subscription(dev); + + QString header = subscription.readLine(); + if(!header.startsWith("[Adblock Plus")) { + qDebug("Invalid format of subscription: %s", qUtf8Printable(m_name)); + return; + } + + // clear all lists + m_urlBlacklist.clear(); + m_urlWhitelist.clear(); + int rules = 0; + + while(!subscription.atEnd()) { + QString line = subscription.readLine(); + if(!line.isEmpty()) { + if(line.startsWith('!')) { + parseComment(line); + } else { + // The line is not a comment + + rules++; + BlockerRule *rule = new BlockerRule(line, this); + + if(rule->isValid()) { + if(rule->isException()) { + m_urlWhitelist.append(rule); + ui->whitelist_listWidget->addItem(rule->toString()); + } else { + ui->blacklist_listWidget->addItem(rule->toString()); + m_urlBlacklist.append(rule); + } + } + + } + } + } + + qDebug("Loaded %i/%i rules from subscription %s", m_urlBlacklist.count() + m_urlWhitelist.count(), rules, qUtf8Printable(m_name)); + dev->deleteLater(); +} + + +void BlockerSubscription::parseComment(const QString &line) +{ + if(line.startsWith("! Title: ")) { + ui->title->setText(line.right(line.length() - 9)); + return; + } + if(line.startsWith("! Homepage: ")) { + ui->homepage->setText(line.right(line.length() - 12)); + return; + } + if(line.startsWith("! Licence: ")) { + ui->license->setText(line.right(line.length() - 11)); + return; + } + if(line.startsWith("! Version: ")) { + ui->version->setText(line.right(line.length() - 11)); + return; + } + if(line.startsWith("! Last modified: ")) { + ui->lastModified->setText(line.right(line.length() - 17)); + return; + } + if(line.startsWith("! Expires: ")) { + ui->expires->setText(line.right(line.length() - 11).left(2)); + return; + } +} diff --git a/src/blocker/blockersubscription.h b/src/blocker/blockersubscription.h new file mode 100644 index 0000000..fddb93d --- /dev/null +++ b/src/blocker/blockersubscription.h @@ -0,0 +1,55 @@ +/** LICENSE ******************************************************************** + ** + ** smolbote: yet another qute browser + ** Copyright (C) 2017 Xian Nox + ** + ** This program is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** This program is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with this program. If not, see <http://www.gnu.org/licenses/>. + ** + ******************************************************************************/ + +#ifndef SUBSCRIPTIONFORM_H +#define SUBSCRIPTIONFORM_H + +#include <QWidget> +#include "blocker/blockerrule.h" + +namespace Ui { +class SubscriptionForm; +} + +class BlockerSubscription : public QWidget +{ + Q_OBJECT + +public: + explicit BlockerSubscription(const QString url, QWidget *parent = 0); + ~BlockerSubscription(); + + QString name() const; + +private slots: + void load(QIODevice *dev); + +private: + void parseComment(const QString &line); + + Ui::SubscriptionForm *ui; + QString m_name; + + QList<BlockerRule*> m_urlWhitelist; // exception rules + QList<BlockerRule*> m_urlBlacklist; // block rules + +}; + +#endif // SUBSCRIPTIONFORM_H diff --git a/src/blocker/regexp.cpp b/src/blocker/regexp.cpp new file mode 100644 index 0000000..a8dff79 --- /dev/null +++ b/src/blocker/regexp.cpp @@ -0,0 +1,70 @@ +/** LICENSE ******************************************************************** + ** + ** smolbote: yet another qute browser + ** Copyright (C) 2017 Xian Nox + ** + ** This program is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** This program is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with this program. If not, see <http://www.gnu.org/licenses/>. + ** + ******************************************************************************/ + +#include "regexp.h" + +RegExp::RegExp() : + QRegularExpression() +{ +} + +bool RegExp::match(const QString &subject, int offset, MatchType matchType, MatchOptions matchOptions) const +{ + // Empty matches all + if(pattern().isEmpty()) { + return true; + } + + return QRegularExpression::match(subject, offset, matchType, matchOptions).hasMatch(); +} + +void RegExp::setWildcardPattern(const QString &pattern) +{ + QString parsed; + + for(int i=0; i<pattern.length(); i++) { + const QChar c = pattern.at(i); + switch (c.toLatin1()) { + case '*': + // remove * at the start and end + if(i != 0 && i != pattern.length()-1) { + parsed.append(".*"); + } + break; + case '^': + parsed.append("(?:[^\\w\\d\\_\\-\\.\\%]|$)"); + break; + case '|': + if(i == 0) { + // beginning of string + parsed.append('^'); + } else { + // end of string + parsed.append('$'); + } + break; + default: + parsed.append(c); + break; + } + } + + setPattern(parsed); +} diff --git a/src/blocker/regexp.h b/src/blocker/regexp.h new file mode 100644 index 0000000..16827ad --- /dev/null +++ b/src/blocker/regexp.h @@ -0,0 +1,38 @@ +/** LICENSE ******************************************************************** + ** + ** smolbote: yet another qute browser + ** Copyright (C) 2017 Xian Nox + ** + ** This program is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** This program is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with this program. If not, see <http://www.gnu.org/licenses/>. + ** + ******************************************************************************/ + +#ifndef REGEXP_H +#define REGEXP_H + +#include <QRegularExpression> + +/*! + * Regular Expression class for AdBlockPlus filters + */ +class RegExp : public QRegularExpression +{ +public: + explicit RegExp(); + + bool match(const QString &subject, int offset=0, MatchType matchType=NormalMatch, MatchOptions matchOptions=NoMatchOption) const; + void setWildcardPattern(const QString &pattern); +}; + +#endif // REGEXP_H diff --git a/src/blocker/subscriptiondialog.ui b/src/blocker/subscriptiondialog.ui new file mode 100644 index 0000000..7a63cc9 --- /dev/null +++ b/src/blocker/subscriptiondialog.ui @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>UrlInterceptorDialog</class> + <widget class="QDialog" name="UrlInterceptorDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>640</width> + <height>480</height> + </rect> + </property> + <property name="windowTitle"> + <string>Blocker</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QTabWidget" name="tabWidget"/> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Close</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>UrlInterceptorDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>UrlInterceptorDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/blocker/subscriptionform.ui b/src/blocker/subscriptionform.ui new file mode 100644 index 0000000..703fb6d --- /dev/null +++ b/src/blocker/subscriptionform.ui @@ -0,0 +1,149 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>SubscriptionForm</class> + <widget class="QWidget" name="SubscriptionForm"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>640</width> + <height>480</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Subscription</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <layout class="QFormLayout" name="leftFormLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="title_label"> + <property name="text"> + <string>Title</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="title"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="homepage_label"> + <property name="text"> + <string>Homepage</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="homepage"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="license_label"> + <property name="text"> + <string>License</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="license"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QFormLayout" name="rightFormLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="version_label"> + <property name="text"> + <string>Version</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="version"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="lastModified_label"> + <property name="text"> + <string>Last Modified</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="lastModified"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="expires_label"> + <property name="text"> + <string>Expires</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="expires"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QTabWidget" name="tabWidget"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="blacklist_tab"> + <attribute name="title"> + <string>URL Blacklist</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QListWidget" name="blacklist_listWidget"/> + </item> + </layout> + </widget> + <widget class="QWidget" name="whitelist_tab"> + <attribute name="title"> + <string>URL Whitelist</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QListWidget" name="whitelist_listWidget"/> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> |