Import TabManager plugin from qupzilla-plugins
11
src/plugins/TabManager/README.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
TabManager extension for QupZilla
|
||||||
|
-------------------------------------------------
|
||||||
|
This extension adds the ability to manage tabs and windows in QupZilla.
|
||||||
|
|
||||||
|
![tbm3](http://i.imgur.com/Gh8bEXo.png)
|
||||||
|
|
||||||
|
You will find more information about the configuration and usage of this extension in the [wiki](https://github.com/QupZilla/qupzilla-plugins/wiki/Tab-Manager).
|
||||||
|
|
||||||
|
**TODOs**
|
||||||
|
|
||||||
|
* Modifying `refresh` algorithm for updating changed items and not all items.
|
30
src/plugins/TabManager/TabManager.pro
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
TARGET = $$qtLibraryTarget(TabManager)
|
||||||
|
# OS/2 allows only 8 chars in TARGET
|
||||||
|
os2: TARGET = TabManPl
|
||||||
|
|
||||||
|
SOURCES += tabmanagerplugin.cpp \
|
||||||
|
tabmanagerwidget.cpp \
|
||||||
|
tabmanagerwidgetcontroller.cpp
|
||||||
|
|
||||||
|
HEADERS += tabmanagerplugin.h \
|
||||||
|
tabmanagerwidget.h \
|
||||||
|
tabmanagerwidgetcontroller.h
|
||||||
|
|
||||||
|
RESOURCES += tabmanagerplugin.qrc
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
tabmanagerwidget.ui
|
||||||
|
|
||||||
|
TRANSLATIONS = \
|
||||||
|
translations/fa_IR.ts
|
||||||
|
|
||||||
|
include(tldextractor/tldextractor.pri)
|
||||||
|
|
||||||
|
PLUGIN_DIR = $$PWD
|
||||||
|
srcdir = $$(QUPZILLA_SRCDIR)
|
||||||
|
equals(srcdir, "") {
|
||||||
|
include(../../plugins.pri)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
include($$srcdir/src/plugins.pri)
|
||||||
|
}
|
BIN
src/plugins/TabManager/data/side-by-side.png
Normal file
After Width: | Height: | Size: 939 B |
BIN
src/plugins/TabManager/data/tab-bookmark.png
Normal file
After Width: | Height: | Size: 807 B |
BIN
src/plugins/TabManager/data/tab-close.png
Normal file
After Width: | Height: | Size: 976 B |
BIN
src/plugins/TabManager/data/tab-detach.png
Normal file
After Width: | Height: | Size: 784 B |
BIN
src/plugins/TabManager/data/tab-loading.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
src/plugins/TabManager/data/tab-pinned.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
src/plugins/TabManager/data/tabmanager.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
183
src/plugins/TabManager/tabmanagerplugin.cpp
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
/* ============================================================
|
||||||
|
* TabManager plugin for QupZilla
|
||||||
|
* Copyright (C) 2013 S. Razi Alavizadeh <s.r.alavizadeh@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 "tabmanagerplugin.h"
|
||||||
|
#include "tabmanagerwidgetcontroller.h"
|
||||||
|
#include "tabmanagerwidget.h"
|
||||||
|
#include "browserwindow.h"
|
||||||
|
#include "pluginproxy.h"
|
||||||
|
#include "mainapplication.h"
|
||||||
|
#include "sidebar.h"
|
||||||
|
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QTranslator>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QAction>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
QString TabManagerPlugin::s_settingsPath;
|
||||||
|
|
||||||
|
TabManagerPlugin::TabManagerPlugin()
|
||||||
|
: QObject()
|
||||||
|
, m_controller(0)
|
||||||
|
, m_tabManagerWidget(0)
|
||||||
|
, m_initState(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PluginSpec TabManagerPlugin::pluginSpec()
|
||||||
|
{
|
||||||
|
PluginSpec spec;
|
||||||
|
spec.name = "Tab Manager";
|
||||||
|
spec.info = "Simple yet powerful tab manager for QupZilla";
|
||||||
|
spec.description = "Adds ability to managing tabs and windows";
|
||||||
|
spec.version = "0.4.1";
|
||||||
|
spec.author = "Razi Alavizadeh <s.r.alavizadeh@gmail.com>";
|
||||||
|
spec.icon = QPixmap(":tabmanager/data/tabmanager.png");
|
||||||
|
spec.hasSettings = true;
|
||||||
|
|
||||||
|
return spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerPlugin::init(InitState state, const QString &settingsPath)
|
||||||
|
{
|
||||||
|
Q_UNUSED(state)
|
||||||
|
|
||||||
|
m_controller = new TabManagerWidgetController(this);
|
||||||
|
connect(mApp->plugins(), SIGNAL(mainWindowCreated(BrowserWindow*)), m_controller, SLOT(mainWindowCreated(BrowserWindow*)));
|
||||||
|
connect(mApp->plugins(), SIGNAL(mainWindowDeleted(BrowserWindow*)), m_controller, SLOT(mainWindowDeleted(BrowserWindow*)));
|
||||||
|
connect(mApp->plugins(), SIGNAL(webPageCreated(WebPage*)), m_controller, SIGNAL(requestRefreshTree()));
|
||||||
|
connect(mApp->plugins(), SIGNAL(webPageDeleted(WebPage*)), m_controller, SIGNAL(requestRefreshTree(WebPage*)));
|
||||||
|
|
||||||
|
s_settingsPath = settingsPath + QL1S("/TabManager");
|
||||||
|
m_initState = true;
|
||||||
|
|
||||||
|
// load settings
|
||||||
|
QSettings settings(s_settingsPath + QL1S("/tabmanager.ini"), QSettings::IniFormat);
|
||||||
|
settings.beginGroup("View");
|
||||||
|
m_controller->setGroupType(TabManagerWidget::GroupType(settings.value("GroupType", TabManagerWidget::GroupByWindow).toInt()));
|
||||||
|
m_controller->setViewType(TabManagerWidgetController::ViewType(settings.value("ViewType", TabManagerWidgetController::ShowAsWindow).toInt()));
|
||||||
|
settings.endGroup();
|
||||||
|
|
||||||
|
insertManagerWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerPlugin::unload()
|
||||||
|
{
|
||||||
|
// save settings
|
||||||
|
QSettings settings(s_settingsPath + QL1S("/tabmanager.ini"), QSettings::IniFormat);
|
||||||
|
settings.beginGroup("View");
|
||||||
|
settings.setValue("GroupType", m_controller->groupType());
|
||||||
|
settings.setValue("ViewType", m_controller->viewType());
|
||||||
|
settings.endGroup();
|
||||||
|
|
||||||
|
removeManagerWidget();
|
||||||
|
|
||||||
|
delete m_controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TabManagerPlugin::testPlugin()
|
||||||
|
{
|
||||||
|
return (Qz::VERSION == QLatin1String(QUPZILLA_VERSION));
|
||||||
|
}
|
||||||
|
|
||||||
|
QTranslator* TabManagerPlugin::getTranslator(const QString &locale)
|
||||||
|
{
|
||||||
|
QTranslator* translator = new QTranslator(this);
|
||||||
|
translator->load(locale, ":/tabmanager/locale/");
|
||||||
|
return translator;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerPlugin::showSettings(QWidget* parent)
|
||||||
|
{
|
||||||
|
bool ok;
|
||||||
|
QString viewType = QInputDialog::getItem(parent, tr("Tab Manager View Type"),
|
||||||
|
tr("<p>Please select view type:<br />"
|
||||||
|
"<b>Note:</b> The \"<i>Window</i>\" type is recommended for managing lots of windows/tabs")
|
||||||
|
, QStringList() << tr("SideBar") << tr("Window")
|
||||||
|
, m_controller->viewType(), false, &ok, Qt::WindowStaysOnTopHint);
|
||||||
|
TabManagerWidgetController::ViewType type;
|
||||||
|
if (viewType == tr("SideBar")) {
|
||||||
|
type = TabManagerWidgetController::ShowAsSideBar;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
type = TabManagerWidgetController::ShowAsWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ok && type != m_controller->viewType()) {
|
||||||
|
removeManagerWidget();
|
||||||
|
m_controller->setViewType(type);
|
||||||
|
insertManagerWidget();
|
||||||
|
|
||||||
|
if (type == TabManagerWidgetController::ShowAsSideBar) {
|
||||||
|
mApp->getWindow()->sideBarManager()->showSideBar("TabManager");
|
||||||
|
}
|
||||||
|
else if (type == TabManagerWidgetController::ShowAsWindow) {
|
||||||
|
// add statusbar icon
|
||||||
|
foreach (BrowserWindow* window, mApp->windows()) {
|
||||||
|
m_controller->addStatusBarIcon(window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerPlugin::insertManagerWidget()
|
||||||
|
{
|
||||||
|
if (m_controller->viewType() == TabManagerWidgetController::ShowAsSideBar) {
|
||||||
|
SideBarManager::addSidebar("TabManager", m_controller);
|
||||||
|
}
|
||||||
|
else if (m_controller->viewType() == TabManagerWidgetController::ShowAsWindow) {
|
||||||
|
if (!m_tabManagerWidget) {
|
||||||
|
m_tabManagerWidget = m_controller->createTabManagerWidget(mApp->getWindow(), 0, true);
|
||||||
|
m_tabManagerWidget->setWindowFlags(Qt::Window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_initState) {
|
||||||
|
foreach (BrowserWindow* window, mApp->windows()) {
|
||||||
|
m_controller->mainWindowCreated(window, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_initState = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerPlugin::removeManagerWidget()
|
||||||
|
{
|
||||||
|
if (m_controller->viewType() == TabManagerWidgetController::ShowAsSideBar) {
|
||||||
|
SideBarManager::removeSidebar("TabManager");
|
||||||
|
}
|
||||||
|
else if (m_controller->viewType() == TabManagerWidgetController::ShowAsWindow) {
|
||||||
|
// remove statusbar icon
|
||||||
|
foreach (BrowserWindow* window, mApp->windows()) {
|
||||||
|
m_controller->removeStatusBarIcon(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_tabManagerWidget->close();
|
||||||
|
delete m_tabManagerWidget;
|
||||||
|
m_tabManagerWidget = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TabManagerPlugin::settingsPath()
|
||||||
|
{
|
||||||
|
return s_settingsPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if QT_VERSION < 0x050000
|
||||||
|
Q_EXPORT_PLUGIN2(TabManager, TabManagerPlugin)
|
||||||
|
#endif
|
69
src/plugins/TabManager/tabmanagerplugin.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/* ============================================================
|
||||||
|
* TabManager plugin for QupZilla
|
||||||
|
* Copyright (C) 2013 S. Razi Alavizadeh <s.r.alavizadeh@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 TABMANAGERPLUGIN_H
|
||||||
|
#define TABMANAGERPLUGIN_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "plugininterface.h"
|
||||||
|
#include "tabmanagerwidgetcontroller.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QWebElement>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QPointer>
|
||||||
|
|
||||||
|
class TabManagerWidget;
|
||||||
|
|
||||||
|
class TabManagerPlugin : public QObject, public PluginInterface
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_INTERFACES(PluginInterface)
|
||||||
|
|
||||||
|
#if QT_VERSION >= 0x050000
|
||||||
|
Q_PLUGIN_METADATA(IID "QupZilla.Browser.plugin.TabManagerPlugin")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TabManagerPlugin();
|
||||||
|
PluginSpec pluginSpec();
|
||||||
|
|
||||||
|
void init(InitState state, const QString &settingsPath);
|
||||||
|
void unload();
|
||||||
|
bool testPlugin();
|
||||||
|
|
||||||
|
QTranslator* getTranslator(const QString &locale);
|
||||||
|
void showSettings(QWidget* parent = 0);
|
||||||
|
|
||||||
|
void removeManagerWidget();
|
||||||
|
|
||||||
|
static QString settingsPath();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void insertManagerWidget();
|
||||||
|
|
||||||
|
private:
|
||||||
|
TabManagerWidgetController* m_controller;
|
||||||
|
TabManagerWidget* m_tabManagerWidget;
|
||||||
|
static QString s_settingsPath;
|
||||||
|
QString m_viewType;
|
||||||
|
bool m_initState;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TABMANAGERPLUGIN_H
|
12
src/plugins/TabManager/tabmanagerplugin.qrc
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/tabmanager">
|
||||||
|
<file>data/tabmanager.png</file>
|
||||||
|
<file>data/tab-close.png</file>
|
||||||
|
<file>data/tab-bookmark.png</file>
|
||||||
|
<file>data/tab-detach.png</file>
|
||||||
|
<file>data/tab-loading.png</file>
|
||||||
|
<file>data/tab-pinned.png</file>
|
||||||
|
<file>data/side-by-side.png</file>
|
||||||
|
<file>locale/fa_IR.qm</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
595
src/plugins/TabManager/tabmanagerwidget.cpp
Normal file
@ -0,0 +1,595 @@
|
|||||||
|
/* ============================================================
|
||||||
|
* TabManager plugin for QupZilla
|
||||||
|
* Copyright (C) 2013 S. Razi Alavizadeh <s.r.alavizadeh@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 "tabmanagerwidget.h"
|
||||||
|
#include "ui_tabmanagerwidget.h"
|
||||||
|
#include "mainapplication.h"
|
||||||
|
#include "browserwindow.h"
|
||||||
|
#include "webtab.h"
|
||||||
|
#include "webpage.h"
|
||||||
|
#include "tabbedwebview.h"
|
||||||
|
#include "tabwidget.h"
|
||||||
|
#include "locationbar.h"
|
||||||
|
#include "bookmarkstools.h"
|
||||||
|
#include "bookmarkitem.h"
|
||||||
|
#include "bookmarks.h"
|
||||||
|
#include "tabmanagerplugin.h"
|
||||||
|
#include "tldextractor/tldextractor.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <QDesktopWidget>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QStackedWidget>
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QLabel>
|
||||||
|
|
||||||
|
#define WebTabPointerRole Qt::UserRole + 10
|
||||||
|
#define QupZillaPointerRole Qt::UserRole + 20
|
||||||
|
|
||||||
|
TLDExtractor* TabManagerWidget::s_tldExtractor = 0;
|
||||||
|
|
||||||
|
TabManagerWidget::TabManagerWidget(BrowserWindow* mainClass, QWidget* parent, bool defaultWidget)
|
||||||
|
: QWidget(parent)
|
||||||
|
, ui(new Ui::TabManagerWidget)
|
||||||
|
, p_QupZilla(mainClass)
|
||||||
|
, m_webPage(0)
|
||||||
|
, m_isRefreshing(false)
|
||||||
|
, m_refreshBlocked(false)
|
||||||
|
, m_waitForRefresh(false)
|
||||||
|
, m_isDefaultWidget(defaultWidget)
|
||||||
|
{
|
||||||
|
if(s_tldExtractor == 0)
|
||||||
|
{
|
||||||
|
s_tldExtractor = TLDExtractor::instance();
|
||||||
|
s_tldExtractor->setDataSearchPaths(QStringList() << TabManagerPlugin::settingsPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->setupUi(this);
|
||||||
|
ui->treeWidget->setExpandsOnDoubleClick(false);
|
||||||
|
ui->treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
|
||||||
|
connect(ui->treeWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(itemDoubleClick(QTreeWidgetItem*,int)));
|
||||||
|
connect(ui->treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TabManagerWidget::~TabManagerWidget()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerWidget::setGroupType(GroupType type)
|
||||||
|
{
|
||||||
|
m_groupType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TabManagerWidget::domainFromUrl(const QUrl &url, bool useHostName)
|
||||||
|
{
|
||||||
|
QString appendString = QL1S(":");
|
||||||
|
QString urlString = url.toString();
|
||||||
|
|
||||||
|
if (url.scheme() == "file") {
|
||||||
|
return tr("Local File System:");
|
||||||
|
}
|
||||||
|
else if (url.scheme() == "qupzilla" || urlString.isEmpty()) {
|
||||||
|
return tr("QupZilla:");
|
||||||
|
}
|
||||||
|
else if (url.scheme() == "ftp") {
|
||||||
|
appendString.prepend(tr(" [FTP]"));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString host = url.host();
|
||||||
|
if (host.isEmpty()) {
|
||||||
|
return urlString.append(appendString);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useHostName || host.contains(QRegExp("^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$"))) {
|
||||||
|
if (host.startsWith("www.", Qt::CaseInsensitive)) {
|
||||||
|
host.remove(0, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
return host.append(appendString);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const QString registeredDomain = s_tldExtractor->registrableDomain(host);
|
||||||
|
|
||||||
|
if (!registeredDomain.isEmpty()) {
|
||||||
|
host = registeredDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
return host.append(appendString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerWidget::delayedRefreshTree(WebPage* p)
|
||||||
|
{
|
||||||
|
if (m_refreshBlocked || m_waitForRefresh) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_isRefreshing && !p) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_webPage = p;
|
||||||
|
m_waitForRefresh = true;
|
||||||
|
QTimer::singleShot(50, this, SLOT(refreshTree()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerWidget::refreshTree()
|
||||||
|
{
|
||||||
|
if (m_refreshBlocked) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_isRefreshing && !m_webPage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// store selected items
|
||||||
|
QList<QWidget*> selectedTabs;
|
||||||
|
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) {
|
||||||
|
QTreeWidgetItem* winItem = ui->treeWidget->topLevelItem(i);
|
||||||
|
if (winItem->checkState(0) == Qt::Unchecked) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < winItem->childCount(); ++j) {
|
||||||
|
QTreeWidgetItem* tabItem = winItem->child(j);
|
||||||
|
if (tabItem->checkState(0) == Qt::Unchecked) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
selectedTabs << qvariant_cast<QWidget*>(tabItem->data(0, WebTabPointerRole));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->treeWidget->clear();
|
||||||
|
|
||||||
|
if (m_groupType == GroupByHost) {
|
||||||
|
groupByDomainName(true);
|
||||||
|
}
|
||||||
|
else if (m_groupType == GroupByDomain) {
|
||||||
|
groupByDomainName();
|
||||||
|
}
|
||||||
|
else { // fallback to GroupByWindow
|
||||||
|
m_groupType = GroupByWindow;
|
||||||
|
groupByWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore selected items
|
||||||
|
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) {
|
||||||
|
QTreeWidgetItem* winItem = ui->treeWidget->topLevelItem(i);
|
||||||
|
|
||||||
|
for (int j = 0; j < winItem->childCount(); ++j) {
|
||||||
|
QTreeWidgetItem* tabItem = winItem->child(j);
|
||||||
|
|
||||||
|
if (selectedTabs.contains(qvariant_cast<QWidget*>(tabItem->data(0, WebTabPointerRole)))) {
|
||||||
|
tabItem->setCheckState(0, Qt::Checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->treeWidget->expandAll();
|
||||||
|
m_isRefreshing = false;
|
||||||
|
m_waitForRefresh = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerWidget::itemDoubleClick(QTreeWidgetItem* item, int)
|
||||||
|
{
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BrowserWindow* mainWindow = qobject_cast<BrowserWindow*>(qvariant_cast<QWidget*>(item->data(0, QupZillaPointerRole)));
|
||||||
|
QWidget* tabWidget = qvariant_cast<QWidget*>(item->data(0, WebTabPointerRole));
|
||||||
|
|
||||||
|
if (!mainWindow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mainWindow->isMinimized()) {
|
||||||
|
mainWindow->showNormal();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mainWindow->show();
|
||||||
|
}
|
||||||
|
mainWindow->activateWindow();
|
||||||
|
mainWindow->raise();
|
||||||
|
mainWindow->weView()->setFocus();
|
||||||
|
|
||||||
|
if (tabWidget && tabWidget != mainWindow->tabWidget()->currentWidget()) {
|
||||||
|
mainWindow->tabWidget()->setCurrentIndex(mainWindow->tabWidget()->indexOf(tabWidget));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TabManagerWidget::isTabSelected()
|
||||||
|
{
|
||||||
|
bool selected = false;
|
||||||
|
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) {
|
||||||
|
QTreeWidgetItem* parentItem = ui->treeWidget->topLevelItem(i);
|
||||||
|
if (parentItem->checkState(0) != Qt::Unchecked) {
|
||||||
|
selected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerWidget::customContextMenuRequested(const QPoint &pos)
|
||||||
|
{
|
||||||
|
QMenu menu;
|
||||||
|
QAction* action;
|
||||||
|
QMenu groupTypeSubmenu(tr("Group by"));
|
||||||
|
action = groupTypeSubmenu.addAction(tr("&Window"), this, SLOT(changeGroupType()));
|
||||||
|
action->setData(GroupByWindow);
|
||||||
|
action->setCheckable(true);
|
||||||
|
action->setChecked(m_groupType == GroupByWindow);
|
||||||
|
|
||||||
|
action = groupTypeSubmenu.addAction(tr("&Domain"), this, SLOT(changeGroupType()));
|
||||||
|
action->setData(GroupByDomain);
|
||||||
|
action->setCheckable(true);
|
||||||
|
action->setChecked(m_groupType == GroupByDomain);
|
||||||
|
|
||||||
|
action = groupTypeSubmenu.addAction(tr("&Host"), this, SLOT(changeGroupType()));
|
||||||
|
action->setData(GroupByHost);
|
||||||
|
action->setCheckable(true);
|
||||||
|
action->setChecked(m_groupType == GroupByHost);
|
||||||
|
|
||||||
|
menu.addMenu(&groupTypeSubmenu);
|
||||||
|
|
||||||
|
if (m_isDefaultWidget) {
|
||||||
|
menu.addAction(QIcon(":/tabmanager/data/side-by-side.png"), tr("&Show side by side"), this, SIGNAL(showSideBySide()))->setObjectName("sideBySide");
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.addSeparator();
|
||||||
|
|
||||||
|
if (isTabSelected()) {
|
||||||
|
menu.addAction(QIcon(":/tabmanager/data/tab-detach.png"), tr("&Detach checked tabs"), this, SLOT(processActions()))->setObjectName("detachSelection");
|
||||||
|
menu.addAction(QIcon(":/tabmanager/data/tab-bookmark.png"), tr("Book&mark checked tabs"), this, SLOT(processActions()))->setObjectName("bookmarkSelection");
|
||||||
|
menu.addAction(QIcon(":/tabmanager/data/tab-close.png"), tr("&Close checked tabs"), this, SLOT(processActions()))->setObjectName("closeSelection");
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.exec(ui->treeWidget->viewport()->mapToGlobal(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerWidget::processActions()
|
||||||
|
{
|
||||||
|
if (!sender()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_refreshBlocked = true;
|
||||||
|
|
||||||
|
QHash<BrowserWindow*, WebTab*> selectedTabs;
|
||||||
|
|
||||||
|
const QString &command = sender()->objectName();
|
||||||
|
|
||||||
|
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) {
|
||||||
|
QTreeWidgetItem* winItem = ui->treeWidget->topLevelItem(i);
|
||||||
|
if (winItem->checkState(0) == Qt::Unchecked) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < winItem->childCount(); ++j) {
|
||||||
|
QTreeWidgetItem* tabItem = winItem->child(j);
|
||||||
|
if (tabItem->checkState(0) == Qt::Unchecked) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
BrowserWindow* mainWindow = qobject_cast<BrowserWindow*>(qvariant_cast<QWidget*>(tabItem->data(0, QupZillaPointerRole)));
|
||||||
|
WebTab* webTab = qobject_cast<WebTab*>(qvariant_cast<QWidget*>(tabItem->data(0, WebTabPointerRole)));
|
||||||
|
|
||||||
|
// current supported actions are not applied to pinned tabs
|
||||||
|
if (webTab->isPinned()) {
|
||||||
|
tabItem->setCheckState(0, Qt::Unchecked);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command == "closeSelection") {
|
||||||
|
if (webTab->url().toString() == "qupzilla:restore") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
selectedTabs.insertMulti(mainWindow, webTab);
|
||||||
|
}
|
||||||
|
else if (command == "detachSelection" || command == "bookmarkSelection") {
|
||||||
|
selectedTabs.insertMulti(mainWindow, webTab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
winItem->setCheckState(0, Qt::Unchecked);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!selectedTabs.isEmpty()) {
|
||||||
|
if (command == "closeSelection") {
|
||||||
|
closeSelectedTabs(selectedTabs);
|
||||||
|
}
|
||||||
|
else if (command == "detachSelection") {
|
||||||
|
detachSelectedTabs(selectedTabs);
|
||||||
|
}
|
||||||
|
else if (command == "bookmarkSelection") {
|
||||||
|
bookmarkSelectedTabs(selectedTabs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_refreshBlocked = false;
|
||||||
|
delayedRefreshTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerWidget::changeGroupType()
|
||||||
|
{
|
||||||
|
QAction* action = qobject_cast<QAction*>(sender());
|
||||||
|
|
||||||
|
if (action) {
|
||||||
|
int type = action->data().toInt();
|
||||||
|
|
||||||
|
if (m_groupType != GroupType(type)) {
|
||||||
|
m_groupType = GroupType(type);
|
||||||
|
|
||||||
|
delayedRefreshTree();
|
||||||
|
|
||||||
|
emit groupTypeChanged(m_groupType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerWidget::closeSelectedTabs(const QHash<BrowserWindow*, WebTab*> &tabsHash)
|
||||||
|
{
|
||||||
|
if (tabsHash.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QList<BrowserWindow*> &windows = tabsHash.uniqueKeys();
|
||||||
|
foreach (BrowserWindow* mainWindow, windows) {
|
||||||
|
QList<WebTab*> tabs = tabsHash.values(mainWindow);
|
||||||
|
|
||||||
|
foreach (WebTab* webTab, tabs) {
|
||||||
|
mainWindow->tabWidget()->closeTab(webTab->tabIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerWidget::detachSelectedTabs(const QHash<BrowserWindow*, WebTab*> &tabsHash)
|
||||||
|
{
|
||||||
|
// TODO: use TabWidget::detachTab()
|
||||||
|
if (tabsHash.isEmpty() ||
|
||||||
|
(tabsHash.uniqueKeys().size() == 1 &&
|
||||||
|
tabsHash.size() == tabsHash.keys().at(0)->tabWidget()->count())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BrowserWindow* newWindow = mApp->createWindow(Qz::BW_OtherRestoredWindow);;
|
||||||
|
newWindow->move(mApp->desktop()->availableGeometry(this).topLeft() + QPoint(30, 30));
|
||||||
|
|
||||||
|
const QList<BrowserWindow*> &windows = tabsHash.uniqueKeys();
|
||||||
|
foreach (BrowserWindow* mainWindow, windows) {
|
||||||
|
const QList<WebTab*> &tabs = tabsHash.values(mainWindow);
|
||||||
|
foreach (WebTab* webTab, tabs) {
|
||||||
|
mainWindow->tabWidget()->locationBars()->removeWidget(webTab->locationBar());
|
||||||
|
|
||||||
|
disconnect(webTab->webView(), SIGNAL(wantsCloseTab(int)), mainWindow->tabWidget(), SLOT(closeTab(int)));
|
||||||
|
disconnect(webTab->webView(), SIGNAL(changed()), mainWindow->tabWidget(), SIGNAL(changed()));
|
||||||
|
disconnect(webTab->webView(), SIGNAL(ipChanged(QString)), mainWindow->ipLabel(), SLOT(setText(QString)));
|
||||||
|
|
||||||
|
webTab->detach();
|
||||||
|
if (mainWindow && mainWindow->tabWidget()->count() == 0) {
|
||||||
|
mainWindow->close();
|
||||||
|
mainWindow = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
newWindow->tabWidget()->addView(webTab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TabManagerWidget::bookmarkSelectedTabs(const QHash<BrowserWindow*, WebTab*> &tabsHash)
|
||||||
|
{
|
||||||
|
QDialog* dialog = new QDialog(getQupZilla(), Qt::WindowStaysOnTopHint | Qt::MSWindowsFixedSizeDialogHint);
|
||||||
|
QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom, dialog);
|
||||||
|
QLabel* label = new QLabel(dialog);
|
||||||
|
BookmarksFoldersButton* folderButton = new BookmarksFoldersButton(dialog);
|
||||||
|
|
||||||
|
QDialogButtonBox* box = new QDialogButtonBox(dialog);
|
||||||
|
box->addButton(QDialogButtonBox::Ok);
|
||||||
|
box->addButton(QDialogButtonBox::Cancel);
|
||||||
|
QObject::connect(box, SIGNAL(rejected()), dialog, SLOT(reject()));
|
||||||
|
QObject::connect(box, SIGNAL(accepted()), dialog, SLOT(accept()));
|
||||||
|
|
||||||
|
layout->addWidget(label);
|
||||||
|
layout->addWidget(folderButton);
|
||||||
|
layout->addWidget(box);
|
||||||
|
|
||||||
|
label->setText(tr("Choose folder for bookmarks:"));
|
||||||
|
dialog->setWindowTitle(tr("Bookmark Selected Tabs"));
|
||||||
|
|
||||||
|
QSize size = dialog->size();
|
||||||
|
size.setWidth(350);
|
||||||
|
dialog->resize(size);
|
||||||
|
dialog->exec();
|
||||||
|
|
||||||
|
if (dialog->result() == QDialog::Rejected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (WebTab* tab, tabsHash) {
|
||||||
|
if (!tab->url().isEmpty()) {
|
||||||
|
BookmarkItem* bookmark = new BookmarkItem(BookmarkItem::Url);
|
||||||
|
bookmark->setTitle(tab->title());
|
||||||
|
bookmark->setUrl(tab->url());
|
||||||
|
mApp->bookmarks()->addBookmark(folderButton->selectedFolder(), bookmark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete dialog;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTreeWidgetItem* TabManagerWidget::createEmptyItem(QTreeWidgetItem* parent, bool addToTree)
|
||||||
|
{
|
||||||
|
QTreeWidgetItem* item = new QTreeWidgetItem(addToTree ? (parent ? parent : ui->treeWidget->invisibleRootItem()) : 0);
|
||||||
|
item->setFlags(item->flags() | (parent ? Qt::ItemIsUserCheckable : Qt::ItemIsUserCheckable | Qt::ItemIsTristate));
|
||||||
|
item->setCheckState(0, Qt::Unchecked);
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerWidget::groupByDomainName(bool useHostName)
|
||||||
|
{
|
||||||
|
QList<BrowserWindow*> windows = mApp->windows();
|
||||||
|
int currentWindowIdx = windows.indexOf(getQupZilla());
|
||||||
|
if (currentWindowIdx == -1) {
|
||||||
|
// getQupZilla() instance is closing
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
windows.move(currentWindowIdx, 0);
|
||||||
|
|
||||||
|
QMap<QString, QTreeWidgetItem*> tabsGroupedByDomain;
|
||||||
|
|
||||||
|
for (int win = 0; win < windows.count(); ++win) {
|
||||||
|
BrowserWindow* mainWin = windows.at(win);
|
||||||
|
|
||||||
|
QList<WebTab*> tabs = mainWin->tabWidget()->allTabs();
|
||||||
|
|
||||||
|
for (int tab = 0; tab < tabs.count(); ++tab) {
|
||||||
|
WebTab* webTab = tabs.at(tab);
|
||||||
|
if (webTab->webView() && m_webPage == webTab->webView()->page()) {
|
||||||
|
m_webPage = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QString domain = domainFromUrl(webTab->url(), useHostName);
|
||||||
|
|
||||||
|
if (!tabsGroupedByDomain.contains(domain)) {
|
||||||
|
QTreeWidgetItem* groupItem = createEmptyItem(0, false);
|
||||||
|
groupItem->setText(0, domain);
|
||||||
|
groupItem->setToolTip(0, domain);
|
||||||
|
QFont font = groupItem->font(0);
|
||||||
|
font.setBold(true);
|
||||||
|
groupItem->setFont(0, font);
|
||||||
|
tabsGroupedByDomain.insert(domain, groupItem);
|
||||||
|
}
|
||||||
|
QTreeWidgetItem* groupItem = tabsGroupedByDomain.value(domain);
|
||||||
|
|
||||||
|
QTreeWidgetItem* tabItem = createEmptyItem(groupItem);
|
||||||
|
if (webTab == mainWin->weView()->webTab()) {
|
||||||
|
QFont font = tabItem->font(0);
|
||||||
|
font.setBold(true);
|
||||||
|
tabItem->setFont(0, font);
|
||||||
|
}
|
||||||
|
if (!webTab->isLoading()) {
|
||||||
|
if (!webTab->isPinned()) {
|
||||||
|
tabItem->setIcon(0, webTab->icon());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tabItem->setIcon(0, QIcon(":tabmanager/data/tab-pinned.png"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tabItem->setIcon(0, QIcon(":tabmanager/data/tab-loading.png"));
|
||||||
|
}
|
||||||
|
tabItem->setText(0, webTab->title());
|
||||||
|
tabItem->setToolTip(0, webTab->title());
|
||||||
|
|
||||||
|
tabItem->setData(0, WebTabPointerRole, QVariant::fromValue(qobject_cast<QWidget*>(webTab)));
|
||||||
|
tabItem->setData(0, QupZillaPointerRole, QVariant::fromValue(qobject_cast<QWidget*>(mainWin)));
|
||||||
|
|
||||||
|
makeWebViewConnections(webTab->webView());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->treeWidget->insertTopLevelItems(0, tabsGroupedByDomain.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerWidget::groupByWindow()
|
||||||
|
{
|
||||||
|
QList<BrowserWindow*> windows = mApp->windows();
|
||||||
|
int currentWindowIdx = windows.indexOf(getQupZilla());
|
||||||
|
if (currentWindowIdx == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_isRefreshing = true;
|
||||||
|
|
||||||
|
if (!m_isDefaultWidget) {
|
||||||
|
windows.move(currentWindowIdx, 0);
|
||||||
|
currentWindowIdx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int win = 0; win < windows.count(); ++win) {
|
||||||
|
BrowserWindow* mainWin = windows.at(win);
|
||||||
|
QTreeWidgetItem* winItem = createEmptyItem();
|
||||||
|
winItem->setText(0, tr("Window %1").arg(QString::number(win + 1)));
|
||||||
|
winItem->setToolTip(0, tr("Double click to switch"));
|
||||||
|
if (win == currentWindowIdx) {
|
||||||
|
QFont font = winItem->font(0);
|
||||||
|
font.setBold(true);
|
||||||
|
winItem->setFont(0, font);
|
||||||
|
}
|
||||||
|
|
||||||
|
winItem->setData(0, QupZillaPointerRole, QVariant::fromValue(qobject_cast<QWidget*>(mainWin)));
|
||||||
|
QList<WebTab*> tabs = mainWin->tabWidget()->allTabs();
|
||||||
|
|
||||||
|
for (int tab = 0; tab < tabs.count(); ++tab) {
|
||||||
|
WebTab* webTab = tabs.at(tab);
|
||||||
|
if (webTab->webView() && m_webPage == webTab->webView()->page()) {
|
||||||
|
m_webPage = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QTreeWidgetItem* tabItem = createEmptyItem(winItem);
|
||||||
|
if (webTab == mainWin->weView()->webTab()) {
|
||||||
|
QFont font = tabItem->font(0);
|
||||||
|
font.setBold(true);
|
||||||
|
tabItem->setFont(0, font);
|
||||||
|
}
|
||||||
|
if (!webTab->isLoading()) {
|
||||||
|
if (!webTab->isPinned()) {
|
||||||
|
tabItem->setIcon(0, webTab->icon());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tabItem->setIcon(0, QIcon(":tabmanager/data/tab-pinned.png"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tabItem->setIcon(0, QIcon(":tabmanager/data/tab-loading.png"));
|
||||||
|
}
|
||||||
|
tabItem->setText(0, webTab->title());
|
||||||
|
tabItem->setToolTip(0, webTab->title());
|
||||||
|
|
||||||
|
tabItem->setData(0, WebTabPointerRole, QVariant::fromValue(qobject_cast<QWidget*>(webTab)));
|
||||||
|
tabItem->setData(0, QupZillaPointerRole, QVariant::fromValue(qobject_cast<QWidget*>(mainWin)));
|
||||||
|
|
||||||
|
makeWebViewConnections(webTab->webView());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BrowserWindow* TabManagerWidget::getQupZilla()
|
||||||
|
{
|
||||||
|
if (m_isDefaultWidget || !p_QupZilla) {
|
||||||
|
return mApp->getWindow();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return p_QupZilla.data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerWidget::makeWebViewConnections(QWebView* view)
|
||||||
|
{
|
||||||
|
if (view) {
|
||||||
|
connect(view->page(), SIGNAL(loadFinished(bool)), this, SLOT(delayedRefreshTree()));
|
||||||
|
connect(view->page(), SIGNAL(loadStarted()), this, SLOT(delayedRefreshTree()));
|
||||||
|
connect(view, SIGNAL(titleChanged(QString)), this, SLOT(delayedRefreshTree()));
|
||||||
|
connect(view, SIGNAL(iconChanged()), this, SLOT(delayedRefreshTree()));
|
||||||
|
}
|
||||||
|
}
|
95
src/plugins/TabManager/tabmanagerwidget.h
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/* ============================================================
|
||||||
|
* TabManager plugin for QupZilla
|
||||||
|
* Copyright (C) 2013 S. Razi Alavizadeh <s.r.alavizadeh@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 TABMANAGERWIDGET_H
|
||||||
|
#define TABMANAGERWIDGET_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QHash>
|
||||||
|
|
||||||
|
namespace Ui
|
||||||
|
{
|
||||||
|
class TabManagerWidget;
|
||||||
|
}
|
||||||
|
class QUrl;
|
||||||
|
class QTreeWidgetItem;
|
||||||
|
class BrowserWindow;
|
||||||
|
class WebPage;
|
||||||
|
class WebTab;
|
||||||
|
class QWebView;
|
||||||
|
class TLDExtractor;
|
||||||
|
|
||||||
|
class TabManagerWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum GroupType {
|
||||||
|
GroupByWindow = 0,
|
||||||
|
GroupByDomain = 1,
|
||||||
|
GroupByHost = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit TabManagerWidget(BrowserWindow* mainClass, QWidget* parent = 0, bool defaultWidget = false);
|
||||||
|
~TabManagerWidget();
|
||||||
|
|
||||||
|
void closeSelectedTabs(const QHash<BrowserWindow*, WebTab*> &tabsHash);
|
||||||
|
void detachSelectedTabs(const QHash<BrowserWindow*, WebTab*> &tabsHash);
|
||||||
|
bool bookmarkSelectedTabs(const QHash<BrowserWindow*, WebTab*> &tabsHash);
|
||||||
|
|
||||||
|
void setGroupType(GroupType type);
|
||||||
|
|
||||||
|
static QString domainFromUrl(const QUrl &url, bool useHostName = false);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void delayedRefreshTree(WebPage* p = 0);
|
||||||
|
void changeGroupType();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTreeWidgetItem* createEmptyItem(QTreeWidgetItem* parent = 0, bool addToTree = true);
|
||||||
|
void groupByDomainName(bool useHostName = false);
|
||||||
|
void groupByWindow();
|
||||||
|
BrowserWindow* getQupZilla();
|
||||||
|
|
||||||
|
void makeWebViewConnections(QWebView* view);
|
||||||
|
|
||||||
|
Ui::TabManagerWidget* ui;
|
||||||
|
QPointer<BrowserWindow> p_QupZilla;
|
||||||
|
WebPage* m_webPage;
|
||||||
|
|
||||||
|
bool m_isRefreshing;
|
||||||
|
bool m_refreshBlocked;
|
||||||
|
bool m_waitForRefresh;
|
||||||
|
bool m_isDefaultWidget;
|
||||||
|
GroupType m_groupType;
|
||||||
|
|
||||||
|
static TLDExtractor* s_tldExtractor;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void refreshTree();
|
||||||
|
void processActions();
|
||||||
|
void itemDoubleClick(QTreeWidgetItem* item, int);
|
||||||
|
bool isTabSelected();
|
||||||
|
void customContextMenuRequested(const QPoint &pos);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void showSideBySide();
|
||||||
|
void groupTypeChanged(TabManagerWidget::GroupType);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TABMANAGERWIDGET_H
|
38
src/plugins/TabManager/tabmanagerwidget.ui
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>TabManagerWidget</class>
|
||||||
|
<widget class="QWidget" name="TabManagerWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>266</width>
|
||||||
|
<height>368</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Tab Manager</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QTreeWidget" name="treeWidget">
|
||||||
|
<attribute name="headerVisible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">1</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>treeWidget</tabstop>
|
||||||
|
</tabstops>
|
||||||
|
<resources>
|
||||||
|
<include location="tabmanagerplugin.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
233
src/plugins/TabManager/tabmanagerwidgetcontroller.cpp
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
/* ============================================================
|
||||||
|
* TabManager plugin for QupZilla
|
||||||
|
* Copyright (C) 2013 S. Razi Alavizadeh <s.r.alavizadeh@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 "tabmanagerwidgetcontroller.h"
|
||||||
|
#include "tabmanagerwidget.h"
|
||||||
|
#include "clickablelabel.h"
|
||||||
|
#include "browserwindow.h"
|
||||||
|
#include "tabwidget.h"
|
||||||
|
#include "mainapplication.h"
|
||||||
|
|
||||||
|
#include <QDesktopWidget>
|
||||||
|
#include <QStatusBar>
|
||||||
|
#include <QAction>
|
||||||
|
#include <QStyle>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
TabManagerWidgetController::TabManagerWidgetController(QObject* parent)
|
||||||
|
: SideBarInterface(parent)
|
||||||
|
, m_defaultTabManager(0)
|
||||||
|
, m_viewType(ShowAsWindow)
|
||||||
|
, m_groupType(TabManagerWidget::GroupByWindow)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TabManagerWidgetController::~TabManagerWidgetController()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TabManagerWidgetController::title() const
|
||||||
|
{
|
||||||
|
return tr("Tab Manager");
|
||||||
|
}
|
||||||
|
|
||||||
|
QAction* TabManagerWidgetController::createMenuAction()
|
||||||
|
{
|
||||||
|
QAction* act = new QAction(tr("Tab Manager"), 0);
|
||||||
|
act->setCheckable(true);
|
||||||
|
act->setIcon(QIcon(":tabmanager/data/tabmanager.png"));
|
||||||
|
act->setShortcut(QKeySequence("Ctrl+Shift+M"));
|
||||||
|
act->setData("TabManager");
|
||||||
|
|
||||||
|
return act;
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget* TabManagerWidgetController::createSideBarWidget(BrowserWindow* mainWindow)
|
||||||
|
{
|
||||||
|
return createTabManagerWidget(mainWindow, mainWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget* TabManagerWidgetController::createStatusBarIcon(BrowserWindow* mainWindow)
|
||||||
|
{
|
||||||
|
if (!defaultTabManager()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_statusBarIcons.contains(mainWindow)) {
|
||||||
|
return m_statusBarIcons.value(mainWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClickableLabel* icon = new ClickableLabel(mainWindow);
|
||||||
|
icon->setCursor(Qt::PointingHandCursor);
|
||||||
|
QPixmap p(":tabmanager/data/tabmanager.png");
|
||||||
|
icon->setPixmap(p.scaledToHeight(16));
|
||||||
|
icon->setToolTip(tr("Show Tab Manager"));
|
||||||
|
|
||||||
|
QAction* showAction = createMenuAction();
|
||||||
|
showAction->setCheckable(false);
|
||||||
|
showAction->setParent(icon);
|
||||||
|
mainWindow->addAction(showAction);
|
||||||
|
connect(showAction, SIGNAL(triggered()), this, SLOT(raiseTabManager()));
|
||||||
|
|
||||||
|
connect(icon, SIGNAL(clicked(QPoint)), this, SLOT(raiseTabManager()));
|
||||||
|
|
||||||
|
m_statusBarIcons.insert(mainWindow, icon);
|
||||||
|
m_actions.insert(mainWindow, showAction);
|
||||||
|
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
TabManagerWidgetController::ViewType TabManagerWidgetController::viewType()
|
||||||
|
{
|
||||||
|
return m_viewType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerWidgetController::setViewType(ViewType type)
|
||||||
|
{
|
||||||
|
m_viewType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
TabManagerWidget::GroupType TabManagerWidgetController::groupType()
|
||||||
|
{
|
||||||
|
return m_groupType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerWidgetController::setGroupType(TabManagerWidget::GroupType type)
|
||||||
|
{
|
||||||
|
m_groupType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
TabManagerWidget* TabManagerWidgetController::createTabManagerWidget(BrowserWindow* mainClass, QWidget* parent, bool defaultWidget)
|
||||||
|
{
|
||||||
|
TabManagerWidget* tabManagerWidget = new TabManagerWidget(mainClass, parent, defaultWidget);
|
||||||
|
tabManagerWidget->setGroupType(m_groupType);
|
||||||
|
|
||||||
|
if (defaultWidget) {
|
||||||
|
m_defaultTabManager = tabManagerWidget;
|
||||||
|
QAction* showAction = createMenuAction();
|
||||||
|
showAction->setCheckable(false);
|
||||||
|
showAction->setParent(m_defaultTabManager);
|
||||||
|
m_defaultTabManager->addAction(showAction);
|
||||||
|
connect(showAction, SIGNAL(triggered()), this, SLOT(raiseTabManager()));
|
||||||
|
connect(tabManagerWidget, SIGNAL(showSideBySide()), this, SLOT(showSideBySide()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_defaultTabManager = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(tabManagerWidget, SIGNAL(groupTypeChanged(TabManagerWidget::GroupType)), this, SLOT(setGroupType(TabManagerWidget::GroupType)));
|
||||||
|
connect(this, SIGNAL(requestRefreshTree(WebPage*)), tabManagerWidget, SLOT(delayedRefreshTree(WebPage*)));
|
||||||
|
connect(this, SIGNAL(pinStateChanged(int,bool)), tabManagerWidget, SLOT(delayedRefreshTree()));
|
||||||
|
|
||||||
|
emit requestRefreshTree();
|
||||||
|
|
||||||
|
return tabManagerWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
TabManagerWidget* TabManagerWidgetController::defaultTabManager()
|
||||||
|
{
|
||||||
|
return m_defaultTabManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerWidgetController::addStatusBarIcon(BrowserWindow* window)
|
||||||
|
{
|
||||||
|
if (window) {
|
||||||
|
window->statusBar()->addPermanentWidget(createStatusBarIcon(window));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerWidgetController::removeStatusBarIcon(BrowserWindow* window)
|
||||||
|
{
|
||||||
|
if (window) {
|
||||||
|
window->statusBar()->removeWidget(m_statusBarIcons.value(window));
|
||||||
|
window->removeAction(m_actions.value(window));
|
||||||
|
delete m_actions.value(window);
|
||||||
|
delete m_statusBarIcons.value(window);
|
||||||
|
m_statusBarIcons.remove(window);
|
||||||
|
m_actions.remove(window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerWidgetController::mainWindowCreated(BrowserWindow* window, bool refresh)
|
||||||
|
{
|
||||||
|
if (window) {
|
||||||
|
addStatusBarIcon(window);
|
||||||
|
connect(window->tabWidget(), SIGNAL(currentChanged(int)), this, SIGNAL(requestRefreshTree()));
|
||||||
|
connect(window->tabWidget(), SIGNAL(pinStateChanged(int,bool)), this, SIGNAL(pinStateChanged(int,bool)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refresh) {
|
||||||
|
emit requestRefreshTree();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerWidgetController::mainWindowDeleted(BrowserWindow* window)
|
||||||
|
{
|
||||||
|
removeStatusBarIcon(window);
|
||||||
|
|
||||||
|
emit requestRefreshTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerWidgetController::raiseTabManager()
|
||||||
|
{
|
||||||
|
if (!defaultTabManager()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClickableLabel* icon = qobject_cast<ClickableLabel*>(sender());
|
||||||
|
if (icon) {
|
||||||
|
static int frameWidth = (defaultTabManager()->frameGeometry().width() - defaultTabManager()->geometry().width()) / 2;
|
||||||
|
static int titleBarHeight = defaultTabManager()->style()->pixelMetric(QStyle::PM_TitleBarHeight);
|
||||||
|
|
||||||
|
int y = qMax(0, icon->mapToGlobal(QPoint(0, 0)).y() - 1 - icon->window()->height() + titleBarHeight - frameWidth);
|
||||||
|
int x = icon->mapToGlobal(QPoint(0, 0)).x();
|
||||||
|
if (!mApp->isRightToLeft()) {
|
||||||
|
x -= defaultTabManager()->width();
|
||||||
|
}
|
||||||
|
QRect newGeo(x, y, defaultTabManager()->width(), icon->window()->height() - titleBarHeight - frameWidth);
|
||||||
|
defaultTabManager()->setGeometry(newGeo);
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultTabManager()->activateWindow();
|
||||||
|
defaultTabManager()->showNormal();
|
||||||
|
defaultTabManager()->raise();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabManagerWidgetController::showSideBySide()
|
||||||
|
{
|
||||||
|
if (!defaultTabManager()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const QRect &availableGeometry = mApp->desktop()->availableGeometry(defaultTabManager());
|
||||||
|
static int frameWidth = (defaultTabManager()->frameGeometry().width() - defaultTabManager()->geometry().width()) / 2;
|
||||||
|
static int titleBarHeight = defaultTabManager()->style()->pixelMetric(QStyle::PM_TitleBarHeight);
|
||||||
|
|
||||||
|
QRect managerRect(availableGeometry.left() + frameWidth, availableGeometry.top() + titleBarHeight,
|
||||||
|
defaultTabManager()->width(), availableGeometry.height() - titleBarHeight - frameWidth);
|
||||||
|
QRect qupzillaRect(managerRect.topRight().x() + 2 * frameWidth, managerRect.top(),
|
||||||
|
availableGeometry.width() - managerRect.width() - 4 * frameWidth, managerRect.height());
|
||||||
|
|
||||||
|
defaultTabManager()->setGeometry(managerRect);
|
||||||
|
mApp->getWindow()->setGeometry(qupzillaRect);
|
||||||
|
mApp->getWindow()->showNormal();
|
||||||
|
mApp->getWindow()->raise();
|
||||||
|
|
||||||
|
defaultTabManager()->show();
|
||||||
|
defaultTabManager()->activateWindow();
|
||||||
|
defaultTabManager()->raise();
|
||||||
|
}
|
75
src/plugins/TabManager/tabmanagerwidgetcontroller.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/* ============================================================
|
||||||
|
* TabManager plugin for QupZilla
|
||||||
|
* Copyright (C) 2013 S. Razi Alavizadeh <s.r.alavizadeh@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 TABMANAGERWIDGETCONTROLLER_H
|
||||||
|
#define TABMANAGERWIDGETCONTROLLER_H
|
||||||
|
|
||||||
|
#include "sidebarinterface.h"
|
||||||
|
#include "tabmanagerwidget.h"
|
||||||
|
|
||||||
|
class WebPage;
|
||||||
|
|
||||||
|
class TabManagerWidgetController : public SideBarInterface
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
enum ViewType {
|
||||||
|
ShowAsSideBar = 0,
|
||||||
|
ShowAsWindow = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit TabManagerWidgetController(QObject* parent = 0);
|
||||||
|
~TabManagerWidgetController();
|
||||||
|
|
||||||
|
QString title() const;
|
||||||
|
QAction* createMenuAction();
|
||||||
|
QWidget* createSideBarWidget(BrowserWindow* mainWindow);
|
||||||
|
|
||||||
|
QWidget* createStatusBarIcon(BrowserWindow* mainWindow);
|
||||||
|
|
||||||
|
ViewType viewType();
|
||||||
|
void setViewType(ViewType type);
|
||||||
|
|
||||||
|
TabManagerWidget::GroupType groupType();
|
||||||
|
TabManagerWidget* createTabManagerWidget(BrowserWindow* mainClass, QWidget* parent = 0, bool defaultWidget = false);
|
||||||
|
|
||||||
|
TabManagerWidget* defaultTabManager();
|
||||||
|
|
||||||
|
void addStatusBarIcon(BrowserWindow* window);
|
||||||
|
void removeStatusBarIcon(BrowserWindow* window);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setGroupType(TabManagerWidget::GroupType type);
|
||||||
|
void mainWindowCreated(BrowserWindow* window, bool refresh = true);
|
||||||
|
void mainWindowDeleted(BrowserWindow* window);
|
||||||
|
void raiseTabManager();
|
||||||
|
void showSideBySide();
|
||||||
|
|
||||||
|
private:
|
||||||
|
TabManagerWidget* m_defaultTabManager;
|
||||||
|
ViewType m_viewType;
|
||||||
|
TabManagerWidget::GroupType m_groupType;
|
||||||
|
|
||||||
|
QHash<BrowserWindow*, QWidget*> m_statusBarIcons;
|
||||||
|
QHash<BrowserWindow*, QAction*> m_actions;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void requestRefreshTree(WebPage* p = 0);
|
||||||
|
void pinStateChanged(int index, bool pinned);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TABMANAGERWIDGETCONTROLLER_H
|
44
src/plugins/TabManager/tldextractor/COPYRIGHT
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
-----------------------------------------------------------------------------
|
||||||
|
Project: TLDExtractor and its example application
|
||||||
|
GitHub link: https://github.com/srazi/TLDExtractor
|
||||||
|
|
||||||
|
CopyRight:
|
||||||
|
/* ============================================================
|
||||||
|
* TLDExtractor, a simple Qt interface to extract TLD part of a host
|
||||||
|
* Copyright (C) 2014 Razi Alavizadeh <s.r.alavizadeh@gmail.com>
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
* ============================================================ */
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
data file: data/effective_tld_names.dat
|
||||||
|
download link: http://mxr.mozilla.org/mozilla-central/source/netwerk/dns/effective_tld_names.dat?raw=1
|
||||||
|
|
||||||
|
CopyRight:
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
test file: data/test_psl.txt
|
||||||
|
download link: http://mxr.mozilla.org/mozilla-central/source/netwerk/test/unit/data/test_psl.txt?raw=1
|
||||||
|
|
||||||
|
CopyRight:
|
||||||
|
// Copyright is dedicated to the Public Domain.
|
||||||
|
// http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
-----------------------------------------------------------------------------
|
9079
src/plugins/TabManager/tldextractor/data/effective_tld_names.dat
Normal file
98
src/plugins/TabManager/tldextractor/data/test_psl.txt
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
// Any copyright is dedicated to the Public Domain.
|
||||||
|
// http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
|
||||||
|
// null input.
|
||||||
|
checkPublicSuffix(null, null);
|
||||||
|
// Mixed case.
|
||||||
|
checkPublicSuffix('COM', null);
|
||||||
|
checkPublicSuffix('example.COM', 'example.com');
|
||||||
|
checkPublicSuffix('WwW.example.COM', 'example.com');
|
||||||
|
// Leading dot.
|
||||||
|
checkPublicSuffix('.com', null);
|
||||||
|
checkPublicSuffix('.example', null);
|
||||||
|
checkPublicSuffix('.example.com', null);
|
||||||
|
checkPublicSuffix('.example.example', null);
|
||||||
|
// Unlisted TLD.
|
||||||
|
checkPublicSuffix('example', null);
|
||||||
|
checkPublicSuffix('example.example', 'example.example');
|
||||||
|
checkPublicSuffix('b.example.example', 'example.example');
|
||||||
|
checkPublicSuffix('a.b.example.example', 'example.example');
|
||||||
|
// Listed, but non-Internet, TLD.
|
||||||
|
//checkPublicSuffix('local', null);
|
||||||
|
//checkPublicSuffix('example.local', null);
|
||||||
|
//checkPublicSuffix('b.example.local', null);
|
||||||
|
//checkPublicSuffix('a.b.example.local', null);
|
||||||
|
// TLD with only 1 rule.
|
||||||
|
checkPublicSuffix('biz', null);
|
||||||
|
checkPublicSuffix('domain.biz', 'domain.biz');
|
||||||
|
checkPublicSuffix('b.domain.biz', 'domain.biz');
|
||||||
|
checkPublicSuffix('a.b.domain.biz', 'domain.biz');
|
||||||
|
// TLD with some 2-level rules.
|
||||||
|
checkPublicSuffix('com', null);
|
||||||
|
checkPublicSuffix('example.com', 'example.com');
|
||||||
|
checkPublicSuffix('b.example.com', 'example.com');
|
||||||
|
checkPublicSuffix('a.b.example.com', 'example.com');
|
||||||
|
checkPublicSuffix('uk.com', null);
|
||||||
|
checkPublicSuffix('example.uk.com', 'example.uk.com');
|
||||||
|
checkPublicSuffix('b.example.uk.com', 'example.uk.com');
|
||||||
|
checkPublicSuffix('a.b.example.uk.com', 'example.uk.com');
|
||||||
|
checkPublicSuffix('test.ac', 'test.ac');
|
||||||
|
// TLD with only 1 (wildcard) rule.
|
||||||
|
checkPublicSuffix('cy', null);
|
||||||
|
checkPublicSuffix('c.cy', null);
|
||||||
|
checkPublicSuffix('b.c.cy', 'b.c.cy');
|
||||||
|
checkPublicSuffix('a.b.c.cy', 'b.c.cy');
|
||||||
|
// More complex TLD.
|
||||||
|
checkPublicSuffix('jp', null);
|
||||||
|
checkPublicSuffix('test.jp', 'test.jp');
|
||||||
|
checkPublicSuffix('www.test.jp', 'test.jp');
|
||||||
|
checkPublicSuffix('ac.jp', null);
|
||||||
|
checkPublicSuffix('test.ac.jp', 'test.ac.jp');
|
||||||
|
checkPublicSuffix('www.test.ac.jp', 'test.ac.jp');
|
||||||
|
checkPublicSuffix('kyoto.jp', null);
|
||||||
|
checkPublicSuffix('test.kyoto.jp', 'test.kyoto.jp');
|
||||||
|
checkPublicSuffix('ide.kyoto.jp', null);
|
||||||
|
checkPublicSuffix('b.ide.kyoto.jp', 'b.ide.kyoto.jp');
|
||||||
|
checkPublicSuffix('a.b.ide.kyoto.jp', 'b.ide.kyoto.jp');
|
||||||
|
checkPublicSuffix('c.kobe.jp', null);
|
||||||
|
checkPublicSuffix('b.c.kobe.jp', 'b.c.kobe.jp');
|
||||||
|
checkPublicSuffix('a.b.c.kobe.jp', 'b.c.kobe.jp');
|
||||||
|
checkPublicSuffix('city.kobe.jp', 'city.kobe.jp');
|
||||||
|
checkPublicSuffix('www.city.kobe.jp', 'city.kobe.jp');
|
||||||
|
// TLD with a wildcard rule and exceptions.
|
||||||
|
checkPublicSuffix('ck', null);
|
||||||
|
checkPublicSuffix('test.ck', null);
|
||||||
|
checkPublicSuffix('b.test.ck', 'b.test.ck');
|
||||||
|
checkPublicSuffix('a.b.test.ck', 'b.test.ck');
|
||||||
|
checkPublicSuffix('www.ck', 'www.ck');
|
||||||
|
checkPublicSuffix('www.www.ck', 'www.ck');
|
||||||
|
// US K12.
|
||||||
|
checkPublicSuffix('us', null);
|
||||||
|
checkPublicSuffix('test.us', 'test.us');
|
||||||
|
checkPublicSuffix('www.test.us', 'test.us');
|
||||||
|
checkPublicSuffix('ak.us', null);
|
||||||
|
checkPublicSuffix('test.ak.us', 'test.ak.us');
|
||||||
|
checkPublicSuffix('www.test.ak.us', 'test.ak.us');
|
||||||
|
checkPublicSuffix('k12.ak.us', null);
|
||||||
|
checkPublicSuffix('test.k12.ak.us', 'test.k12.ak.us');
|
||||||
|
checkPublicSuffix('www.test.k12.ak.us', 'test.k12.ak.us');
|
||||||
|
// IDN labels.
|
||||||
|
checkPublicSuffix('食狮.com.cn', '食狮.com.cn');
|
||||||
|
checkPublicSuffix('食狮.公司.cn', '食狮.公司.cn');
|
||||||
|
checkPublicSuffix('www.食狮.公司.cn', '食狮.公司.cn');
|
||||||
|
checkPublicSuffix('shishi.公司.cn', 'shishi.公司.cn');
|
||||||
|
checkPublicSuffix('公司.cn', null);
|
||||||
|
checkPublicSuffix('食狮.中国', '食狮.中国');
|
||||||
|
checkPublicSuffix('www.食狮.中国', '食狮.中国');
|
||||||
|
checkPublicSuffix('shishi.中国', 'shishi.中国');
|
||||||
|
checkPublicSuffix('中国', null);
|
||||||
|
// Same as above, but punycoded.
|
||||||
|
checkPublicSuffix('xn--85x722f.com.cn', 'xn--85x722f.com.cn');
|
||||||
|
checkPublicSuffix('xn--85x722f.xn--55qx5d.cn', 'xn--85x722f.xn--55qx5d.cn');
|
||||||
|
checkPublicSuffix('www.xn--85x722f.xn--55qx5d.cn', 'xn--85x722f.xn--55qx5d.cn');
|
||||||
|
checkPublicSuffix('shishi.xn--55qx5d.cn', 'shishi.xn--55qx5d.cn');
|
||||||
|
checkPublicSuffix('xn--55qx5d.cn', null);
|
||||||
|
checkPublicSuffix('xn--85x722f.xn--fiqs8s', 'xn--85x722f.xn--fiqs8s');
|
||||||
|
checkPublicSuffix('www.xn--85x722f.xn--fiqs8s', 'xn--85x722f.xn--fiqs8s');
|
||||||
|
checkPublicSuffix('shishi.xn--fiqs8s', 'shishi.xn--fiqs8s');
|
||||||
|
checkPublicSuffix('xn--fiqs8s', null);
|
421
src/plugins/TabManager/tldextractor/tldextractor.cpp
Normal file
@ -0,0 +1,421 @@
|
|||||||
|
/* ============================================================
|
||||||
|
* TLDExtractor, a simple Qt interface to extract TLD part of a host
|
||||||
|
* Copyright (C) 2014 Razi Alavizadeh <s.r.alavizadeh@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 "tldextractor.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
TLDExtractor* TLDExtractor::s_instance = 0;
|
||||||
|
|
||||||
|
TLDExtractor::TLDExtractor(QObject* parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
setDataSearchPaths();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList TLDExtractor::defaultDataSearchPaths()
|
||||||
|
{
|
||||||
|
return QStringList() << QLatin1String(":/tldextractor/data");
|
||||||
|
}
|
||||||
|
|
||||||
|
TLDExtractor* TLDExtractor::instance()
|
||||||
|
{
|
||||||
|
if(s_instance == 0)
|
||||||
|
{
|
||||||
|
s_instance = new TLDExtractor(qApp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
TLDExtractor::~TLDExtractor()
|
||||||
|
{
|
||||||
|
s_instance = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TLDExtractor::isDataLoaded()
|
||||||
|
{
|
||||||
|
return !m_tldHash.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TLDExtractor::TLD(const QString &host)
|
||||||
|
{
|
||||||
|
if (host.isEmpty() || host.startsWith(QLatin1Char('.'))) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString cleanHost = normalizedHost(host);
|
||||||
|
|
||||||
|
QString tldPart = cleanHost.mid(cleanHost.lastIndexOf(QLatin1Char('.')) + 1);
|
||||||
|
cleanHost = QString::fromUtf8(QUrl::toAce(cleanHost));
|
||||||
|
|
||||||
|
loadData();
|
||||||
|
|
||||||
|
if (!m_tldHash.contains(tldPart)) {
|
||||||
|
return tldPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList tldRules = m_tldHash.values(tldPart);
|
||||||
|
|
||||||
|
if (!tldRules.contains(tldPart)) {
|
||||||
|
tldRules << tldPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxLabelCount = 0;
|
||||||
|
bool isExceptionTLD = false;
|
||||||
|
bool isWildcardTLD = false;
|
||||||
|
|
||||||
|
foreach(QString rule, tldRules) {
|
||||||
|
const int labelCount = rule.count(QLatin1Char('.')) + 1;
|
||||||
|
|
||||||
|
if (rule.startsWith(QLatin1Char('!'))) {
|
||||||
|
rule = rule.remove(0, 1);
|
||||||
|
|
||||||
|
rule = QString::fromUtf8(QUrl::toAce(rule));
|
||||||
|
isExceptionTLD = true;
|
||||||
|
|
||||||
|
// matches with exception TLD
|
||||||
|
if (cleanHost.endsWith(rule)) {
|
||||||
|
tldPart = rule.mid(rule.indexOf(QLatin1Char('.')) + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
isExceptionTLD = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rule.startsWith(QLatin1Char('*'))) {
|
||||||
|
rule.remove(0, 1);
|
||||||
|
|
||||||
|
if (rule.startsWith(QLatin1Char('.'))) {
|
||||||
|
rule.remove(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
isWildcardTLD = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
isWildcardTLD = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_UNUSED(isExceptionTLD)
|
||||||
|
|
||||||
|
rule = QString::fromUtf8(QUrl::toAce(rule));
|
||||||
|
const QString testRule = QLatin1Char('.') + rule;
|
||||||
|
const QString testUrl = QLatin1Char('.') + cleanHost;
|
||||||
|
|
||||||
|
if (labelCount > maxLabelCount && testUrl.endsWith(testRule)) {
|
||||||
|
tldPart = rule;
|
||||||
|
maxLabelCount = labelCount;
|
||||||
|
|
||||||
|
if (isWildcardTLD) {
|
||||||
|
QString temp = cleanHost;
|
||||||
|
temp.remove(temp.lastIndexOf(tldPart), tldPart.size());
|
||||||
|
|
||||||
|
if (temp.endsWith(QLatin1Char('.'))) {
|
||||||
|
temp.remove(temp.size() - 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
temp = temp.mid(temp.lastIndexOf(QLatin1Char('.')) + 1);
|
||||||
|
|
||||||
|
tldPart = temp.isEmpty() ? rule : (temp + "." + rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString temp = normalizedHost(host);
|
||||||
|
tldPart = temp.section(QLatin1Char('.'), temp.count(QLatin1Char('.')) - tldPart.count(QLatin1Char('.')));
|
||||||
|
|
||||||
|
return tldPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TLDExtractor::domain(const QString &host)
|
||||||
|
{
|
||||||
|
const QString tldPart = TLD(host);
|
||||||
|
|
||||||
|
return domainHelper(host, tldPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TLDExtractor::domainHelper(const QString &host, const QString &tldPart)
|
||||||
|
{
|
||||||
|
if (host.isEmpty() || tldPart.isEmpty()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString temp = normalizedHost(host);
|
||||||
|
temp.remove(temp.lastIndexOf(tldPart), tldPart.size());
|
||||||
|
|
||||||
|
if (temp.endsWith(QLatin1Char('.'))) {
|
||||||
|
temp.remove(temp.size() - 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return temp.mid(temp.lastIndexOf(QLatin1Char('.')) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TLDExtractor::registrableDomainHelper(const QString &domainPart, const QString &tldPart)
|
||||||
|
{
|
||||||
|
if (tldPart.isEmpty() || domainPart.isEmpty()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return QString("%1.%2").arg(domainPart).arg(tldPart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TLDExtractor::subdomainHelper(const QString &host, const QString ®istrablePart)
|
||||||
|
{
|
||||||
|
if (!registrablePart.isEmpty()) {
|
||||||
|
QString subdomain = normalizedHost(host);
|
||||||
|
|
||||||
|
subdomain.remove(subdomain.lastIndexOf(registrablePart), registrablePart.size());
|
||||||
|
|
||||||
|
if (subdomain.endsWith(QLatin1Char('.'))) {
|
||||||
|
subdomain.remove(subdomain.size() - 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return subdomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TLDExtractor::registrableDomain(const QString &host)
|
||||||
|
{
|
||||||
|
const QString tldPart = TLD(host);
|
||||||
|
|
||||||
|
return registrableDomainHelper(domainHelper(host, tldPart), tldPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TLDExtractor::subdomain(const QString &host)
|
||||||
|
{
|
||||||
|
return subdomainHelper(host, registrableDomain(host));
|
||||||
|
}
|
||||||
|
|
||||||
|
// a light function that extract all parts with just one call to TLD()
|
||||||
|
TLDExtractor::HostParts TLDExtractor::splitParts(const QString &host)
|
||||||
|
{
|
||||||
|
HostParts hostParts;
|
||||||
|
|
||||||
|
hostParts.host = host;
|
||||||
|
hostParts.tld = TLD(host);
|
||||||
|
hostParts.domain = domainHelper(host, hostParts.tld);
|
||||||
|
hostParts.registrableDomain = registrableDomainHelper(hostParts.domain, hostParts.tld);
|
||||||
|
hostParts.subdomain = subdomainHelper(host, hostParts.registrableDomain);
|
||||||
|
|
||||||
|
return hostParts;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList TLDExtractor::dataSearchPaths() const
|
||||||
|
{
|
||||||
|
return m_dataSearchPaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TLDExtractor::setDataSearchPaths(const QStringList &searchPaths)
|
||||||
|
{
|
||||||
|
m_dataSearchPaths = searchPaths;
|
||||||
|
|
||||||
|
m_dataSearchPaths << TLDExtractor::defaultDataSearchPaths();
|
||||||
|
|
||||||
|
m_dataSearchPaths.removeDuplicates();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TLDExtractor::loadData()
|
||||||
|
{
|
||||||
|
if (isDataLoaded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString dataFileName;
|
||||||
|
bool parsedDataFileExist = false;
|
||||||
|
|
||||||
|
foreach(const QString &path, m_dataSearchPaths) {
|
||||||
|
dataFileName = QFileInfo(path + QLatin1String("/effective_tld_names.dat")).absoluteFilePath();
|
||||||
|
|
||||||
|
if (QFileInfo(dataFileName).exists()) {
|
||||||
|
parsedDataFileExist = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!parsedDataFileExist) {
|
||||||
|
const QString tldDataFileDownloadLink = QLatin1String("http://mxr.mozilla.org/mozilla-central/source/netwerk/dns/effective_tld_names.dat?raw=1");
|
||||||
|
QMessageBox::information(0, tr("File not found!"),
|
||||||
|
tr("File \'effective_tld_names.dat\' was not found!\n"
|
||||||
|
"You can download it form \'<a href=\"%1\"><b>here</b></a>\' to one of the following paths:\n%2")
|
||||||
|
.arg(tldDataFileDownloadLink).arg(m_dataSearchPaths.join("\n")));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dataFileName = dataFileName;
|
||||||
|
|
||||||
|
if (!parseData(dataFileName)) {
|
||||||
|
qWarning() << "TLDExtractor: There is some parse errors for file:" << dataFileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TLDExtractor::parseData(const QString &dataFile, bool loadPrivateDomains)
|
||||||
|
{
|
||||||
|
m_tldHash.clear();
|
||||||
|
|
||||||
|
QFile file(dataFile);
|
||||||
|
|
||||||
|
if (!file.open(QFile::ReadOnly | QFile::Text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool seekToEndOfPrivateDomains = false;
|
||||||
|
|
||||||
|
while (!file.atEnd()) {
|
||||||
|
QString line = QString::fromUtf8(file.readLine().constData()).simplified();
|
||||||
|
|
||||||
|
if (line.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.startsWith(QLatin1Char('.'))) {
|
||||||
|
line.remove(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (line.startsWith(QLatin1String("//"))) {
|
||||||
|
if (line.contains(QLatin1String("===END PRIVATE DOMAINS==="))) {
|
||||||
|
seekToEndOfPrivateDomains = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loadPrivateDomains && line.contains(QLatin1String("===BEGIN PRIVATE DOMAINS==="))) {
|
||||||
|
if (m_tldHash.isEmpty()) {
|
||||||
|
seekToEndOfPrivateDomains = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seekToEndOfPrivateDomains) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each line is only read up to the first whitespace
|
||||||
|
line = line.left(line.indexOf(QLatin1Char(' ')));
|
||||||
|
|
||||||
|
if (!line.contains(QLatin1Char('.'))) {
|
||||||
|
m_tldHash.insertMulti(line, line);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
QString key = line.mid(line.lastIndexOf(QLatin1Char('.')) + 1);
|
||||||
|
|
||||||
|
m_tldHash.insertMulti(key, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isDataLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TLDExtractor::normalizedHost(const QString &host) const
|
||||||
|
{
|
||||||
|
return host.toLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
// methods for testing
|
||||||
|
bool TLDExtractor::test()
|
||||||
|
{
|
||||||
|
if (!parseData(m_dataFileName, true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString testDataFileName;
|
||||||
|
bool testDataFileExist = false;
|
||||||
|
|
||||||
|
foreach(const QString &path, m_dataSearchPaths) {
|
||||||
|
testDataFileName = QFileInfo(path + QLatin1String("/test_psl.txt")).absoluteFilePath();
|
||||||
|
|
||||||
|
if (QFileInfo(testDataFileName).exists()) {
|
||||||
|
testDataFileExist = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!testDataFileExist) {
|
||||||
|
const QString testFileDownloadLink = QLatin1String("http://mxr.mozilla.org/mozilla-central/source/netwerk/test/unit/data/test_psl.txt?raw=1");
|
||||||
|
|
||||||
|
QMessageBox::information(0, tr("File not found!"),
|
||||||
|
tr("File \'test_psl.txt\' was not found!\n"
|
||||||
|
"You can download it form \'<a href=\"%1\"><b>here</b></a>\' to one of the following paths:\n%2")
|
||||||
|
.arg(testFileDownloadLink).arg(m_dataSearchPaths.join("\n")));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile file(testDataFileName);
|
||||||
|
|
||||||
|
if (!file.open(QFile::ReadOnly | QFile::Text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRegExp testRegExp("checkPublicSuffix\\(('([^']+)'|null), ('([^']+)'|null)\\);");
|
||||||
|
bool allTestSuccess = true;
|
||||||
|
|
||||||
|
while (!file.atEnd()) {
|
||||||
|
QString line = QString::fromUtf8(file.readLine().constData()).simplified();
|
||||||
|
|
||||||
|
if (line.startsWith(QLatin1String("//")) || line.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
line.indexOf(testRegExp);
|
||||||
|
|
||||||
|
const QString hostName = testRegExp.cap(2);
|
||||||
|
const QString registrableName = testRegExp.cap(4);
|
||||||
|
|
||||||
|
if (!checkPublicSuffix(hostName, registrableName)) {
|
||||||
|
allTestSuccess = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allTestSuccess) {
|
||||||
|
qWarning() << "TLDExtractor: Test passed successfully.";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qWarning() << "TLDExtractor: Test finished with some errors!";
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset cache for normal use
|
||||||
|
m_tldHash.clear();
|
||||||
|
|
||||||
|
return allTestSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TLDExtractor::checkPublicSuffix(const QString &hostName, const QString ®istrableName)
|
||||||
|
{
|
||||||
|
if (registrableDomain(hostName) != registrableName) {
|
||||||
|
qWarning() << "TLDExtractor Test Error: hostName:" << hostName
|
||||||
|
<< "Correct registrableName:" << registrableName
|
||||||
|
<< "Wrong registrableName:" << registrableDomain(hostName);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
81
src/plugins/TabManager/tldextractor/tldextractor.h
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/* ============================================================
|
||||||
|
* TLDExtractor, a simple Qt interface to extract TLD part of a host
|
||||||
|
* Copyright (C) 2014 Razi Alavizadeh <s.r.alavizadeh@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 TLDEXTRACTOR_H
|
||||||
|
#define TLDEXTRACTOR_H
|
||||||
|
|
||||||
|
#define TLDExtractor_Version "1.0"
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
class TLDExtractor : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
static TLDExtractor* instance();
|
||||||
|
~TLDExtractor();
|
||||||
|
|
||||||
|
bool isDataLoaded();
|
||||||
|
|
||||||
|
struct HostParts {
|
||||||
|
QString host;
|
||||||
|
QString tld;
|
||||||
|
QString domain;
|
||||||
|
QString registrableDomain;
|
||||||
|
QString subdomain;
|
||||||
|
};
|
||||||
|
|
||||||
|
QString TLD(const QString &host);
|
||||||
|
QString domain(const QString &host);
|
||||||
|
QString registrableDomain(const QString &host);
|
||||||
|
QString subdomain(const QString &host);
|
||||||
|
|
||||||
|
HostParts splitParts(const QString &host);
|
||||||
|
|
||||||
|
QStringList dataSearchPaths() const;
|
||||||
|
void setDataSearchPaths(const QStringList &searchPaths = TLDExtractor::defaultDataSearchPaths());
|
||||||
|
|
||||||
|
bool test();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_DISABLE_COPY(TLDExtractor)
|
||||||
|
|
||||||
|
static TLDExtractor* s_instance;
|
||||||
|
TLDExtractor(QObject* parent = 0);
|
||||||
|
|
||||||
|
static QStringList defaultDataSearchPaths();
|
||||||
|
|
||||||
|
void loadData();
|
||||||
|
bool parseData(const QString &dataFile, bool loadPrivateDomains = false);
|
||||||
|
|
||||||
|
QString domainHelper(const QString &host, const QString &tldPart);
|
||||||
|
QString registrableDomainHelper(const QString &domainPart, const QString &tldPart);
|
||||||
|
QString subdomainHelper(const QString &host, const QString ®istrablePart);
|
||||||
|
|
||||||
|
QString normalizedHost(const QString &host) const;
|
||||||
|
|
||||||
|
bool checkPublicSuffix(const QString &hostName, const QString ®istrableName);
|
||||||
|
|
||||||
|
QString m_dataFileName;
|
||||||
|
QStringList m_dataSearchPaths;
|
||||||
|
|
||||||
|
QMultiHash<QString, QString> m_tldHash;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TLDEXTRACTOR_H
|
14
src/plugins/TabManager/tldextractor/tldextractor.pri
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# TLDExtractor, a simple Qt interface to extract TLD part of a host
|
||||||
|
# Copyright (C) 2014 Razi Alavizadeh <s.r.alavizadeh@gmail.com>
|
||||||
|
#
|
||||||
|
# Project created by QtCreator 2014-12-04T15:32:18
|
||||||
|
#
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
INCLUDEPATH += $$PWD
|
||||||
|
|
||||||
|
SOURCES += $$PWD/tldextractor.cpp
|
||||||
|
HEADERS += $$PWD/tldextractor.h
|
||||||
|
|
||||||
|
RESOURCES += $$PWD/tldextractor.qrc
|
6
src/plugins/TabManager/tldextractor/tldextractor.qrc
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/tldextractor">
|
||||||
|
<file>data/effective_tld_names.dat</file>
|
||||||
|
<file>data/test_psl.txt</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
110
src/plugins/TabManager/translations/de_DE.ts
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.0" language="de_DE">
|
||||||
|
<context>
|
||||||
|
<name>TabManagerPlugin</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerplugin.cpp" line="104"/>
|
||||||
|
<source>Tab Manager View Type</source>
|
||||||
|
<translation>Tab-Manager Darstellung</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerplugin.cpp" line="105"/>
|
||||||
|
<source><p>Please select view type:<br /><b>Note:</b> The "<i>Window</i>" type is recommended for managing lots of windows/tabs</source>
|
||||||
|
<translation><p>Bitte Darstellung wählen:<br /><b>Hinweis:</b> Darstellung "<i>Fenster</i>" ist für die Verwaltung von vielen Fenstern/ Tabs empfohlen</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerplugin.cpp" line="107"/>
|
||||||
|
<location filename="../tabmanagerplugin.cpp" line="110"/>
|
||||||
|
<source>SideBar</source>
|
||||||
|
<translation>Seiten-Leiste</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerplugin.cpp" line="107"/>
|
||||||
|
<source>Window</source>
|
||||||
|
<translation>Fenster</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>TabManagerWidget</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.ui" line="14"/>
|
||||||
|
<source>Tab Manager</source>
|
||||||
|
<translation>Tab-Manager</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.ui" line="25"/>
|
||||||
|
<source>Group By:</source>
|
||||||
|
<translation>Gruppieren nach:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.ui" line="48"/>
|
||||||
|
<source>...</source>
|
||||||
|
<translation>...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="54"/>
|
||||||
|
<source>Window</source>
|
||||||
|
<translation>Fenster</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="54"/>
|
||||||
|
<source>Domain</source>
|
||||||
|
<translation>Domäne</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="54"/>
|
||||||
|
<source>Host</source>
|
||||||
|
<translation>Host</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="88"/>
|
||||||
|
<source>Local File System:</source>
|
||||||
|
<translation>Lokales Dateisystem:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="91"/>
|
||||||
|
<source>QupZilla:</source>
|
||||||
|
<translation>QupZilla:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="94"/>
|
||||||
|
<source> [FTP]</source>
|
||||||
|
<translation> [FTP]</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="412"/>
|
||||||
|
<source>Choose folder for bookmarks:</source>
|
||||||
|
<translation>Ordner für Lesezeichen auswählen:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="413"/>
|
||||||
|
<source>Bookmark Selected Tabs</source>
|
||||||
|
<translation>Ausgewählte Tabs speichern</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="526"/>
|
||||||
|
<source>Window %1</source>
|
||||||
|
<translation>Fenster %1</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="527"/>
|
||||||
|
<source>Double click to switch</source>
|
||||||
|
<translation>Zum Wechseln doppelklicken</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>TabManagerWidgetController</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidgetcontroller.cpp" line="46"/>
|
||||||
|
<location filename="../tabmanagerwidgetcontroller.cpp" line="51"/>
|
||||||
|
<source>Tab Manager</source>
|
||||||
|
<translation>Tab-Manager</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidgetcontroller.cpp" line="79"/>
|
||||||
|
<source>Show/Hide Tab Manager</source>
|
||||||
|
<translation>Tab-Manager anzeigen/ verbergen</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
110
src/plugins/TabManager/translations/empty.ts
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.0">
|
||||||
|
<context>
|
||||||
|
<name>TabManagerPlugin</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerplugin.cpp" line="106"/>
|
||||||
|
<source>Tab Manager View Type</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerplugin.cpp" line="107"/>
|
||||||
|
<source><p>Please select view type:<br /><b>Note:</b> The "<i>Window</i>" type is recommended for managing lots of windows/tabs</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerplugin.cpp" line="109"/>
|
||||||
|
<location filename="../tabmanagerplugin.cpp" line="112"/>
|
||||||
|
<source>SideBar</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerplugin.cpp" line="109"/>
|
||||||
|
<source>Window</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>TabManagerWidget</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.ui" line="14"/>
|
||||||
|
<source>Tab Manager</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.ui" line="25"/>
|
||||||
|
<source>Group By:</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.ui" line="48"/>
|
||||||
|
<source>...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="54"/>
|
||||||
|
<source>Window</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="54"/>
|
||||||
|
<source>Domain</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="54"/>
|
||||||
|
<source>Host</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="88"/>
|
||||||
|
<source>Local File System:</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="91"/>
|
||||||
|
<source>QupZilla:</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="94"/>
|
||||||
|
<source> [FTP]</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="388"/>
|
||||||
|
<source>Choose folder for bookmarks:</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="389"/>
|
||||||
|
<source>Bookmark Selected Tabs</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="505"/>
|
||||||
|
<source>Window %1</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="506"/>
|
||||||
|
<source>Double click to switch</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>TabManagerWidgetController</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidgetcontroller.cpp" line="46"/>
|
||||||
|
<location filename="../tabmanagerwidgetcontroller.cpp" line="51"/>
|
||||||
|
<source>Tab Manager</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidgetcontroller.cpp" line="79"/>
|
||||||
|
<source>Show Tab Manager</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
114
src/plugins/TabManager/translations/fa_IR.ts
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.0" language="fa_IR">
|
||||||
|
<context>
|
||||||
|
<name>TabManagerPlugin</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerplugin.cpp" line="106"/>
|
||||||
|
<source>Tab Manager View Type</source>
|
||||||
|
<translation>فرم نمایش مدیر برگه</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerplugin.cpp" line="107"/>
|
||||||
|
<source><p>Please select view type:<br /><b>Note:</b> The "<i>Window</i>" type is recommended for managing lots of windows/tabs</source>
|
||||||
|
<translation><p>لطفا فرم نمایش را انتخاب کنید:<br /><b>توجه:</b> نمایش به فرم «<i>پنجره</i>» برای مدیریت تعداد زیادی پنجره و برگه توصیه میشود</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerplugin.cpp" line="109"/>
|
||||||
|
<location filename="../tabmanagerplugin.cpp" line="112"/>
|
||||||
|
<source>SideBar</source>
|
||||||
|
<translation>نوار کناری</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerplugin.cpp" line="109"/>
|
||||||
|
<source>Window</source>
|
||||||
|
<translation>پنجره</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>TabManagerWidget</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.ui" line="14"/>
|
||||||
|
<source>Tab Manager</source>
|
||||||
|
<translation>مدیر برگه</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.ui" line="25"/>
|
||||||
|
<source>Group By:</source>
|
||||||
|
<translation>گروهبندی با:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.ui" line="48"/>
|
||||||
|
<source>...</source>
|
||||||
|
<translation>...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="54"/>
|
||||||
|
<source>Window</source>
|
||||||
|
<translation>پنجره</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="54"/>
|
||||||
|
<source>Domain</source>
|
||||||
|
<translation>نام دامنه</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="54"/>
|
||||||
|
<source>Host</source>
|
||||||
|
<translation>نام سایت</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="88"/>
|
||||||
|
<source>Local File System:</source>
|
||||||
|
<translation>پروندههای روی سیستم:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="91"/>
|
||||||
|
<source>QupZilla:</source>
|
||||||
|
<translation>کوپزیلا:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="94"/>
|
||||||
|
<source> [FTP]</source>
|
||||||
|
<translation> [FTP]</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="388"/>
|
||||||
|
<source>Choose folder for bookmarks:</source>
|
||||||
|
<translation>انتخاب پوشه برای نشانها:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="389"/>
|
||||||
|
<source>Bookmark Selected Tabs</source>
|
||||||
|
<translation>برگههای انتخابی را نشاندار کن</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="505"/>
|
||||||
|
<source>Window %1</source>
|
||||||
|
<translation>پنجره %1</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidget.cpp" line="506"/>
|
||||||
|
<source>Double click to switch</source>
|
||||||
|
<translation>جفتکلیک برای نمایش پنجره</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>TabManagerWidgetController</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidgetcontroller.cpp" line="46"/>
|
||||||
|
<location filename="../tabmanagerwidgetcontroller.cpp" line="51"/>
|
||||||
|
<source>Tab Manager</source>
|
||||||
|
<translation>مدیر برگه</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../tabmanagerwidgetcontroller.cpp" line="79"/>
|
||||||
|
<source>Show Tab Manager</source>
|
||||||
|
<translation>نمایش مدیر برگه</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Show/Hide Tab Manager</source>
|
||||||
|
<translation type="obsolete">نمایش/مخفیسازی مدیر برگه</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|