1
mirror of https://invent.kde.org/network/falkon.git synced 2024-09-21 09:42:10 +02:00

Import TabManager plugin from qupzilla-plugins

This commit is contained in:
David Rosca 2015-01-21 12:59:20 +01:00
parent 2a9288209c
commit ee80b04a11
27 changed files with 11418 additions and 0 deletions

View 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.

View 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)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 939 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 976 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 784 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View 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

View 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

View 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>

View 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()));
}
}

View 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

View 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>

View 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();
}

View 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

View 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/
-----------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

View 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);

View 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 &registrablePart)
{
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 &registrableName)
{
if (registrableDomain(hostName) != registrableName) {
qWarning() << "TLDExtractor Test Error: hostName:" << hostName
<< "Correct registrableName:" << registrableName
<< "Wrong registrableName:" << registrableDomain(hostName);
return false;
}
return true;
}

View 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 &registrablePart);
QString normalizedHost(const QString &host) const;
bool checkPublicSuffix(const QString &hostName, const QString &registrableName);
QString m_dataFileName;
QStringList m_dataSearchPaths;
QMultiHash<QString, QString> m_tldHash;
};
#endif // TLDEXTRACTOR_H

View 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

View File

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/tldextractor">
<file>data/effective_tld_names.dat</file>
<file>data/test_psl.txt</file>
</qresource>
</RCC>

View 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>&lt;p&gt;Please select view type:&lt;br /&gt;&lt;b&gt;Note:&lt;/b&gt; The &quot;&lt;i&gt;Window&lt;/i&gt;&quot; type is recommended for managing lots of windows/tabs</source>
<translation>&lt;p&gt;Bitte Darstellung wählen:&lt;br /&gt;&lt;b&gt;Hinweis:&lt;/b&gt; Darstellung &quot;&lt;i&gt;Fenster&lt;/i&gt;&quot; 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>

View 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>&lt;p&gt;Please select view type:&lt;br /&gt;&lt;b&gt;Note:&lt;/b&gt; The &quot;&lt;i&gt;Window&lt;/i&gt;&quot; 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>

View 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>&lt;p&gt;Please select view type:&lt;br /&gt;&lt;b&gt;Note:&lt;/b&gt; The &quot;&lt;i&gt;Window&lt;/i&gt;&quot; type is recommended for managing lots of windows/tabs</source>
<translation>&lt;p&gt;لطفا فرم نمایش را انتخاب کنید:&lt;br /&gt;&lt;b&gt;توجه:&lt;/b&gt; نمایش به فرم «&lt;i&gt;پنجره&lt;/i&gt;» برای مدیریت تعداد زیادی پنجره و برگه توصیه میشود</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>